Skip to content

Commit

Permalink
Merge pull request #96 from BoxiLi/circuit_refactor
Browse files Browse the repository at this point in the history
The first step of refactoring the gate representation. It defines a gate class for each gate function as a subclass of `Gate`, this simplifies several things:
- Using the gate class will allow one to identify the gate type through `isinstance` and `issubclass`, instead of by the name attribute.
- Get rid of several look-up tables in several different places.
- Allow a more natural way of defining custom gates. One only needs to define one's own gate class with a `get_compact_operator` method.
  • Loading branch information
BoxiLi committed May 25, 2022
2 parents ed1ddad + eb89dc1 commit 546a212
Show file tree
Hide file tree
Showing 12 changed files with 1,372 additions and 609 deletions.
2 changes: 2 additions & 0 deletions .codeclimate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ plugins:
checks:
E203:
enabled: false
E501: # Disable "Line too long", covered in Black
enabled: false
duplication:
enabled: true
config:
Expand Down
34 changes: 34 additions & 0 deletions doc/source/apidoc/qutip_qip.operations.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,40 @@ qutip\_qip.operations
.. autosummary::

Gate
GATE_CLASS_MAP
X
Y
Z
RX
RY
RZ
H
SNOT
SQRTNOT
S
T
QASMU
SWAP
ISWAP
CNOT
SQRTSWAP
SQRTISWAP
BERKELEY
SWAPALPHA
MS
TOFFOLI
FREDKIN
CNOT
CSIGN
CRX
CRY
CRZ
CY
CX
CZ
CS
CT
CPHASE

.. rubric:: Functions

Expand Down
8 changes: 4 additions & 4 deletions doc/source/qip-basics.rst
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,11 @@ A circuit with the various gates and registers available is demonstrated below:
.. testoutput::
:options: +NORMALIZE_WHITESPACE

[Gate(SWAP, targets=[0, 1], controls=None, classical controls=None, control_value=None),
[Gate(SWAP, targets=[0, 1], controls=None, classical controls=None, control_value=None, classical_control_value=None),
Measurement(M0, target=[1], classical_store=0),
Gate(CNOT, targets=[1], controls=[0], classical controls=None, control_value=None),
Gate(X, targets=[0], controls=None, classical controls=[0], control_value=None),
Gate(SWAP, targets=[0, 1], controls=None, classical controls=None, control_value=None)]
Gate(CNOT, targets=[1], controls=[0], classical controls=None, control_value=1, classical_control_value=None),
Gate(X, targets=[0], controls=None, classical controls=[0], control_value=None, classical_control_value=None),
Gate(SWAP, targets=[0, 1], controls=None, classical controls=None, control_value=None, classical_control_value=None)]

Unitaries
=========
Expand Down
6 changes: 3 additions & 3 deletions doc/source/qip-simulator.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ examples of circuit evolution. We take a circuit from
.. testcode::

from qutip_qip.circuit import QubitCircuit
from qutip_qip.operations import (Gate, controlled_gate,
hadamard_transform)
from qutip_qip.operations import (
Gate, controlled_gate, hadamard_transform)
def controlled_hadamard():
# Controlled Hadamard
return controlled_gate(
hadamard_transform(1), 2, control=0, target=1, control_value=1)
hadamard_transform(1), controls=0, targets=1, control_value=1)
qc = QubitCircuit(N=3, num_cbits=3)
qc.user_gates = {"cH": controlled_hadamard}
qc.add_gate("QASMU", targets=[0], arg_value=[1.91063, 0, 0])
Expand Down
102 changes: 29 additions & 73 deletions src/qutip_qip/circuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,39 +15,10 @@
from . import circuit_latex as _latex
from .operations import (
Gate,
rx,
ry,
rz,
sqrtnot,
snot,
phasegate,
x_gate,
y_gate,
z_gate,
cy_gate,
cz_gate,
s_gate,
t_gate,
cs_gate,
qasmu_gate,
ct_gate,
cphase,
cnot,
csign,
berkeley,
swapalpha,
swap,
iswap,
sqrtswap,
sqrtiswap,
fredkin,
toffoli,
controlled_gate,
globalphase,
expand_operator,
gate_sequence_product,
GATE_CLASS_MAP,
)
from .operations.gates import _gate_label, _single_qubit_gates
from qutip import basis, ket2dm, qeye
from qutip import Qobj
from qutip.measurement import measurement_statistics
Expand Down Expand Up @@ -325,6 +296,7 @@ def add_gate(
index=None,
classical_controls=None,
control_value=None,
classical_control_value=None,
):
"""
Adds a gate with specified parameters to the circuit.
Expand Down Expand Up @@ -354,44 +326,30 @@ def add_gate(
2 ** len(classical_controls) - 1
(i.e. all classical controls are 1).
"""
if isinstance(gate, Gate):
name = gate.name
targets = gate.targets
controls = gate.controls
arg_value = gate.arg_value
arg_label = gate.arg_label
classical_controls = gate.classical_controls
control_value = gate.control_value

else:
name = gate

if index is None:
gate = Gate(
name,
if not isinstance(gate, Gate):
if gate in GATE_CLASS_MAP:
gate_class = GATE_CLASS_MAP[gate]
else:
gate_class = Gate
gate = gate_class(
name=gate,
targets=targets,
controls=controls,
arg_value=arg_value,
arg_label=arg_label,
classical_controls=classical_controls,
control_value=control_value,
classical_control_value=classical_control_value,
)

if index is None:
self.gates.append(gate)

else:
# NOTE: Every insertion shifts the indices in the original list of
# gates by an additional position to the right.
shifted_inds = np.sort(index) + np.arange(len(index))
for position in shifted_inds:
gate = Gate(
name,
targets=targets,
controls=controls,
arg_value=arg_value,
arg_label=arg_label,
classical_controls=classical_controls,
control_value=control_value,
)
self.gates.insert(position, gate)

def add_gates(self, gates):
Expand All @@ -417,6 +375,7 @@ def add_1q_gate(
arg_label=None,
classical_controls=None,
control_value=None,
classical_control_value=None,
):
"""
Adds a single qubit gate with specified parameters on a variable
Expand All @@ -438,34 +397,31 @@ def add_1q_gate(
arg_label : string
Label for gate representation.
"""
if name not in _single_qubit_gates:
raise ValueError("%s is not a single qubit gate" % name)

if qubits is not None:
for _, i in enumerate(qubits):
gate = Gate(
name,
gate = GATE_CLASS_MAP[name](
targets=qubits[i],
controls=None,
arg_value=arg_value,
arg_label=arg_label,
classical_controls=classical_controls,
control_value=control_value,
classical_control_value=classical_control_value,
)
self.gates.append(gate)

else:
if end is None:
end = self.N - 1
for i in range(start, end + 1):
gate = Gate(
name,
gate = GATE_CLASS_MAP[name](
targets=i,
controls=None,
arg_value=arg_value,
arg_label=arg_label,
classical_controls=classical_controls,
control_value=control_value,
classical_control_value=classical_control_value,
)
self.gates.append(gate)

Expand Down Expand Up @@ -1885,13 +1841,12 @@ def latex_code(self):
r" \multigate{%d}{%s} "
% (
len(gate.targets) - 1,
_gate_label(gate.name, gate.arg_label),
_gate_label(gate),
)
)
else:
col.append(
r" \ghost{%s} "
% (_gate_label(gate.name, gate.arg_label))
r" \ghost{%s} " % (_gate_label(gate))
)

elif gate.name == "CNOT":
Expand All @@ -1907,10 +1862,7 @@ def latex_code(self):
elif gate.name == "TOFFOLI":
col.append(r" \targ ")
else:
col.append(
r" \gate{%s} "
% _gate_label(gate.name, gate.arg_label)
)
col.append(r" \gate{%s} " % _gate_label(gate))

elif gate.controls and n in gate.controls:
control_tag = (-1 if self.reverse_states else 1) * (
Expand All @@ -1934,14 +1886,11 @@ def latex_code(self):
r" \multigate{%d}{%s} "
% (
self.N - 1,
_gate_label(gate.name, gate.arg_label),
_gate_label(gate),
)
)
else:
col.append(
r" \ghost{%s} "
% (_gate_label(gate.name, gate.arg_label))
)
col.append(r" \ghost{%s} " % (_gate_label(gate)))
else:
col.append(r" \qw ")

Expand Down Expand Up @@ -2093,6 +2042,13 @@ def _to_qasm(self, qasm_out):
"""


def _gate_label(gate):
gate_label = gate.latex_str
if gate.arg_label is not None:
return r"%s(%s)" % (gate_label, arg_label)
return r"%s" % gate_label


class CircuitResult:
"""
Result of a quantum circuit simulation.
Expand Down
1 change: 1 addition & 0 deletions src/qutip_qip/operations/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
Operations on quantum circuits.
"""
from .gates import *
from .gateclass import *

0 comments on commit 546a212

Please sign in to comment.