Skip to content
Open
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 cirq-core/cirq/circuits/qasm_output.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,12 @@ def __repr__(self) -> str:

def _decompose_(self, qubits):
q = qubits[0]
phase_correction_half_turns = (self.phi + self.lmda) / 2
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
phase_correction_half_turns = (self.phi + self.lmda) / 2

return [
ops.rz(self.lmda * np.pi).on(q),
ops.ry(self.theta * np.pi).on(q),
ops.rz(self.phi * np.pi).on(q),
ops.global_phase_operation(1j ** (2 * phase_correction_half_turns)),
Copy link
Collaborator

Choose a reason for hiding this comment

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

No need to divide by two above only to multiply by two here

Suggested change
ops.global_phase_operation(1j ** (2 * phase_correction_half_turns)),
ops.global_phase_operation(1j ** (self.phi + self.lmda)),

Copy link
Collaborator

@pavoljuhas pavoljuhas Oct 29, 2025

Choose a reason for hiding this comment

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

Ack. I worked through the multiplication of Rz*Ry*Rz unitaries (as defined in cirq) and the angle of phase coefficient to turn it to the qiskit Ugate unitary is (phi + lambda) / 2. Cirq's QasmUGate parameters are all in half-turns so I used the same scale for the phase correction angle. I'd prefer to save a bit on mental overhead later on when checking this code rather than optimizing out the multiplication / divisions by 2 which are likely negligible in time.

]

def _value_equality_values_(self):
Expand Down
16 changes: 14 additions & 2 deletions cirq-core/cirq/circuits/qasm_output_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,17 @@ def _make_qubits(n):
return [cirq.NamedQubit(f'q{i}') for i in range(n)]


def _qiskit_unitary(gate: QasmUGate) -> np.ndarray:
# Ref: https://quantum.cloud.ibm.com/docs/en/api/qiskit/qiskit.circuit.library.U3Gate#u3gate
th, ph, lm = np.pi * np.array([gate.theta, gate.phi, gate.lmda])
Comment on lines +32 to +34
Copy link
Collaborator

Choose a reason for hiding this comment

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

I would change this to a generating function for just the matrix, so it can be used for some additional tests suggested below

Suggested change
def _qiskit_unitary(gate: QasmUGate) -> np.ndarray:
# Ref: https://quantum.cloud.ibm.com/docs/en/api/qiskit/qiskit.circuit.library.U3Gate#u3gate
th, ph, lm = np.pi * np.array([gate.theta, gate.phi, gate.lmda])
def _ugate_unitary(theta: float, phi: float, lmda: float) -> np.ndarray:
# Ref: https://quantum.cloud.ibm.com/docs/en/api/qiskit/qiskit.circuit.library.U3Gate#u3gate
th, ph, lm = np.pi * np.array([theta, phi, lmda])

Copy link
Collaborator

Choose a reason for hiding this comment

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

Again, I am sure other people are better with keeping track of angle conventions between cirq and qasm. The intent in passing a gate was to avoid having to know angle units when calling _qiskit_unitary. It can sort it out itself from the QasmUGate definition; as a caller I don't need to care.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Fair enough, I would at least rename to _qiskit_ugate_unitary in that case

return np.array(
[
[np.cos(th / 2), -np.exp(1j * lm) * np.sin(th / 2)],
[np.exp(1j * ph) * np.sin(th / 2), np.exp(1j * (ph + lm)) * np.cos(th / 2)],
]
)


def test_u_gate_repr() -> None:
gate = QasmUGate(0.1, 0.2, 0.3)
assert repr(gate) == 'cirq.circuits.qasm_output.QasmUGate(theta=0.1, phi=0.2, lmda=0.3)'
Expand All @@ -53,13 +64,14 @@ def test_qasm_u_qubit_gate_unitary() -> None:
u = cirq.testing.random_unitary(2)
g = QasmUGate.from_matrix(u)
cirq.testing.assert_allclose_up_to_global_phase(cirq.unitary(g), u, atol=1e-7)

cirq.testing.assert_implements_consistent_protocols(g)
np.testing.assert_allclose(cirq.unitary(g), _qiskit_unitary(g), atol=1e-7)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
np.testing.assert_allclose(cirq.unitary(g), _qiskit_unitary(g), atol=1e-7)
np.testing.assert_allclose(cirq.unitary(g), _ugate_unitary(g.theta, g.phi, g.lmda), atol=1e-7)


u = cirq.unitary(cirq.Y)
g = QasmUGate.from_matrix(u)
cirq.testing.assert_allclose_up_to_global_phase(cirq.unitary(g), u, atol=1e-7)
cirq.testing.assert_implements_consistent_protocols(g)
np.testing.assert_allclose(cirq.unitary(g), _qiskit_unitary(g), atol=1e-7)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
np.testing.assert_allclose(cirq.unitary(g), _qiskit_unitary(g), atol=1e-7)
np.testing.assert_allclose(cirq.unitary(g), _ugate_unitary(g.theta, g.phi, g.lmda), atol=1e-7)



def test_qasm_two_qubit_gate_unitary() -> None:
Expand Down Expand Up @@ -200,7 +212,7 @@ def test_h_gate_with_parameter() -> None:
)


def test_qasm_global_pahse() -> None:
def test_qasm_global_phase() -> None:
output = cirq.QasmOutput((cirq.global_phase_operation(np.exp(1j * 5))), ())
assert (
str(output)
Expand Down
4 changes: 2 additions & 2 deletions cirq-core/cirq/contrib/qasm_import/_parser_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -1089,8 +1089,8 @@ def test_standard_gates_wrong_params_error(qasm_gate: str, num_params: int) -> N
# Mapping of two-qubit gates and `num_params`
two_qubit_param_gates = {
# TODO: fix and enable commented gates below
# ('cu1', cirq.ControlledGate(QasmUGate(0, 0, 0.1 / np.pi))): 1,
# ('cu3', cirq.ControlledGate(QasmUGate(0.1 / np.pi, 0.2 / np.pi, 0.3 / np.pi))): 3,
('cu1', cirq.ControlledGate(QasmUGate(0, 0, 0.1 / np.pi))): 1,
('cu3', cirq.ControlledGate(QasmUGate(0.1 / np.pi, 0.2 / np.pi, 0.3 / np.pi))): 3,
# ('cu', cirq.ControlledGate(QasmUGate(0.1 / np.pi, 0.2 / np.pi, 0.3 / np.pi))): 3,
('crx', cirq.ControlledGate(cirq.rx(0.1))): 1,
('cry', cirq.ControlledGate(cirq.ry(0.1))): 1,
Expand Down