9

In qiskit, I can transpile a given circuit into a some predefined gate set as follows (just an example)

from qiskit import QuantumCircuit
from qiskit.compiler import transpile
from qiskit.circuit.random import random_circuit

basis_gates = ['id', 'u3', 'cx']

qc = random_circuit(3, 1, seed=0) qc_trans = transpile(qc, basis_gates=basis_gates)

I have several related questions.

  1. Where can I find an exhaustive list of operators allowed as basis_gates?
  2. For any operator label from the list of allowed basis gates, how can I find the precise meaning of the corresponding gate, say as a matrix representation?
  3. Most importantly, can I add my own custom gates to use as basis gates? Can I add parametric gates? For examples as far as I can tell qiskit standard tools include Rxz and Ryz gates but no Rxy gate. Can I make one?

Example of a (trivial) transpilation into custom gate set failing

from qiskit import QuantumCircuit
from qiskit.compiler import transpile

qc = QuantumCircuit(2, name='mycx') qc.cx(0, 1) mycx = qc.to_gate() qc = QuantumCircuit(2) qc.cx(0, 1)

transpile(qc, basis_gates=['id','mycx'])

gives me a TranspileError.

Nikita Nemkov
  • 1,605
  • 5
  • 18

2 Answers2

12

The Qiskit standard gate list

You can find the full list of Qiskit standard gates in the module qiskit.circuit.library.standard_gates (documentation).

The matrix representation of a standard gate

For each gate, you can see its matrix representation with the to_matrix method. For example:

from qiskit.circuit.library import standard_gates
standard_gates.HGate().to_matrix()
array([[ 0.70710678+0.j,  0.70710678+0.j],
       [ 0.70710678+0.j, -0.70710678+0.j]])

Or, its latex representation:

from qiskit.visualization import array_to_latex
array_to_latex(standard_gates.HGate().to_matrix())

matrix output in latex

Creating your own custom gate

You can create your own gates from a circuit. For example:

from qiskit import QuantumCircuit

custom_circuit = QuantumCircuit(2, name='bell') custom_circuit.h(0) custom_circuit.cx(0, 1)

custom_gate = custom_circuit.to_gate()

You can create a circuit using that custom gate:

circuit = QuantumCircuit(3)
circuit.h(0)
circuit.append(custom_gate, [0,1])
circuit.cx(1, 2)
circuit.draw()
     ┌───┐┌───────┐     
q_0: ┤ H ├┤0      ├─────
     └───┘│  bell │     
q_1: ─────┤1      ├──■──
          └───────┘┌─┴─┐
q_2: ──────────────┤ X ├
                   └───┘

Telling the transpiler not to decompose your custom gate

Following the previous example, you can transpile that circuit using the custom gate name in the target basis list:

from qiskit.compiler import transpile

basis_gates = ['bell', 'u3', 'cx']

qc_trans = transpile(circuit, basis_gates=basis_gates) qc_trans.draw()

     ┌─────────────┐┌───────┐     
q_0: ┤ U3(π/2,0,π) ├┤0      ├─────
     └─────────────┘│  bell │     
q_1: ───────────────┤1      ├──■──
                    └───────┘┌─┴─┐
q_2: ────────────────────────┤ X ├
                             └───┘

Your custom gate as basis target of a circuit that is not using it

You can basis-target your own custom in some cases. That requires to extend the equivalence library. Following your mycx example:

  1. Create a circuit definition with your custom gate
mycx = QuantumCircuit(2, name='mycx')
mycx.cx(0, 1)

mycx_def = QuantumCircuit(2) mycx_def.append(mycx.to_gate(), [0, 1])

  1. Add an equivalence to the library where a gate (CXGate in this case) is equivalent to that definition.
StandardEquivalenceLibrary.add_equivalence(CXGate(), mycx_def)
  1. Create a circuit that uses the domain gate (CXGate in this case).
from qiskit.compiler import transpile

qc = QuantumCircuit(3) qc.h(0) qc.cx(0, 1) qc.cx(1, 2)

  1. Transpile using the parameter translation_method='translator'. This will tell the transpiler to use the equivalence library for basis translation. In the basis_gate parameter you can refer to your custom gate name (mycx in this case):
result = transpile(qc, basis_gates=['mycx', 'u3'], translation_method='translator')
result.draw()
     ┌─────────────┐┌───────┐         
q_0: ┤ U3(π/2,0,π) ├┤0      ├─────────
     └─────────────┘│  mycx │┌───────┐
q_1: ───────────────┤1      ├┤0      ├
                    └───────┘│  mycx │
q_2: ────────────────────────┤1      ├
                             └───────┘
luciano
  • 5,763
  • 1
  • 12
  • 34
  • Hi, thanks a lot for your answer! I have several follow up questions though.

    (1) If I just have a label from basis_gates list like 'u3' or 'cx', can I do something like Gate.from_label('cx') instead of looking up the documentation to get the gate?

    – Nikita Nemkov Jun 08 '21 at 11:11
  • (2) Your example of transpilation is a bit trivial because you use the custom 'bell' gate both in the original circuit and in the transpiled circuit. In a more practical situation the transpilation seems to fail. Please see updated question. – Nikita Nemkov Jun 08 '21 at 11:19
  • It is not possible to make a from_label. But it looks like a great feature request candidate: https://github.com/Qiskit/qiskit-terra/issues/new?type%3A+feature+request&template=FEATURE_REQUEST.md – luciano Jun 08 '21 at 11:29
  • Your mycx example fails because qc includes cx which is not in the basis. (and you probably forgot to append mycx to qc? – luciano Jun 08 '21 at 11:31
  • Thanks a lot for further elaboration! Still, it seems that we have different goals in mind. Ideally, I would like to transpile an arbitrary circuit into any custom gate set which is universal. Theorems like Solovay-Kitaev's guarantee that this is possible. Perhaps I am interested whether there is some general-purpose algorithm under the hood in qiskit that does that. From your new amendment I gather that I need to set the rules for decomposing into my custom gate set by hands. So I would guess that current qiskit traspiler can only target gates native to IBM machines? – Nikita Nemkov Jun 09 '21 at 08:34
  • Kinda. Qiskit is/pretends-to-be hardware agnostic. Qiskit handle qiskit standard gates in the equivalence library. If your universal basis set is not in the standard gate set, you need to create it with custom gates and add the equivalences to the library. – luciano Jun 09 '21 at 08:53
  • Another possibility is to synthesis to your basis from some low-level representation. If so, you could write transpilation passes that take you that representation and then run that synthesis. For example, we have way to go to clifford+t, if you can convert that to your basis, then it is just that last step missing. – luciano Jun 09 '21 at 12:08
  • For custom single qubit circuit, I am getting error CircuitError: "to_matrix not defined for this <class 'qiskit.circuit.gate.Gate'>" for array_to_latex(custom_circuit.to_gate().to_matrix()). – RSW Aug 13 '22 at 07:46
2

You can use BQSKit to accomplish this very easily. BQSKit is a powerful and portable quantum compiler/transpiler. You will need to calculate the unitary of your gate and you can just plug that into BQSKit.

You can accomplish this with the following:

from bqskit import compile, MachineModel, Circuit
from bqskit.ir.gates import IdentityGate, ConstantUnitaryGate
mycx = ConstantUnitaryGate([...]) # Fill in unitary here
model = MachineModel(gate_set={mycx, IdentityGate(1)})
output_circuit = compile(circuit, model)
edyounis
  • 96
  • 2