Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add global_phase to QuantumCircuit class #4565

Merged
merged 47 commits into from
Jul 25, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
7a50b4c
minor commit
ewinston Jun 4, 2020
bc056a3
rz agrees with u1
ewinston Jun 11, 2020
174a9f7
synthesis tests pass
ewinston Jun 16, 2020
0a72b1c
passing tests except open controlled random unitary
ewinston Jun 18, 2020
9a9a474
resolve most errors in test_unroller
ewinston Jun 18, 2020
7d3cc36
passing tests
ewinston Jun 24, 2020
aa63b20
move global phase change to circuit_to_gate
ewinston Jun 24, 2020
92d9314
add missing to_matrix to [most] gates
ewinston Jun 24, 2020
8dcc2dc
Merge branch 'master' into qc_phase_def
ewinston Jun 24, 2020
e89f14c
revert base gate of c3x
ewinston Jun 24, 2020
82e5e98
definition changed to circuit
ewinston Jun 26, 2020
4a636fa
resolve c_if register conversion
ewinston Jun 30, 2020
202d433
revert removal of _define in standard gates
ewinston Jul 1, 2020
87db632
linting
ewinston Jul 1, 2020
24d31b3
Merge branch 'master' into def_to_circuit
ewinston Jul 1, 2020
c3b3776
resolve qreg bug in transpiler
ewinston Jul 3, 2020
2320aa5
simplify unroller passes a bit
ewinston Jul 3, 2020
3be2fdd
linting
ewinston Jul 3, 2020
56a21a5
resolve doc forward ref
ewinston Jul 3, 2020
c977819
linting
ewinston Jul 3, 2020
269f307
Merge branch 'def_to_circuit' into qc_phase_def2
ewinston Jul 3, 2020
905fb43
Merge branch 'master' into qc_phase_def2
ewinston Jul 5, 2020
3847b34
Merge branch 'qc_phase_def2' into qc_phase_def
ewinston Jul 5, 2020
9448094
update u3 cx equivalence_library
ewinston Jul 5, 2020
8e1fe8c
put phase back into gate class definitions
ewinston Jul 7, 2020
0068dea
added checks for circuit inverse, compose, and state.evolve.
ewinston Jul 10, 2020
456f69e
passing tests
ewinston Jul 10, 2020
ec29cd5
Merge branch 'master' into qc_phase
ewinston Jul 10, 2020
5a6b37e
linting
ewinston Jul 10, 2020
171811f
revert phase accurate one_qubit_decompose for future pr.
ewinston Jul 10, 2020
f9ec746
linting
ewinston Jul 10, 2020
b4b0229
Update test/python/circuit/test_gate_definitions.py
ewinston Jul 10, 2020
3b29519
add release notes
ewinston Jul 10, 2020
e7deb96
revert equiv test
ewinston Jul 10, 2020
f424ea8
update release notes
ewinston Jul 10, 2020
1c7d2ca
Update qiskit/circuit/quantumcircuit.py
ewinston Jul 14, 2020
73509d4
don't unroll global phase to single qubit gates
ewinston Jul 21, 2020
5a62063
typeo
ewinston Jul 21, 2020
9c3a613
linting
ewinston Jul 22, 2020
ae5b636
Merge branch 'master' into qc_phase
ewinston Jul 22, 2020
3e5532b
linting
ewinston Jul 22, 2020
b1b746d
update cx_global_phase test
ewinston Jul 22, 2020
d2921eb
Merge branch 'master' into qc_phase
ewinston Jul 23, 2020
96b2240
minor linting
ewinston Jul 23, 2020
5ca9f3b
Update releasenotes/notes/add_quantumcircuit_phase-5006d1e930348d2e.yaml
ajavadia Jul 23, 2020
79779b7
Update releasenotes/notes/add_quantumcircuit_phase-5006d1e930348d2e.yaml
ajavadia Jul 23, 2020
999728a
Merge branch 'master' into qc_phase
mergify[bot] Jul 25, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 61 additions & 43 deletions qiskit/circuit/add_control.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,57 +99,75 @@ def control(operation: Union[Gate, ControlledGate],
from math import pi
# pylint: disable=cyclic-import
import qiskit.circuit.controlledgate as controlledgate
# pylint: disable=unused-import
import qiskit.circuit.library.standard_gates.multi_control_rotation_gates

q_control = QuantumRegister(num_ctrl_qubits, name='control')
q_target = QuantumRegister(operation.num_qubits, name='target')
q_ancillae = None # TODO: add
qc = QuantumCircuit(q_control, q_target)

controlled_circ = QuantumCircuit(q_control, q_target,
name='c_{}'.format(operation.name))
global_phase = 0
if operation.name == 'x' or (
isinstance(operation, controlledgate.ControlledGate) and
operation.base_gate.name == 'x'):
qc.mct(q_control[:] + q_target[:-1], q_target[-1], q_ancillae)
elif operation.name == 'rx':
qc.mcrx(operation.definition.data[0][0].params[0], q_control, q_target[0],
use_basis_gates=True)
elif operation.name == 'ry':
qc.mcry(operation.definition.data[0][0].params[0], q_control, q_target[0],
q_ancillae, mode='noancilla', use_basis_gates=True)
elif operation.name == 'rz':
qc.mcrz(operation.definition.data[0][0].params[0], q_control, q_target[0],
use_basis_gates=True)
controlled_circ.mct(q_control[:] + q_target[:-1], q_target[-1], q_ancillae)
if operation.definition is not None and operation.definition.global_phase:
global_phase += operation.definition.global_phase
else:
bgate = _unroll_gate(operation, ['u1', 'u3', 'cx'])
# now we have a bunch of single qubit rotation gates and cx
for rule in bgate.definition.data:
if rule[0].name == 'u3':
theta, phi, lamb = rule[0].params
basis = ['u1', 'u3', 'x', 'rx', 'ry', 'rz', 'cx']
unrolled_gate = _unroll_gate(operation, basis_gates=basis)
for gate, qreg, _ in unrolled_gate.definition.data:
if gate.name == 'x':
controlled_circ.mct(q_control, q_target[qreg[0].index],
q_ancillae)
elif gate.name == 'rx':
controlled_circ.mcrx(gate.definition.data[0][0].params[0],
q_control, q_target[qreg[0].index],
use_basis_gates=True)
elif gate.name == 'ry':
controlled_circ.mcry(gate.definition.data[0][0].params[0],
q_control, q_target[qreg[0].index],
q_ancillae, mode='noancilla',
use_basis_gates=True)
elif gate.name == 'rz':
controlled_circ.mcrz(gate.definition.data[0][0].params[0],
q_control, q_target[qreg[0].index],
use_basis_gates=True)
elif gate.name == 'u1':
controlled_circ.mcu1(gate.params[0], q_control, q_target[qreg[0].index])
elif gate.name == 'cx':
controlled_circ.mct(q_control[:] + [q_target[qreg[0].index]],
q_target[qreg[1].index],
q_ancillae)
elif gate.name == 'u3':
theta, phi, lamb = gate.params
if phi == -pi / 2 and lamb == pi / 2:
qc.mcrx(theta, q_control, q_target[rule[1][0].index],
use_basis_gates=True)
controlled_circ.mcrx(theta, q_control, q_target[qreg[0].index],
use_basis_gates=True)
elif phi == 0 and lamb == 0:
qc.mcry(theta, q_control, q_target[rule[1][0].index],
q_ancillae, use_basis_gates=True)
controlled_circ.mcry(theta, q_control, q_target[qreg[0].index],
q_ancillae, use_basis_gates=True)
elif theta == 0 and phi == 0:
qc.mcrz(lamb, q_control, q_target[rule[1][0].index],
use_basis_gates=True)
controlled_circ.mcrz(lamb, q_control, q_target[qreg[0].index],
use_basis_gates=True)
else:
qc.mcrz(lamb, q_control, q_target[rule[1][0].index],
use_basis_gates=True)
qc.mcry(theta, q_control, q_target[rule[1][0].index],
q_ancillae, use_basis_gates=True)
qc.mcrz(phi, q_control, q_target[rule[1][0].index],
use_basis_gates=True)
elif rule[0].name == 'u1':
qc.mcu1(rule[0].params[0], q_control, q_target[rule[1][0].index])
elif rule[0].name == 'cx':
qc.mct(q_control[:] + [q_target[rule[1][0].index]], q_target[rule[1][1].index],
q_ancillae)
controlled_circ.mcrz(lamb, q_control, q_target[qreg[0].index],
use_basis_gates=True)
controlled_circ.mcry(theta, q_control, q_target[qreg[0].index],
q_ancillae, use_basis_gates=True)
controlled_circ.mcrz(phi, q_control, q_target[qreg[0].index],
use_basis_gates=True)
else:
raise CircuitError('gate contains non-controllable instructions')

raise CircuitError('gate contains non-controllable instructions: {}'.format(
gate.name))
if gate.definition is not None and gate.definition.global_phase:
global_phase += gate.definition.global_phase
# apply controlled global phase
if ((operation.definition is not None and operation.definition.global_phase) or global_phase):
if len(q_control) < 2:
controlled_circ.u1(operation.definition.global_phase + global_phase, q_control)
else:
controlled_circ.mcu1(operation.definition.global_phase + global_phase,
q_control[:-1], q_control[-1])
if isinstance(operation, controlledgate.ControlledGate):
new_num_ctrl_qubits = num_ctrl_qubits + operation.num_ctrl_qubits
new_ctrl_state = operation.ctrl_state << num_ctrl_qubits | ctrl_state
Expand All @@ -170,11 +188,11 @@ def control(operation: Union[Gate, ControlledGate],
ctrl_substr = ('{0}' * new_num_ctrl_qubits).format('c')
new_name = '{0}{1}'.format(ctrl_substr, base_name)
cgate = controlledgate.ControlledGate(new_name,
qc.num_qubits,
controlled_circ.num_qubits,
operation.params,
label=label,
num_ctrl_qubits=new_num_ctrl_qubits,
definition=qc,
definition=controlled_circ,
ctrl_state=new_ctrl_state)
cgate.base_gate = base_gate
return cgate
Expand All @@ -196,7 +214,7 @@ def _gate_to_circuit(operation):

def _gate_to_dag(operation):
from qiskit.converters.circuit_to_dag import circuit_to_dag
if hasattr(operation, 'definition') and operation.definition:
if hasattr(operation, 'definition') and operation.definition is not None:
return circuit_to_dag(operation.definition)
else:
qr = QuantumRegister(operation.num_qubits)
Expand All @@ -210,5 +228,5 @@ def _unroll_gate(operation, basis_gates):
from qiskit.transpiler.passes import Unroller
unroller = Unroller(basis_gates)
dag = _gate_to_dag(operation)
qc = dag_to_circuit(unroller.run(dag))
return qc.to_gate()
opqc = dag_to_circuit(unroller.run(dag))
return opqc.to_gate()
3 changes: 2 additions & 1 deletion qiskit/circuit/controlledgate.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,4 +207,5 @@ def __eq__(self, other) -> bool:

def inverse(self) -> 'ControlledGate':
"""Invert this gate by calling inverse on the base gate."""
return self.base_gate.inverse().control(self.num_ctrl_qubits)
return self.base_gate.inverse().control(self.num_ctrl_qubits,
ctrl_state=self.ctrl_state)
1 change: 0 additions & 1 deletion qiskit/circuit/equivalence.py
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,6 @@ def _raise_if_shape_mismatch(gate, circuit):

def _rebind_equiv(equiv, query_params):
equiv_params, equiv_circuit = equiv

param_map = dict(zip(equiv_params, query_params))
equiv = equiv_circuit.assign_parameters(param_map, inplace=False)

Expand Down
29 changes: 23 additions & 6 deletions qiskit/circuit/library/standard_gates/equivalence_library.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
RZGate,
CRZGate,
RZZGate,
RZXGate,
SGate,
SdgGate,
SwapGate,
Expand Down Expand Up @@ -158,14 +159,30 @@
(HGate(), [q[0]], []),
(HGate(), [q[1]], []),
(CXGate(), [q[0], q[1]], []),
(U1Gate(theta), [q[1]], []),
(RZGate(theta), [q[1]], []),
(CXGate(), [q[0], q[1]], []),
(HGate(), [q[1]], []),
(HGate(), [q[0]], []),
]:
def_rxx.append(inst, qargs, cargs)
_sel.add_equivalence(RXXGate(theta), def_rxx)

# RZXGate

q = QuantumRegister(2, 'q')
theta = Parameter('theta')
def_rzx = QuantumCircuit(q)
for inst, qargs, cargs in [
(HGate(), [q[1]], []),
(CXGate(), [q[0], q[1]], []),
(RZGate(theta), [q[1]], []),
(CXGate(), [q[0], q[1]], []),
(HGate(), [q[1]], []),
]:
def_rzx.append(inst, qargs, cargs)
_sel.add_equivalence(RZXGate(theta), def_rzx)


# RYGate

q = QuantumRegister(1, 'q')
Expand Down Expand Up @@ -209,7 +226,7 @@

q = QuantumRegister(1, 'q')
theta = Parameter('theta')
def_rz = QuantumCircuit(q)
def_rz = QuantumCircuit(q, global_phase=-theta / 2)
def_rz.append(U1Gate(theta), [q[0]], [])
_sel.add_equivalence(RZGate(theta), def_rz)

Expand Down Expand Up @@ -242,7 +259,7 @@
def_rzz = QuantumCircuit(q)
for inst, qargs, cargs in [
(CXGate(), [q[0], q[1]], []),
(U1Gate(theta), [q[1]], []),
(RZGate(theta), [q[1]], []),
(CXGate(), [q[0], q[1]], [])
]:
def_rzz.append(inst, qargs, cargs)
Expand Down Expand Up @@ -338,7 +355,7 @@
def_tdg.append(U1Gate(-pi / 4), [q[0]], [])
_sel.add_equivalence(TdgGate(), def_tdg)

# U1Gate
# U2Gate

q = QuantumRegister(1, 'q')
phi = Parameter('phi')
Expand Down Expand Up @@ -376,7 +393,7 @@
theta = Parameter('theta')
phi = Parameter('phi')
lam = Parameter('lam')
u3_qasm_def = QuantumCircuit(q)
u3_qasm_def = QuantumCircuit(q, global_phase=(lam + phi) / 2)
u3_qasm_def.rz(lam, 0)
u3_qasm_def.rx(pi/2, 0)
u3_qasm_def.rz(theta+pi, 0)
Expand Down Expand Up @@ -427,7 +444,7 @@
_sel.add_equivalence(CXGate(), cx_to_cz)

q = QuantumRegister(2, 'q')
cx_to_iswap = QuantumCircuit(q)
cx_to_iswap = QuantumCircuit(q, global_phase=3*pi/4)
for inst, qargs, cargs in [
(HGate(), [q[0]], []),
(XGate(), [q[1]], []),
Expand Down
3 changes: 2 additions & 1 deletion qiskit/circuit/library/standard_gates/ms.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,12 @@ def _define(self):
# pylint: disable=cyclic-import
from qiskit.circuit.quantumcircuit import QuantumCircuit
from .rxx import RXXGate
theta = self.params[0]
q = QuantumRegister(self.num_qubits, 'q')
qc = QuantumCircuit(q, name=self.name)
rules = []
for i in range(self.num_qubits):
for j in range(i + 1, self.num_qubits):
rules += [(RXXGate(self.params[0]), [q[i], q[j]], [])]
rules += [(RXXGate(theta), [q[i], q[j]], [])]
qc._data = rules
self.definition = qc
29 changes: 17 additions & 12 deletions qiskit/circuit/library/standard_gates/rx.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,18 +200,23 @@ def inverse(self):
"""Return inverse RX gate (i.e. with the negative rotation angle)."""
return CRXGate(-self.params[0])

# TODO: this is the correct definition but has a global phase with respect
# to the decomposition above. Restore after allowing phase on circuits.
# def to_matrix(self):
# """Return a numpy.array for the CRX gate."""
# half_theta = self.params[0] / 2
# cos = numpy.cos(half_theta)
# isin = 1j * numpy.sin(half_theta)
# return numpy.array([[1, 0, 0, 0],
# [0, cos, 0, -isin],
# [0, 0, 1, 0],
# [0, -isin, 0, cos]],
# dtype=complex)
def to_matrix(self):
"""Return a numpy.array for the CRX gate."""
half_theta = self.params[0] / 2
cos = numpy.cos(half_theta)
isin = 1j * numpy.sin(half_theta)
if self.ctrl_state:
return numpy.array([[1, 0, 0, 0],
[0, cos, 0, -isin],
[0, 0, 1, 0],
[0, -isin, 0, cos]],
dtype=complex)
else:
return numpy.array([[cos, 0, -isin, 0],
[0, 1, 0, 0],
[-isin, 0, cos, 0],
[0, 0, 0, 1]],
dtype=complex)


class CrxGate(CRXGate, metaclass=CRXMeta):
Expand Down
28 changes: 14 additions & 14 deletions qiskit/circuit/library/standard_gates/rxx.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,16 +77,16 @@ def _define(self):
# pylint: disable=cyclic-import
from qiskit.circuit.quantumcircuit import QuantumCircuit
from .x import CXGate
from .u1 import U1Gate
from .h import HGate
from .rz import RZGate
theta = self.params[0]
q = QuantumRegister(2, 'q')
qc = QuantumCircuit(q, name=self.name)
theta = self.params[0]
rules = [
(HGate(), [q[0]], []),
(HGate(), [q[1]], []),
(CXGate(), [q[0], q[1]], []),
(U1Gate(theta), [q[1]], []),
(RZGate(theta), [q[1]], []),
(CXGate(), [q[0], q[1]], []),
(HGate(), [q[1]], []),
(HGate(), [q[0]], []),
Expand All @@ -98,14 +98,14 @@ def inverse(self):
"""Return inverse RXX gate (i.e. with the negative rotation angle)."""
return RXXGate(-self.params[0])

# NOTE: we should use the following as the canonical matrix
# definition but we don't include it yet since it differs from
# the circuit decomposition matrix by a global phase
# def to_matrix(self):
# """Return a Numpy.array for the RXX gate."""
# theta = float(self.params[0])
# return np.array([
# [np.cos(theta / 2), 0, 0, -1j * np.sin(theta / 2)],
# [0, np.cos(theta / 2), -1j * np.sin(theta / 2), 0],
# [0, -1j * np.sin(theta / 2), np.cos(theta / 2), 0],
# [-1j * np.sin(theta / 2), 0, 0, np.cos(theta / 2)]], dtype=complex)
def to_matrix(self):
"""Return a Numpy.array for the RXX gate."""
import numpy
theta2 = float(self.params[0]) / 2
cos = numpy.cos(theta2)
isin = 1j * numpy.sin(theta2)
return numpy.array([
[cos, 0, 0, -isin],
[0, cos, -isin, 0],
[0, -isin, cos, 0],
[-isin, 0, 0, cos]], dtype=complex)
29 changes: 17 additions & 12 deletions qiskit/circuit/library/standard_gates/ry.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,18 +195,23 @@ def inverse(self):
"""Return inverse RY gate (i.e. with the negative rotation angle)."""
return CRYGate(-self.params[0])

# TODO: this is the correct definition but has a global phase with respect
# to the decomposition above. Restore after allowing phase on circuits.
# def to_matrix(self):
# """Return a numpy.array for the CRY gate."""
# half_theta = self.params[0] / 2
# cos = numpy.cos(half_theta)
# sin = numpy.sin(half_theta)
# return numpy.array([[1, 0, 0, 0],
# [0, cos, 0, -sin],
# [0, 0, 1, 0],
# [0, sin, 0, cos]],
# dtype=complex)
def to_matrix(self):
"""Return a numpy.array for the CRY gate."""
half_theta = self.params[0] / 2
cos = numpy.cos(half_theta)
sin = numpy.sin(half_theta)
if self.ctrl_state:
return numpy.array([[1, 0, 0, 0],
[0, cos, 0, -sin],
[0, 0, 1, 0],
[0, sin, 0, cos]],
dtype=complex)
else:
return numpy.array([[cos, 0, -sin, 0],
[0, 1, 0, 0],
[sin, 0, cos, 0],
[0, 0, 0, 1]],
dtype=complex)


class CryGate(CRYGate, metaclass=CRYMeta):
Expand Down
Loading