diff --git a/qiskit/circuit/library/generalized_gates/diagonal.py b/qiskit/circuit/library/generalized_gates/diagonal.py index f2ca341fab66..a5691df87725 100644 --- a/qiskit/circuit/library/generalized_gates/diagonal.py +++ b/qiskit/circuit/library/generalized_gates/diagonal.py @@ -109,6 +109,7 @@ def __init__(self, target_qubit = num_qubits - num_act_qubits self.ucrz(angles_rz, ctrl_qubits, target_qubit) n //= 2 + self.global_phase += diag_phases[0] # extract a Rz rotation (angle given by first output) such that exp(j*phase)*Rz(z_angle) diff --git a/qiskit/extensions/quantum_initializer/diagonal.py b/qiskit/extensions/quantum_initializer/diagonal.py index 96f4e48befac..d9a685f7e81c 100644 --- a/qiskit/extensions/quantum_initializer/diagonal.py +++ b/qiskit/extensions/quantum_initializer/diagonal.py @@ -99,6 +99,7 @@ def _dec_diag(self): target_qubit = q[self.num_qubits - num_act_qubits] circuit.ucrz(angles_rz, contr_qubits, target_qubit) n //= 2 + circuit.global_phase += diag_phases[0] return circuit diff --git a/qiskit/quantum_info/operators/operator.py b/qiskit/quantum_info/operators/operator.py index 7b6025a7fe59..b7941eceff53 100644 --- a/qiskit/quantum_info/operators/operator.py +++ b/qiskit/quantum_info/operators/operator.py @@ -531,7 +531,7 @@ def _append_instruction(self, obj, qargs=None): 'definition is {} but expected QuantumCircuit.'.format( obj.name, type(obj.definition))) if obj.definition.global_phase: - dimension = 2 ** self.num_qubits + dimension = 2 ** obj.num_qubits op = self.compose( ScalarOp(dimension, np.exp(1j * float(obj.definition.global_phase))), qargs=qargs) diff --git a/qiskit/quantum_info/synthesis/one_qubit_decompose.py b/qiskit/quantum_info/synthesis/one_qubit_decompose.py index 0a640d8e194a..08f1f5cbbb22 100644 --- a/qiskit/quantum_info/synthesis/one_qubit_decompose.py +++ b/qiskit/quantum_info/synthesis/one_qubit_decompose.py @@ -338,12 +338,13 @@ def _circuit_psx(theta, # Phase(phi+pi).SX.Phase(theta+pi).SX.Phase(lam) theta = _mod2pi(theta + np.pi) phi = _mod2pi(phi + np.pi) - circuit = QuantumCircuit(1, global_phase=phase) + circuit = QuantumCircuit(1, global_phase=phase - np.pi / 2) # Check for decomposition into minimimal number required SX gates if simplify and np.isclose(abs(theta), np.pi, atol=atol): if not np.isclose(_mod2pi(abs(lam + phi + theta)), [0., 2*np.pi], atol=atol).any(): circuit.append(PhaseGate(_mod2pi(lam + phi + theta)), [0]) + circuit.global_phase += np.pi / 2 elif simplify and np.isclose(abs(theta), [np.pi/2, 3*np.pi/2], atol=atol).any(): if not np.isclose(_mod2pi(abs(lam + theta)), @@ -353,6 +354,8 @@ def _circuit_psx(theta, if not np.isclose(_mod2pi(abs(phi + theta)), [0., 2*np.pi], atol=atol).any(): circuit.append(PhaseGate(_mod2pi(phi + theta)), [0]) + if np.isclose(theta, [-np.pi / 2, 3 * np.pi / 2], atol=atol).any(): + circuit.global_phase += np.pi / 2 else: if not np.isclose(abs(lam), [0., 2*np.pi], atol=atol).any(): circuit.append(PhaseGate(lam), [0]) @@ -375,30 +378,39 @@ def _circuit_zsx(theta, # RZ(phi+pi).SX.RZ(theta+pi).SX.RZ(lam) theta = _mod2pi(theta + np.pi) phi = _mod2pi(phi + np.pi) - circuit = QuantumCircuit(1, global_phase=phase) + circuit = QuantumCircuit(1, global_phase=phase - np.pi / 2) # Check for decomposition into minimimal number required SX gates if simplify and np.isclose(abs(theta), np.pi, atol=atol): if not np.isclose(_mod2pi(abs(lam + phi + theta)), [0., 2*np.pi], atol=atol).any(): circuit.append(RZGate(_mod2pi(lam + phi + theta)), [0]) + circuit.global_phase += 0.5 * _mod2pi(lam + phi + theta) + circuit.global_phase += np.pi / 2 elif simplify and np.isclose(abs(theta), [np.pi/2, 3*np.pi/2], atol=atol).any(): if not np.isclose(_mod2pi(abs(lam + theta)), [0., 2*np.pi], atol=atol).any(): circuit.append(RZGate(_mod2pi(lam + theta)), [0]) + circuit.global_phase += 0.5 * _mod2pi(lam + theta) circuit.append(SXGate(), [0]) if not np.isclose(_mod2pi(abs(phi + theta)), [0., 2*np.pi], atol=atol).any(): circuit.append(RZGate(_mod2pi(phi + theta)), [0]) + circuit.global_phase += 0.5 * _mod2pi(phi + theta) + if np.isclose(theta, [-np.pi / 2, 3 * np.pi / 2], atol=atol).any(): + circuit.global_phase += np.pi / 2 else: if not np.isclose(abs(lam), [0., 2*np.pi], atol=atol).any(): circuit.append(RZGate(lam), [0]) + circuit.global_phase += 0.5 * lam circuit.append(SXGate(), [0]) if not np.isclose(abs(theta), [0., 2*np.pi], atol=atol).any(): circuit.append(RZGate(theta), [0]) + circuit.global_phase += 0.5 * theta circuit.append(SXGate(), [0]) if not np.isclose(abs(phi), [0., 2*np.pi], atol=atol).any(): circuit.append(RZGate(phi), [0]) + circuit.global_phase += 0.5 * phi return circuit @staticmethod diff --git a/test/python/circuit/library/test_diagonal.py b/test/python/circuit/library/test_diagonal.py index b4f02d24644b..f3acac59dfc8 100644 --- a/test/python/circuit/library/test_diagonal.py +++ b/test/python/circuit/library/test_diagonal.py @@ -19,6 +19,7 @@ from qiskit.test.base import QiskitTestCase from qiskit.circuit.library import Diagonal from qiskit.quantum_info import Statevector, Operator +from qiskit.quantum_info.operators.predicates import matrix_equal @ddt @@ -37,10 +38,10 @@ def test_diag_gate(self, phases): """Test correctness of diagonal decomposition.""" diag = [np.exp(1j * ph) for ph in phases] qc = Diagonal(diag) - simulated_diag = Statevector(Operator(qc).data.diagonal()) - ref_diag = Statevector(diag) + simulated_diag = Statevector(Operator(qc).data.diagonal()).data + ref_diag = Statevector(diag).data - self.assertTrue(simulated_diag.equiv(ref_diag)) + self.assertTrue(matrix_equal(simulated_diag, ref_diag, ignore_phase=False)) if __name__ == '__main__': diff --git a/test/python/circuit/test_diagonal_gate.py b/test/python/circuit/test_diagonal_gate.py index 900751c205ef..f79c43d08369 100644 --- a/test/python/circuit/test_diagonal_gate.py +++ b/test/python/circuit/test_diagonal_gate.py @@ -41,13 +41,13 @@ def test_diag_gate(self): qc = QuantumCircuit(q) qc.diagonal(diag, q[0:num_qubits]) # Decompose the gate - qc = transpile(qc, basis_gates=['u1', 'u3', 'u2', 'cx', 'id']) + qc = transpile(qc, basis_gates=['u1', 'u3', 'u2', 'cx', 'id'], optimization_level=0) # Simulate the decomposed gate simulator = BasicAer.get_backend('unitary_simulator') result = execute(qc, simulator).result() unitary = result.get_unitary(qc) unitary_desired = _get_diag_gate_matrix(diag) - self.assertTrue(matrix_equal(unitary, unitary_desired, ignore_phase=True)) + self.assertTrue(matrix_equal(unitary, unitary_desired, ignore_phase=False)) def _get_diag_gate_matrix(diag): diff --git a/test/python/quantum_info/test_synthesis.py b/test/python/quantum_info/test_synthesis.py index 4e32f93ad582..14bea519f51f 100644 --- a/test/python/quantum_info/test_synthesis.py +++ b/test/python/quantum_info/test_synthesis.py @@ -22,7 +22,7 @@ from qiskit import execute from qiskit.circuit import QuantumCircuit, QuantumRegister from qiskit.extensions import UnitaryGate -from qiskit.circuit.library import (HGate, IGate, SdgGate, SGate, U3Gate, +from qiskit.circuit.library import (HGate, IGate, SdgGate, SGate, U3Gate, UGate, XGate, YGate, ZGate, CXGate, CZGate, iSwapGate, RXXGate) from qiskit.providers.basicaer import UnitarySimulatorPy @@ -79,7 +79,7 @@ class CheckDecompositions(QiskitTestCase): """Implements decomposition checkers.""" def check_one_qubit_euler_angles(self, operator, basis='U3', tolerance=1e-12, - phase_equal=False): + phase_equal=True): """Check OneQubitEulerDecomposer works for the given unitary""" target_unitary = operator.data if basis is None: @@ -162,7 +162,7 @@ class TestOneQubitEulerDecomposer(CheckDecompositions): def check_one_qubit_euler_angles(self, operator, basis='U3', tolerance=1e-12, - phase_equal=False): + phase_equal=True): """Check euler_angles_1q works for the given unitary""" decomposer = OneQubitEulerDecomposer(basis) with self.subTest(operator=operator): @@ -176,7 +176,7 @@ def check_one_qubit_euler_angles(self, operator, basis='U3', maxdist = np.max(np.abs(target_unitary + decomp_unitary)) self.assertTrue(np.abs(maxdist) < tolerance, "Worst distance {}".format(maxdist)) - @combine(basis=['U3', 'U1X', 'ZYZ', 'ZXZ', 'XYX', 'RR'], + @combine(basis=['U3', 'U1X', 'PSX', 'ZSX', 'ZYZ', 'ZXZ', 'XYX', 'RR'], name='test_one_qubit_clifford_{basis}_basis') def test_one_qubit_clifford_all_basis(self, basis): """Verify for {basis} basis and all Cliffords.""" @@ -188,6 +188,8 @@ def test_one_qubit_clifford_all_basis(self, basis): ('ZXZ', 1e-12), ('ZYZ', 1e-12), ('U1X', 1e-7), + ('PSX', 1e-7), + ('ZSX', 1e-7), ('RR', 1e-12)], name='test_one_qubit_hard_thetas_{basis_tolerance[0]}_basis') # Lower tolerance for U1X test since decomposition since it is @@ -199,13 +201,43 @@ def test_one_qubit_hard_thetas_all_basis(self, basis_tolerance): self.check_one_qubit_euler_angles(Operator(gate), basis_tolerance[0], basis_tolerance[1]) - @combine(basis=['U3', 'U1X', 'ZYZ', 'ZXZ', 'XYX', 'RR'], seed=range(50), + @combine(basis=['U3', 'U1X', 'PSX', 'ZSX', 'ZYZ', 'ZXZ', 'XYX', 'RR'], seed=range(50), name='test_one_qubit_random_{basis}_basis_{seed}') def test_one_qubit_random_all_basis(self, basis, seed): """Verify for {basis} basis and random_unitary (seed={seed}).""" unitary = random_unitary(2, seed=seed) self.check_one_qubit_euler_angles(unitary, basis) + def test_psx_zsx_special_cases(self): + """Test decompositions of psx and zsx at special values of parameters""" + oqed_psx = OneQubitEulerDecomposer(basis='PSX') + oqed_zsx = OneQubitEulerDecomposer(basis='ZSX') + theta = np.pi / 3 + phi = np.pi / 5 + lam = np.pi / 7 + test_gates = [UGate(np.pi, phi, lam), UGate(-np.pi, phi, lam), + # test abs(lam + phi + theta) near 0 + UGate(np.pi, np.pi / 3, 2 * np.pi / 3), + # test theta=pi/2 + UGate(np.pi / 2, phi, lam), + # test theta=pi/2 and theta+lam=0 + UGate(np.pi / 2, phi, -np.pi / 2), + # test theta close to 3*pi/2 and theta+phi=2*pi + UGate(3*np.pi / 2, np.pi / 2, lam), + # test theta 0 + UGate(0, phi, lam), + # test phi 0 + UGate(theta, 0, lam), + # test lam 0 + UGate(theta, phi, 0)] + + for gate in test_gates: + unitary = gate.to_matrix() + qc_psx = oqed_psx(unitary) + qc_zsx = oqed_zsx(unitary) + self.assertTrue(np.allclose(unitary, Operator(qc_psx).data)) + self.assertTrue(np.allclose(unitary, Operator(qc_zsx).data)) + # FIXME: streamline the set of test cases class TestTwoQubitWeylDecomposition(CheckDecompositions): diff --git a/test/randomized/test_synthesis.py b/test/randomized/test_synthesis.py index e07cd907a413..142134249cf7 100644 --- a/test/randomized/test_synthesis.py +++ b/test/randomized/test_synthesis.py @@ -39,6 +39,8 @@ def test_1q_random(self, seed): self.check_one_qubit_euler_angles(unitary) self.check_one_qubit_euler_angles(unitary, 'U3') self.check_one_qubit_euler_angles(unitary, 'U1X') + self.check_one_qubit_euler_angles(unitary, 'PSX') + self.check_one_qubit_euler_angles(unitary, 'ZSX') self.check_one_qubit_euler_angles(unitary, 'ZYZ') self.check_one_qubit_euler_angles(unitary, 'ZXZ') self.check_one_qubit_euler_angles(unitary, 'XYX')