This is an quantum automatic test generator, compatible with Qiskit version 1.0 to 2.0. The generator generates a configuration for each user-defined fault.
Configurations are generated by test templates, and templates are combined by test elements. One test element consists of one original gate and one activation gate. The use of activation gate is to enlarge the output probability difference of the original gate and the faulty gate.
Since the activation gate is solved assuming as it is an UGate, qATG requires the basis gate set of the quantum circuit should be 'universal'. That is, the quantum circuit can implement any UGate by using gates in the basis gate set.
Note that qATG assumes different faults for different qubits, and one consistent fault for one qubit.
Installing from the pypi server, execute:
pip install qatg==0.8Or, installing from Github repository,
git clone https://github.com/NTU-LaDS-II/qATG.git
cd qatgat the root of cloned directory, execute:
pip install -e .And then pip will handle the rest.
If you only want to install the library in a containerized environment, try run:
docker pull ntuladsii/qatg
docker run -it --rm ntuladsii/qatgPlease refer to the examples in the examples folder.
- Example 1: Faulty UGate w/ self-defined bias fault. The basis gate set is {UGate}.
- Example 2: Faulty RXGate and RZGate w/ self-defined bias fault. The basis gate set is {RXGate, RZGate}.
- Example 3: Faulty CXGate w/ self-defined fault. The basis gate set is {UGate}. Note that currently we only care about those single-qubit gates in the basis gate set.
- Playground: Jupyter-lab playground with the above stuff.
A QATG object is the main generator class. Below are the parameters to initialize the class object.
Necessary Parameters
- circuitSize: the size of the qiskit circuit. Note that since qiskit starts their circuit from #0, if you want to construct a fault with qubits not starting from #0, please choose your circuit size wisely. For example, if you want a single-qubit fault on #3, since qubit #3 should exists the circuit size should at least be 4. This should be a positive integer.
- basisGateSet: the basis gate set of the circuit. Should be "universal", that is, the circuit can implement any effective U gate by using the gates in the basis gate set. The
qatggenerator will transpile the optimal activation gate (which is a U gate) byqiskit.transpile(). This should be a list ofqiskit.circuit.librarygates. - circuitInitializedStates: this is a dictionary that records the initialized of the circuit with different qubit length. The key should be positive integers indicating the number of qubit the state concerns, and the value should be a normalized complex vector with length
2**key, which states the initial state of the circuit for the concerned length of qubits. For example, this can be something like{1: [1, 0], 2: [1, 0, 0, 0]}, where in this case, the circuit is initialized to |0> for single-qubit gates, and |00> for two-qubit gates. Note that the order of qiskit qubits is quite different from some physics textbooks.
Qiskit Circuit Parameters
- quantumRegisterName: the quantum register name of the qiskit circuit. Is the parameter of
qiskit.QuantumRegister(). The default value is 'q'. - classicalRegisterName: the classical register name of the qiskit circuit. Is the parameter of
qiskit.ClassicalRegister(). The default value is 'c'.
Grid Search / Gradient Descent Parameters
- gridSlice: the slices of the grid while doing grid search, searching for parameters for sub-optimal activation gates. Currently the generator searches every U gate parameters in
numpy.linspace(-np.pi, np.pi, num=gridSlice, endpoint=True). This should be a positive integer and the default value is 11. - gradientDescentMaxIteration: the max iteration of gradient descent after the grid search. The generator does a bit gradient descent after the grid search for better fine tuning. This should be a positive integer and the default value is 1000.
- gradientDescentStep: the step of each gradient descent. The gradient descent is performed by
x(t+1) = x(t) + s * gradient(score(x(t))), and the s is the step. The default value is 0.2. - gradientMeasureStep: since we cannot obtain the true gradient of the score function, we measure it by a discrete method
gradient(score(x(t))) = (x(t+e)-x(t))/e. e is the measure step. The default value is 0.0001. - gradientDeltaThreshold: the ending criteria of gradient descent is that the 2-norm of the estimated gradient is less than this threshold. The default value is 1e-8.
Template Size Parameters
- maxTestTemplateSize: the maximum number of test elements in the test template. The default value is 50.
- minRequiredEffectSize: the minimum required effect size for the generator to terminate. For smaller effect size, you might get a short test template, but a large repetition; for larger effect size, you might get a small repetition since the output probability difference is quite large for the faultfree and faulty circuit, but it requires long test template. The default value is 3.
Simulation Parameters
- oneQubitErrorProb: the depolarizing error of single-qubit gates while generating noise model during simulation. The default value is 0.001.
- twoQubitErrorProb: the depolarizing error of two-qubit gates while generating noise model during simulation. The default value is 0.1.
- zeroReadoutErrorProb: the readout error, called
qiskit.providers.aer.noise.errors.ReadoutError([self.zeroReadoutErrorProb, self.oneReadoutErrorProb])while generating noise model during simulation. The default value is [0.985, 0.015]. - oneReadoutErrorProb the readout error, called
qiskit.providers.aer.noise.errors.ReadoutError([self.zeroReadoutErrorProb, self.oneReadoutErrorProb])while generating noise model during simulation. The default value is [0.015, 0.985]. - targetAlpha: target 1-overkill of the test configuration. The default value is 0.99.
- targetBeta: target 1-(test escape) of the test configuration. The default value is 0.999.
- simulationShots: simulation shots while evaluating the faulty/faultfree distribution of the circuit. The default value is 200000.
- testSampleTime: simulated overkill and simulated test escape will be evalutated using testSampleTime times of simulation. The default value is 10000.
Other Parameters
- verbose: whether additional information is printed during test configuration generation. The default value is False.
To properly use the generator, initialized the generator as:
from qatg import QATG
generator = QATG(arguments)
configurationList = generator.createTestConfiguration(FaultList)Thus the generator will return a configuration list based on the fault list user provided. The fault list is a python list of user-defined fault objects. About how to properly define a fault, please refer to section QATGFault. The configurations in the configuration list is described using the class QATGConfiguration.
QATGConfiguration is to describe configurations that the QATG generator produces. generator.createTestConfiguration returns a list of configuration.
For a configuration in the configuration list, print(configuration) will print the detailed information of the configuration including target fault, circuit length, required repetition, total cost, the chi-value boundary, final effect size, simulated overkill and simulated test escape.
Also configuration.circuit will return the qiskit circuit of the configuration. By using configuration.circuit.draw() the program will draw the circuit on the terminal, which is a qiskit function. draw('mpl') will draw the circuit using matplotlib window.
Note that drawing circuit by draw('mpl') requires module pylatexenc and ipympl on jupyter-lab, and by installing qatg by pip will install that for you.
QATGFault is the most important part of QATG since it describes the fault in a general way. A valid fault defined by users should inherit the class QATGFault. Below is an example of faulty U Gate, using bias fault as its faulty behavior.
from qatg import QATGFault
class myUFault(QATGFault):
def __init__(self, params):
super(myUFault, self).__init__(qGate.UGate, 0, f"gateType: U, qubits: 0, params: {params}")
self.params = params
def createOriginalGate(self):
return qGate.UGate(*self.params)
def createFaultyGate(self, faultfreeGate):
return qGate.UGate(faultfreeGate.params[0] - 0.1*np.pi, faultfreeGate.params[1], faultfreeGate.params[2]) # bias fault on thetaAt least three functions are required in the user-defined fault class.
-
__init__(self, *args)-
This is the initializer of the class. By initializing the parent class
QATGFaultplease usesuper(<user-defined-class-name>, self).__init__(gateType, qubits, description = None). -
gateTypeis the gate type of the fault. Note that sinceqatgis written based onqiskit, we currently only support gate types inqiskit.circuit.library. For example,qiskit.circuit.library.UGatefor U gate, andqiskit.circuit.library.CXGatefor CNOT gate. Please don't useqiskit.circuit.gate.Gateas your gate type, since every gate is aGate, thusqatgsimulator will treat every gate in your circuit faulty while doing faulty simulation, which might cause unexpected behavior if yourcreateFaultyGatedoesn't block anything you don't want. -
qubitsis the qubit list of the fault. For example, for a faulty CNOT gate, if the CNOT the user is discussing has its control bit on #0, and its target bit on #1, thequbitslist would be[0, 1]. For single-qubit gates, either[0]or0is fine.-
Note that
qatgrequires the length ofqubitsis the same as the qubits that the fault/gate type works on. Sinceqatgassumes different fault on different qubits, PLEASE declare different class objects for different qubits, despite the fact that they might have the same faulty behavior. -
For example, if I want to test the U Gate fault on qubit #0 and #1, and they have the same faulty behavior. This is fine:
# fine class myUFault0(QATGFault): def __init__(self): super(myUFault0, self).__init__(qGate.UGate, 0) class myUFault1(QATGFault): def __init__(self): super(myUFault1, self).__init__(qGate.UGate, 1) configurationList = generator.createTestConfiguration([myUFault0(), myUFault1()])
This is also fine:
# fine class myUFault(QATGFault): def __init__(self, qubit): super(myUFault, self).__init__(qGate.UGate, qubit) configurationList = generator.createTestConfiguration([myUFault(0), myUFault(1)])
But this is NOT WORKING:
# NOT WORKING class myUFault(QATGFault): def __init__(self): super(myUFault, self).__init__(qGate.UGate, [0, 1]) configurationList = generator.createTestConfiguration([myUFault()])
-
-
descriptionis just for user convenience. Thedescriptionwill be printed while printing the configuration of the fault. It is optional and doesn't affect any computation inqatg. The default value isNone.
-
-
createOriginalGate(self)- User-defined fault class should inherit this function, with only
selfas parameter. Since there is a original gate in every test element of the configuration generated byqatg, this function requires user to return a faultfree gate with typegateType.
- User-defined fault class should inherit this function, with only
-
createFaultyGate(self, faultfreeGate)- User-defined fault class should inherit this function, as it defines the faulty behavior of the fault class. The parameter of this function would be
selfand afaultfreeGate, whilefaultfreeGatewill be a gate with typegateType. - The
faultfreeGatewould be any possiblegateTypesince the function is called inqatg, with afaultfreeGatethatqatggenerates. - While those gates are qiskit gates, you can use
faultfreeGate.paramsto obtain the parameters in the gate (see example #1, #2), or obtain the matrix byfaultfreeGate.to_matrix(), change the matrix, and return a decent gate by usingqiskit.extensions.UnitaryGate(matrix)(see example #3).
- User-defined fault class should inherit this function, as it defines the faulty behavior of the fault class. The parameter of this function would be
- Ratio faults might be troublesome since
qatgmight generate some parameters such as3*np.piwhile "transpiling". Since3*np.piis actuallynp.pi, user might expectnp.pi * 0.9instead of3*np.pi * 0.9. This is treated as a bug we currently are trying to fix. - Numba accelerates is something we are working on.
- Currently
qatgonly supports single-qubit gates and CNOT gate. Adding more supports is something we are working on.
Please contact NTU-LaDS-II ([email protected]) if you have any suggest or problems.