Skip to main content

Property-based testing framework for qiskit algorithms.

Project description

Qiskit-PTesting

This project aims to implement property-based testing for quantum circuits using the qiskit library for Python 3.6+.

To learn more about qiskit, follow this link.

I also recommend following their tutorials on quantum computing.

To learn more about property-based testing in general, follow these ressources: Intro to property based testing, Wikipedia page.

Installation

  1. Run pip install qiskit-ptesting in a terminal environement

  2. In any python file where you want to define property tests, add:

   from Qiskit_PTesting.Qiskit_PTesting import QiskitPropertyTest, TestProperty, Qarg

That's it, you should be able to define tests in the same file.

Usage

  1. Create a superclass of "QiskitPropertyTest" using any name you want
  2. In that class, define 3 functions (optionally leave them out for default behaviour):
  • property(self)
  • quantumFunction(self, qc)
  • assertions(self)
  1. Inside of the function property(), define a TestProperty object and return it
  2. Inside of the function quantumFunction(), define which steps are needed to be applied to qc (the quantum circuit). All of the generated tests will have those operations applied.
  3. Inside of the function assertion(), define which properties you would like to hold true using the built-in assertions.
  4. Run the test class you created using the "runTests()" method.

How to define a TestProperty

A TestProperty object contains all of the necessary information to generate random tests.

It contains:

  1. p_value: The p_value for all tests (float between 0 and 1)
  2. nbTests: The number of randomly generated tests (int greater than 0)
  3. nbTrials: The number of times each generated test will be run, otherwise called the amount of trials (int greater than 0)
  4. nbMeasurement: The number of times each trial will be measured (int greater than 0)
  5. nbQubits: The amount of required qubits for each test (int greater than 0, or a list of 2 integers, the 2nd one being greater of equal to the first)
  6. nbClassicalBits: The amount of classical bits required for each test(int greater or equal to 0)
  7. qargs: A dictionary of Qarg objects for each qubit that you want to initialise to a specific range/value (can be empty)
  8. backend: A string that expresses which backend should be used for the measurements. It can be any backend from Aer or "ibmq", which automatically uses the API token stored on the computer.

These are the default values:

testProperty = TestProperty(p_value=0.01,
                            nbTests=10,
                            nbTrials=100,
                            nbMeasurements=500,
                            nbQubits=5,
                            nbClassicalBits=0,
                            qargs={},
                            backend="aer_simulator")

NbQubits can be an integer or a list. In that case, the framework will generate each test with a random amount of qubits between the two specified values.

How to initalise a Qarg

A Qarg object holds 4 ints that define 2 ranges. A test property will apply this Qarg to initialise a qubit to a random value between those 2 ranges. Any qubit of a quantum circuit can be initilised using 2 values: a theta and a phi. The first range specifies what values theta can be used to initialise a qubit. The second range specifies what values phi can take.

Here is an example Qarg, that specifies that a qubit needs to be initialised with w theta between 0 and 90 degrees, and a phi of 40 to 60 degrees:

qarg = Qarg(0, 90, 45, 60)

The same Qarg can be specified using radians, if the last paramter, isRad, is set to True:

from math import pi
qarg = Qarg(0, pi/2, pi/4, pi/3, True)

A Qarg can also be specified with a statevector with two complex values in the following way:

from math import sqrt
qarg = Qarg([1/sqrt(2), 1/sqrt(2)], [10, 10], False)

The above code initialises a qubit to the state |+>. The second argument specifies that the theta can be up to 10 degrees higher or lower than the specified statevector, and same for the phi. The False means that degrees are used instead of radians.

Finally, some common states can be directly initialised with just one string:

  • Qarg("0") initialises to state 0
  • Qarg("1") to state 1
  • Qarg("+") to state +
  • Qarg("-") to state -
  • Qarg("Any") to any state on the Bloch sphere

Assertions

6 assertions are up to your disposition:

Single-Qubit assertions

Assert the probability of a qubit to be in state |0>

assertProbability(qu0, expectedProba, qu0_pre=False, basis="z", filter_qc=None)
assertNotProbability(qu0, expectedProba, qu0_pre=False, basis="z", filter_qc=None)

This assertion requires 2 arguments: first, the index of the qubit to be tested, and secondly the expected probability of measuring the qubit in the state |0> along the Z-axis. It can also optionally take in an extra bool argument, that specifies whether the sampling will occur before the quantumFunction is applied. It defaults to False, so the sampling occurs after the function.

Assert that a qubit is in a given state

assertState(qu0, theta, phi, isRadian=False, qu0_pre=False, filter_qc=None)
assertNotState(qu0, theta, phi, isRadian=False, qu0_pre=False, filter_qc=None)

Assert that a qubit has teleported into another

assertTeleported(sent, received, basis="z", filter_qc=None)
assertNotTeleported(sent, received, basis="z", filter_qc=None)

This assertion requires 2 positional arguments: a sent and a received qubit. It evaluates whether quantum teleportation has occured between the qubits.

Multi-Qubit Assertions

Assert the equality or inequality of qubits:

assertEqual(qu0, qu1, qu0_pre=False, qu1_pre=False, basis="z", filter_qc=None)
assertNotEqual(qu0, qu1, qu0_pre=False, qu1_pre=False, basis="z", filter_qc=None)

This assertion requires 2 arguments, which are the indexes of the qubits to be tested, and 2 optional arguments that specify whether the qubits are to be tested before the quantumFunction() is applied. It defaults to False, so if no arguments are specified there, it will compare the qubits after the function is applied. This assertion tests whether the probabilities of measuring two qubits in the states |0> or |1> are the same. The tests are done on the Z-axis.

Assert that two qubits are entangled

assertEntangled(qu0, qu1, basis="z", filter_qc=None)
assertNotEntangled(qu0, qu1, basis="z", filter_qc=None)

This assertion requires 2 positional arguments which are the indexes of qubits. This assertion evaluates whether those two input qubits are entangled.

Assert The Most Common Output(s) Of The Circuit

assertMostCommon(output, filter_qc=None)

This assertion takes in one required positional argument: a string or a list of strings showing the most common states of a circuit

Features

Measure qubit values before the quantum algorithm

By specifying the boolean parameters "qu0_pre" or "qu1_pre" to be True, it is possbile to tell the framework to measure the specified qubit values before or after running the quantum function This is useful in many cases, for example ensuring that a qubit has changed values, or that a qubit is in the same state as another, etc...

An example of using this would be:

class example(QiskitPropertyTest):
   def property(self):
      return TestProperty(nbQubits=2,
                          qargs={0: Qarg("0"),
                                 1: Qarg("+")})

   def quantumFunction(self, qc):
      qc.h(0)

   def assertions(self):
      #compares the qubits after the quantumFunction is run
      self.assertEqual(0, 1)

      #compares both qubits before
      self.assertNotEqual(0, 1, qu0_pre=True, qu1_pre=True)

      #compares qubit 0 after quantumFunction to qubit 1 before
      self.assertEqual(0, 1, qu0_pre=False, qu1_pre=True)

Filter generated tests

It is possible to apply a certain assertion to only the generated tests that have a certain attribute. That attribute can be ANY function that returns a boolean and that takes a QuantumCircuit as input. This feature can give a lot of depth to the defined tests, and enables the users to give very general properties about the input program.

Here is an example of a use case of this feature: enabling properties on tests of any length:

from Qiskit_PTesting.Qiskit_PTesting import QiskitPropertyTest, TestProperty, Qarg

min, max = (2, 10)
class example(QiskitPropertyTest):
   def property(self):
      return TestProperty(nbQubits=[min, max])

   def quantumFunction(self, qc):
      for index in range(len(qc.qubits):
         qc.h(index)

   def assertions(self):
      #specifies that all the qubits should be equal to each other
      for index in range(max):
         self.assertEqual(0, index, filter_qc=lamda qc: len(qc.qubits) > index)

example().run()

Change The Backend Used For The Measurements

Aer Simulators

The default simulator is aer_simulator, but all Aer simulators are available to choose. Simply pass down backend="<any Aer backend>" replacing the string with your chosen backend.

Run Tests On Quantum Computers

In order to run the code on quantum comupters, you have to create an IBMQ account, and specify in the testProperty backend="ibmq"

  1. Create an account with IBMQ and login
  2. Copy the API key found in the settings panel of the "My Account" section of the website
  3. Run the following python code:
from qiskit import IBMQ
IBMQ.save_account("<replace_with_api_token>")

Examples

from Qiskit_PTesting.Qiskit_PTesting import QiskitPropertyTest, TestProperty, Qarg

class example(QiskitPropertyTest):
   def property(self):
      return TestProperty(nbQubits=2,
                          qargs={0: Qarg("0"),
                                 1: Qarg("+")})

   def quantumFunction(self, qc):
      qc.h(0)

   def assertions(self):
      self.assertEqual(0, 1)

example().run()

Inner-workings of the framework

There are 4 main parts in this project:

  • Test case generator
  • Test execution engine
  • Statistical analysis engine
  • Programming interface

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

qiskit-ptesting-1.0.0.tar.gz (21.4 kB view details)

Uploaded Source

File details

Details for the file qiskit-ptesting-1.0.0.tar.gz.

File metadata

  • Download URL: qiskit-ptesting-1.0.0.tar.gz
  • Upload date:
  • Size: 21.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.8.0 pkginfo/1.8.2 readme-renderer/32.0 requests/2.27.1 requests-toolbelt/0.9.1 urllib3/1.26.9 tqdm/4.62.3 importlib-metadata/4.8.1 keyring/23.5.0 rfc3986/2.0.0 colorama/0.4.4 CPython/3.10.4

File hashes

Hashes for qiskit-ptesting-1.0.0.tar.gz
Algorithm Hash digest
SHA256 1e09285f0bdbd22633006ab7998fa57ecf1a905cea5cda986a2b36f2e58a4e7c
MD5 18fc9cfa1fc847b72b760b776af5243d
BLAKE2b-256 7a0efbd5fb5fb14658966986a99563e0c0822f072dcd6c38d3746af03e23a626

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page