Package: qiskit-ptesting
Qiskit-PTestingThis project aims to implement property-based testing for quantum circuits using the qiskit library for Python 3.6+.To learn more about qiskit, follow thislink.I also recommend followingtheir tutorials on quantum computing.To learn more about property-based testing in general, follow these ressources:Intro to property based testing,Wikipedia page.InstallationRunpip install qiskit-ptestingin a terminal environementIn any python file where you want to define property tests, add:fromQiskit_PTesting.Qiskit_PTestingimportQiskitPropertyTest,TestProperty,QargThat's it, you should be able to define tests in the same file.UsageCreate a superclass of "QiskitPropertyTest" using any name you wantIn that class, define 3 functions (optionally leave them out for default behaviour):property(self)quantumFunction(self, qc)assertions(self)Inside of the functionproperty(), define a TestProperty object and return itInside of the functionquantumFunction(), define which steps are needed to be applied to qc (the quantum circuit). All of the generated tests will have those operations applied.Inside of the functionassertion(), define which properties you would like to hold true using the built-in assertions.Run the test class you created using the "runTests()" method.How to define a TestPropertyA TestProperty object contains all of the necessary information to generate random tests.It contains:p_value: The p_value for all tests (float between 0 and 1)nbTests: The number of randomly generated tests (int greater than 0)nbTrials: The number of times each generated test will be run, otherwise called the amount of trials (int greater than 0)nbMeasurement: The number of times each trial will be measured (int greater than 0)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)nbClassicalBits: The amount of classical bits required for each test(int greater or equal to 0)qargs: A dictionary of Qarg objects for each qubit that you want to initialise to a specific range/value (can be empty)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 QargA 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:frommathimportpiqarg=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:frommathimportsqrtqarg=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 0Qarg("1") to state 1Qarg("+") to state +Qarg("-") to state -Qarg("Any") to any state on the Bloch sphereAssertions6 assertions are up to your disposition:Single-Qubit assertionsAssert 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 stateassertState(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 anotherassertTeleported(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 AssertionsAssert 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 entangledassertEntangled(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 CircuitassertMostCommon(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 circuitFeaturesMeasure qubit values before the quantum algorithmBy 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:classexample(QiskitPropertyTest):defproperty(self):returnTestProperty(nbQubits=2,qargs={0:Qarg("0"),1:Qarg("+")})defquantumFunction(self,qc):qc.h(0)defassertions(self):#compares the qubits after the quantumFunction is runself.assertEqual(0,1)#compares both qubits beforeself.assertNotEqual(0,1,qu0_pre=True,qu1_pre=True)#compares qubit 0 after quantumFunction to qubit 1 beforeself.assertEqual(0,1,qu0_pre=False,qu1_pre=True)Filter generated testsIt 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:fromQiskit_PTesting.Qiskit_PTestingimportQiskitPropertyTest,TestProperty,Qargmin,max=(2,10)classexample(QiskitPropertyTest):defproperty(self):returnTestProperty(nbQubits=[min,max])defquantumFunction(self,qc):forindexinrange(len(qc.qubits):qc.h(index)defassertions(self):#specifies that all the qubits should be equal to each otherforindexinrange(max):self.assertEqual(0,index,filter_qc=lamdaqc:len(qc.qubits)>index)example().run()Change The Backend Used For The MeasurementsAer SimulatorsThe default simulator isaer_simulator, but all Aer simulators are available to choose.
Simply pass downbackend="<any Aer backend>"replacing the string with your chosen backend.Run Tests On Quantum ComputersIn order to run the code on quantum comupters, you have to create an IBMQ account, and specify in the testPropertybackend="ibmq"Create an account withIBMQand loginCopy the API key found in the settings panel of the "My Account" section of the websiteRun the following python code:fromqiskitimportIBMQIBMQ.save_account("<replace_with_api_token>")ExamplesfromQiskit_PTesting.Qiskit_PTestingimportQiskitPropertyTest,TestProperty,Qargclassexample(QiskitPropertyTest):defproperty(self):returnTestProperty(nbQubits=2,qargs={0:Qarg("0"),1:Qarg("+")})defquantumFunction(self,qc):qc.h(0)defassertions(self):self.assertEqual(0,1)example().run()Inner-workings of the frameworkThere are 4 main parts in this project:Test case generatorTest execution engineStatistical analysis engineProgramming interface