Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions qiskit/circuit/library/grover_operator.py
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,8 @@ def _build(self):
self.barrier()
self.compose(self.state_preparation, list(range(self.state_preparation.num_qubits)),
inplace=True)
# minus sign
self.global_phase = numpy.pi


# TODO use the oracle compiler or the bit string oracle
Expand Down
2 changes: 1 addition & 1 deletion qiskit/circuit/quantumcircuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -1799,7 +1799,7 @@ def global_phase(self, angle):
Args:
angle (float, ParameterExpression): radians
"""
if isinstance(angle, ParameterExpression):
if isinstance(angle, ParameterExpression) and angle.parameters:
self._global_phase = angle
else:
# Set the phase to the [-2 * pi, 2 * pi] interval
Expand Down
6 changes: 4 additions & 2 deletions qiskit/transpiler/passes/basis/basis_translator.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,12 +151,14 @@ def run(self, dag):
bound_target_dag = circuit_to_dag(target_circuit)
else:
bound_target_dag = target_dag
if bound_target_dag.global_phase:
dag.global_phase += bound_target_dag.global_phase

if (len(bound_target_dag.op_nodes()) == 1
and len(bound_target_dag.op_nodes()[0].qargs) == len(node.qargs)):
dag_op = bound_target_dag.op_nodes()[0].op
dag.substitute_node(node, dag_op, inplace=True)

if bound_target_dag.global_phase:
dag.global_phase += bound_target_dag.global_phase
else:
dag.substitute_node_with_dag(node, bound_target_dag)
else:
Expand Down
3 changes: 0 additions & 3 deletions qiskit/transpiler/passes/basis/unroller.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,5 @@ def run(self, dag):
(str(self.basis), node.op.name))
decomposition = circuit_to_dag(node.op.definition)
unrolled_dag = self.run(decomposition) # recursively unroll ops
if unrolled_dag.global_phase:
dag.global_phase += unrolled_dag.global_phase
unrolled_dag.global_phase = 0
dag.substitute_node_with_dag(node, unrolled_dag)
return dag
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
fixes:
- |
The :class:`~qiskit.transpiler.passes.BasisTranslator` and :class:`~qiskit.transpiler.passes.Unroller` passes, in some cases, had not been
preserving the global phase of the circuit under transpilation. This has
been fixed.
3 changes: 1 addition & 2 deletions test/python/circuit/library/test_grover_operator.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,12 +106,11 @@ def test_custom_zero_reflection(self):
self.assertGroverOperatorIsCorrect(grover_op, oracle)

with self.subTest('circuits match'):
expected = QuantumCircuit(*grover_op.qregs)
expected = QuantumCircuit(*grover_op.qregs, global_phase=np.pi)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this isn't correct on stable/0.16, the global phase of pi was added to the grover operator in #5517: https://github.com/Qiskit/qiskit-terra/pull/5517/files#diff-5cc16ff691e55c7c0e9d381f48c31157dcc945f3107f4e6ea246b2a2bcce986e we should check with @Cryoris if that needs to be backported too or not

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can include that phase change from 5517, since then the Grover operator fits the definition from the papers 🙂

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, I'll add that here then

expected.compose(oracle, inplace=True)
expected.h(0) # state_in is H
expected.compose(zero_reflection, inplace=True)
expected.h(0)

self.assertEqual(expected, grover_op)

def test_num_mcx_ancillas(self):
Expand Down
4 changes: 1 addition & 3 deletions test/python/circuit/test_circuit_qasm.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@

from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit
from qiskit.test import QiskitTestCase
from qiskit.quantum_info import random_unitary


class TestCircuitQasm(QiskitTestCase):
Expand Down Expand Up @@ -176,8 +175,7 @@ def test_circuit_qasm_pi(self):
"""Test circuit qasm() method with pi params.
"""
circuit = QuantumCircuit(2)
circuit.append(random_unitary(4, seed=1234), [0, 1])
circuit = circuit.decompose()
circuit.cz(0, 1)
circuit.u(2*pi, 3*pi, -5*pi, 0)
qasm_str = circuit.qasm()
circuit2 = QuantumCircuit.from_qasm_str(qasm_str)
Expand Down
14 changes: 8 additions & 6 deletions test/python/compiler/test_transpiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -436,7 +436,7 @@ def test_parameterized_circuit_for_simulator(self):

transpiled_qc = transpile(qc, backend=BasicAer.get_backend('qasm_simulator'))

expected_qc = QuantumCircuit(qr)
expected_qc = QuantumCircuit(qr, global_phase=-1 * theta / 2.0)
expected_qc.append(U1Gate(theta), [qr[0]])

self.assertEqual(expected_qc, transpiled_qc)
Expand All @@ -453,7 +453,7 @@ def test_parameterized_circuit_for_device(self):
initial_layout=Layout.generate_trivial_layout(qr))

qr = QuantumRegister(14, 'q')
expected_qc = QuantumCircuit(qr)
expected_qc = QuantumCircuit(qr, global_phase=-1 * theta / 2.0)
expected_qc.append(U1Gate(theta), [qr[0]])

self.assertEqual(expected_qc, transpiled_qc)
Expand All @@ -470,7 +470,7 @@ def test_parameter_expression_circuit_for_simulator(self):

transpiled_qc = transpile(qc, backend=BasicAer.get_backend('qasm_simulator'))

expected_qc = QuantumCircuit(qr)
expected_qc = QuantumCircuit(qr, global_phase=-1 * square / 2.0)
expected_qc.append(U1Gate(square), [qr[0]])
self.assertEqual(expected_qc, transpiled_qc)

Expand All @@ -488,7 +488,7 @@ def test_parameter_expression_circuit_for_device(self):
initial_layout=Layout.generate_trivial_layout(qr))

qr = QuantumRegister(14, 'q')
expected_qc = QuantumCircuit(qr)
expected_qc = QuantumCircuit(qr, global_phase=-1 * square / 2.0)
expected_qc.append(U1Gate(square), [qr[0]])
self.assertEqual(expected_qc, transpiled_qc)

Expand Down Expand Up @@ -927,14 +927,16 @@ def test_circuit_with_delay(self, optimization_level):
@data(1, 2, 3)
def test_no_infinite_loop(self, optimization_level):
"""Verify circuit cost always descends and optimization does not flip flop indefinitely."""

qc = QuantumCircuit(1)
qc.ry(0.2, 0)

out = transpile(qc, basis_gates=['id', 'p', 'sx', 'cx'],
optimization_level=optimization_level)

expected = QuantumCircuit(1)
# Expect a -pi/2 global phase for the U3 to RZ/SX conversion, and
# a -0.5 * theta phase for RZ to P twice, once at theta, and once at 3 pi
# for the second and third RZ gates in the U3 decomposition.
expected = QuantumCircuit(1, global_phase=-np.pi/2 - 0.5 * (0.2 + np.pi) - 0.5 * 3 * np.pi)
expected.sx(0)
expected.p(np.pi + 0.2, 0)
expected.sx(0)
Expand Down
183 changes: 146 additions & 37 deletions test/python/transpiler/test_basis_translator.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,138 @@ def test_double_substitution(self):

self.assertEqual(actual, expected_dag)

def test_single_substitution_with_global_phase(self):
"""Verify we correctly unroll gates through a single equivalence with global phase."""
eq_lib = EquivalenceLibrary()

gate = OneQubitZeroParamGate()
equiv = QuantumCircuit(1, global_phase=0.2)
equiv.append(OneQubitOneParamGate(pi), [0])

eq_lib.add_equivalence(gate, equiv)

qc = QuantumCircuit(1, global_phase=0.1)
qc.append(OneQubitZeroParamGate(), [0])
dag = circuit_to_dag(qc)

expected = QuantumCircuit(1, global_phase=0.1 + 0.2)
expected.append(OneQubitOneParamGate(pi), [0])
expected_dag = circuit_to_dag(expected)

pass_ = BasisTranslator(eq_lib, ['1q1p'])
actual = pass_.run(dag)

self.assertEqual(actual, expected_dag)

def test_single_two_gate_substitution_with_global_phase(self):
"""Verify we correctly unroll gates through a single equivalence with global phase."""
eq_lib = EquivalenceLibrary()

gate = OneQubitZeroParamGate()
equiv = QuantumCircuit(1, global_phase=0.2)
equiv.append(OneQubitOneParamGate(pi), [0])
equiv.append(OneQubitOneParamGate(pi), [0])

eq_lib.add_equivalence(gate, equiv)

qc = QuantumCircuit(1, global_phase=0.1)
qc.append(OneQubitZeroParamGate(), [0])
dag = circuit_to_dag(qc)

expected = QuantumCircuit(1, global_phase=0.1 + 0.2)
expected.append(OneQubitOneParamGate(pi), [0])
expected.append(OneQubitOneParamGate(pi), [0])
expected_dag = circuit_to_dag(expected)

pass_ = BasisTranslator(eq_lib, ['1q1p'])
actual = pass_.run(dag)

self.assertEqual(actual, expected_dag)

def test_two_substitutions_with_global_phase(self):
"""Verify we correctly unroll gates through a single equivalences with global phase."""
eq_lib = EquivalenceLibrary()

gate = OneQubitZeroParamGate()
equiv = QuantumCircuit(1, global_phase=0.2)
equiv.append(OneQubitOneParamGate(pi), [0])

eq_lib.add_equivalence(gate, equiv)

qc = QuantumCircuit(1, global_phase=0.1)
qc.append(OneQubitZeroParamGate(), [0])
qc.append(OneQubitZeroParamGate(), [0])
dag = circuit_to_dag(qc)

expected = QuantumCircuit(1, global_phase=0.1 + 2 * 0.2)
expected.append(OneQubitOneParamGate(pi), [0])
expected.append(OneQubitOneParamGate(pi), [0])
expected_dag = circuit_to_dag(expected)

pass_ = BasisTranslator(eq_lib, ['1q1p'])
actual = pass_.run(dag)

self.assertEqual(actual, expected_dag)

def test_two_single_two_gate_substitutions_with_global_phase(self):
"""Verify we correctly unroll gates through a single equivalence with global phase."""
eq_lib = EquivalenceLibrary()

gate = OneQubitZeroParamGate()
equiv = QuantumCircuit(1, global_phase=0.2)
equiv.append(OneQubitOneParamGate(pi), [0])
equiv.append(OneQubitOneParamGate(pi), [0])

eq_lib.add_equivalence(gate, equiv)

qc = QuantumCircuit(1, global_phase=0.1)
qc.append(OneQubitZeroParamGate(), [0])
qc.append(OneQubitZeroParamGate(), [0])
dag = circuit_to_dag(qc)

expected = QuantumCircuit(1, global_phase=0.1 + 2 * 0.2)
expected.append(OneQubitOneParamGate(pi), [0])
expected.append(OneQubitOneParamGate(pi), [0])
expected.append(OneQubitOneParamGate(pi), [0])
expected.append(OneQubitOneParamGate(pi), [0])

expected_dag = circuit_to_dag(expected)

pass_ = BasisTranslator(eq_lib, ['1q1p'])
actual = pass_.run(dag)

self.assertEqual(actual, expected_dag)

def test_double_substitution_with_global_phase(self):
"""Verify we correctly unroll gates through multiple equivalences with global phase."""
eq_lib = EquivalenceLibrary()

gate = OneQubitZeroParamGate()
equiv = QuantumCircuit(1, global_phase=0.2)
equiv.append(OneQubitOneParamGate(pi), [0])

eq_lib.add_equivalence(gate, equiv)

theta = Parameter('theta')
gate = OneQubitOneParamGate(theta)
equiv = QuantumCircuit(1, global_phase=0.4)
equiv.append(OneQubitTwoParamGate(theta, pi/2), [0])

eq_lib.add_equivalence(gate, equiv)

qc = QuantumCircuit(1, global_phase=0.1)
qc.append(OneQubitZeroParamGate(), [0])
dag = circuit_to_dag(qc)

expected = QuantumCircuit(1, global_phase=0.1 + 0.2 + 0.4)
expected.append(OneQubitTwoParamGate(pi, pi/2), [0])
expected_dag = circuit_to_dag(expected)

pass_ = BasisTranslator(eq_lib, ['1q2p'])
actual = pass_.run(dag)

self.assertEqual(actual, expected_dag)

def test_multiple_variadic(self):
"""Verify circuit with multiple instances of variadic gate."""
eq_lib = EquivalenceLibrary()
Expand Down Expand Up @@ -299,7 +431,8 @@ def test_unroll_1q_chain_conditional(self):
pass_ = BasisTranslator(std_eqlib, ['u1', 'u2', 'u3'])
unrolled_dag = pass_.run(dag)

ref_circuit = QuantumCircuit(qr, cr)
# Pick up -1 * 0.3 / 2 global phase for one RZ -> U1.
ref_circuit = QuantumCircuit(qr, cr, global_phase=-0.3 / 2)
ref_circuit.append(U2Gate(0, pi), [qr[0]])
ref_circuit.append(U1Gate(-pi/4), [qr[0]])
ref_circuit.append(U1Gate(pi), [qr[0]])
Expand All @@ -312,6 +445,7 @@ def test_unroll_1q_chain_conditional(self):
ref_circuit.append(U3Gate(pi, pi/2, pi/2), [qr[0]]).c_if(cr, 1)
ref_circuit.append(U1Gate(pi), [qr[0]]).c_if(cr, 1)
ref_dag = circuit_to_dag(ref_circuit)

self.assertEqual(unrolled_dag, ref_dag)

def test_unroll_no_basis(self):
Expand Down Expand Up @@ -488,7 +622,7 @@ def test_simple_unroll_parameterized_without_expressions(self):

unrolled_dag = BasisTranslator(std_eqlib, ['u1', 'cx']).run(dag)

expected = QuantumCircuit(qr)
expected = QuantumCircuit(qr, global_phase=-theta / 2)
expected.append(U1Gate(theta), [qr[0]])

self.assertEqual(circuit_to_dag(expected), unrolled_dag)
Expand All @@ -509,7 +643,7 @@ def test_simple_unroll_parameterized_with_expressions(self):

unrolled_dag = BasisTranslator(std_eqlib, ['p', 'cx']).run(dag)

expected = QuantumCircuit(qr)
expected = QuantumCircuit(qr, global_phase=-sum_ / 2)
expected.p(sum_, qr[0])

self.assertEqual(circuit_to_dag(expected), unrolled_dag)
Expand Down Expand Up @@ -558,7 +692,9 @@ def test_unrolling_parameterized_composite_gates(self):

out_dag = BasisTranslator(mock_sel, ['p', 'cx']).run(dag)

expected = QuantumCircuit(qr2)
# Pick up -1 * theta / 2 global phase four twice (once for each RZ -> P
# in each of the two sub_instr instructions).
expected = QuantumCircuit(qr2, global_phase=-1 * 4 * theta / 2.0)
expected.p(theta, qr2[0])
expected.p(theta, qr2[2])
expected.cx(qr2[0], qr2[1])
Expand All @@ -585,7 +721,7 @@ def test_unrolling_parameterized_composite_gates(self):

out_dag = BasisTranslator(mock_sel, ['p', 'cx']).run(dag)

expected = QuantumCircuit(qr2)
expected = QuantumCircuit(qr2, global_phase=-1 * (2 * phi + 2 * gamma) / 2.0)
expected.p(phi, qr2[0])
expected.p(gamma, qr2[2])
expected.cx(qr2[0], qr2[1])
Expand All @@ -608,22 +744,8 @@ def test_cx_bell_to_cz(self):
in_dag = circuit_to_dag(bell)
out_dag = BasisTranslator(std_eqlib, ['cz', 'rx', 'rz']).run(in_dag)

qr = QuantumRegister(2, 'q')
expected = QuantumCircuit(qr)
expected.rz(pi, qr)
expected.rx(pi / 2, qr)
expected.rz(3 * pi / 2, qr)
expected.rx(pi / 2, qr)
expected.rz(3 * pi, qr)
expected.cz(qr[0], qr[1])
expected.rz(pi, qr[1])
expected.rx(pi / 2, qr[1])
expected.rz(3 * pi / 2, qr[1])
expected.rx(pi / 2, qr[1])
expected.rz(3 * pi, qr[1])
expected_dag = circuit_to_dag(expected)

self.assertEqual(out_dag, expected_dag)
self.assertTrue(set(out_dag.count_ops()).issubset(['cz', 'rx', 'rz']))
self.assertEqual(Operator(bell), Operator(dag_to_circuit(out_dag)))

def test_cx_bell_to_iswap(self):
"""Verify we can translate a CX bell to iSwap,U3."""
Expand All @@ -634,22 +756,8 @@ def test_cx_bell_to_iswap(self):
in_dag = circuit_to_dag(bell)
out_dag = BasisTranslator(std_eqlib, ['iswap', 'u']).run(in_dag)

qr = QuantumRegister(2, 'q')
expected = QuantumCircuit(2)
expected.u(pi / 2, 0, pi, qr[0])
expected.u(pi, 0, pi, qr[1])
expected.u(pi / 2, 0, pi, qr)
expected.iswap(qr[0], qr[1])
expected.u(pi, 0, pi, qr)
expected.u(pi / 2, 0, pi, qr[1])
expected.iswap(qr[0], qr[1])
expected.u(pi / 2, 0, pi, qr[0])
expected.u(0, 0, pi / 2, qr)
expected.u(pi, 0, pi, qr[1])
expected.u(pi / 2, 0, pi, qr[1])
expected_dag = circuit_to_dag(expected)

self.assertEqual(out_dag, expected_dag)
self.assertTrue(set(out_dag.count_ops()).issubset(['iswap', 'u']))
self.assertEqual(Operator(bell), Operator(dag_to_circuit(out_dag)))

def test_global_phase(self):
"""Verify global phase preserved in basis translation"""
Expand All @@ -668,3 +776,4 @@ def test_global_phase(self):
expected_dag = circuit_to_dag(expected)
self.assertEqual(out_dag, expected_dag)
self.assertEqual(float(out_dag.global_phase), float(expected_dag.global_phase))
self.assertEqual(Operator(dag_to_circuit(out_dag)), Operator(expected))
Loading