This is an quantum automatic test generator, compatible with Qiskit version 1.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.8
Or, installing from Github repository,
git clone https://github.com/NTU-LaDS-II/qATG.git
cd qatg
at 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/qatg
Please 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
qatg
generator will transpile the optimal activation gate (which is a U gate) byqiskit.transpile()
. This should be a list ofqiskit.circuit.library
gates. - 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 theta
At 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
QATGFault
please usesuper(<user-defined-class-name>, self).__init__(gateType, qubits, description = None)
. -
gateType
is the gate type of the fault. Note that sinceqatg
is written based onqiskit
, we currently only support gate types inqiskit.circuit.library
. For example,qiskit.circuit.library.UGate
for U gate, andqiskit.circuit.library.CXGate
for CNOT gate. Please don't useqiskit.circuit.gate.Gate
as your gate type, since every gate is aGate
, thusqatg
simulator will treat every gate in your circuit faulty while doing faulty simulation, which might cause unexpected behavior if yourcreateFaultyGate
doesn't block anything you don't want. -
qubits
is 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, thequbits
list would be[0, 1]
. For single-qubit gates, either[0]
or0
is fine.-
Note that
qatg
requires the length ofqubits
is the same as the qubits that the fault/gate type works on. Sinceqatg
assumes 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()])
-
-
description
is just for user convenience. Thedescription
will 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
self
as 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
self
and afaultfreeGate
, whilefaultfreeGate
will be a gate with typegateType
. - The
faultfreeGate
would be any possiblegateType
since the function is called inqatg
, with afaultfreeGate
thatqatg
generates. - While those gates are qiskit gates, you can use
faultfreeGate.params
to 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
qatg
might generate some parameters such as3*np.pi
while "transpiling". Since3*np.pi
is actuallynp.pi
, user might expectnp.pi * 0.9
instead 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
qatg
only 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.