Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.

Circuits and Gates

Marc Davis edited this page Apr 10, 2020 · 7 revisions

The classes representing quantum gates are found in circuits.py, and are subclasses of sc.circuits.QuantumStep. You will need to work with QuantumStep objects to crate custom gatesets, and you will get a QuantumStep object as a return value from compilation.

# Here are some examples of what you can do with QuantumStep objects
U3 = sc.QiskitU3QubitStep()
CNOT = sc.CNOTStep()

# get the matrix that a gate represents in a numpy matrix format
U3_unitary = U3.matrix([np.pi/2, np.pi/4, np./pi/6]) # the array of parameters must be provided
CNOT_unitary = CNOT.matrix([]) # cnot takes no parameters so an empty array is provided

# combine multiple gates to form a larger circuit
mycircuit = ProductStep(KroneckerStep(U3,U3), CNOT) # note that mycircuit is itself an instance of QuantumStep

# assemble a circuit to qiskit or qasm code (only if all components of the circuit implement the assemble function)
v = [np.pi/2, np.pi/4, np./pi/6, np.pi/2, np.pi/4, np./pi/6] # an array that contains the parameters for all parameterized gates in the circuit.  Normally you get this array from the compiler.
sc.assembler.assemble(mycircuit, v, sc.assembler.ASSEMBLY_OPENQASM)

# produce a drawing of the circuit (this features is not fully fleshed out, and requires all components of the circuit to implement the _draw-assemble function
mycircuit.draw()

Provided Gates

Single Qubit Gates

  • ZXZXZQubitStep - represents a single qubit gate using the RZ-RX90-RZ-RX90-RZ decomposition
  • XZXZPartialQubitStep - is an incomplete representation of a single qubit gate. The first Z in a ZXZXZ decomposition can commute through the control qubit of a CNOT and combine with the single qubit gate on the other side, so this incomplete single qubit gate can be used instead of a full single qubit gate in certain placements with no lack of generality, yet it has one fewer parameter. Over the course of a long speedup, this leads to a significant speedup.
  • QiskitU3QubitStep - represents a single qubit gate using the same decomposition as IBM's Qiskit U3. It is more efficient to compute than the ZXZXZQubitStep and therefore is generally superior as a fully general single qubit gate.

Two Qubit Gates

  • CNOTStep - represents the CNOT two-qubit gate
  • NonadjacentCNOTStep - represents the CNOT step, but is customizable for nonlinear topologies. It takes three parameters, the number of qubits dits, the index of the control qubit control, and the index of the target qubit target. The gate is the size of dits qubits, regardless of the location of the control and target qubits, and dits must be large enough to hold both the control and target qubits.
  • CNOTRootStep - represents the sqrt(CNOT) gate.
  • CRZStep - represents an incomplete CRZ gate that consists of two sqrt(CNOT) gates and a Z gate. It is made into a complete CRZ gate when the surrounding single-qubit gates from the circuit tree construction are included.

Utility Gates

  • IdentityStep - implements the identity. It takes two parameters: the size of the unitary n, and the number of qu-dits required dits.
  • KroneckerStep - represents the Kronecker product between two or more gates. It is used to combine gates that are run on different qudits during the same time slot.
  • ProductStep - represents the matrix product between two or more gates. It is used to combine gates that are run on the same set of qudits during different time slots.

Qutrit Gates

  • CSUMStep - represents the CSUM two-qutrit gate.
  • CPIStep - represents the CPI two-qutrit gate without phases.
  • CPIPhaseStep - represents the CPI two-qutrit gate with phases. It is the default two-qutrit gate.

Custom Gates

There is an existing gate that can be customized to your needs. However it will not show up when you assembly the circuit to OpenQASM or Qiskit.

  • UStep - represents the gate described by the unitary U passed to __init__, and takes up dits qudits.

You can also write your own QuantumStep subclasses the required functions are:

  • __init__ - you must customize the initializer to set self.num_inputs to the number of parameters for the gate (e.g. 3 for U3 or ZXZXZ, 0 for CNOT), and self.dits to the number of qudits used by the gate (e.g. 1 for U3 or ZXZXZ, 2 for CNOT).
  • matrix(v) - here you generate and return the matrix represented by your gate when passed the parameters provided in the array v.

Assembling with Custom Gates

If you want your code to output your custom gates when assembling, you must implement assemble as well.

  • assemble(v, i) - here you are given v, the list of parameters needed for your gate, and i, the index of the first qubit in the set of qubits that your gate is assigned. You must return an array of the form [gate1, gate2] where gate1 and gate2 are tuples that represent gates that the assembler will be able to interpret. For example, ZXZXZQubitStep returns an array for 5 tuples, one for each of the Z and X gates that it is based on, but QiskitU3QubitStep only returns an array of 1 tuple because the assembler interprets it as a single gate. The tuples take the form ("gate", gatename, parameters, indices), where the word "gate" is included to specify that this tuple represents a well defined gate as opposed to a Kronecker product of gates, gatename is a string that will be used to look up the relevant format to print this gate when assembling, and parameters is a list of the parameters formatted and organized the way they are needed to fill the format specified in the assembler, and indices is a list of the indices of the involved qubits.

Faster Solving with Jacobians

If you would like to take advantage of faster solvers that can take advantage of the Jacobian (marked with Jac in their name), and your custom gate uses one or more parameters, you will need to implement mat_jac as well.

  • mat_jac(v) - here you generate and return a tuple (U, [J1, ... ,Jn]) where U is the same matrix you would return in matrix, and [J1, ... ,Jn] is a list of matrix derivatives, where J1 is the matrix of derivatives with respect to the first parameter in v, and so on for all the parameters in v. If your custom gate is constant (self.num_inputs == 0), then you can take advantage of Jacobian solvers without implementing mat_jac yourself.