From b15e98406f2f7aa03c44c1563ccbb6f75004843f Mon Sep 17 00:00:00 2001 From: Dave Bacon Date: Fri, 13 May 2022 20:41:16 +0000 Subject: [PATCH 1/3] Fix qasm output --- cirq-core/cirq/circuits/qasm_output_test.py | 44 ++++++++++++++++- cirq-core/cirq/ops/common_gates.py | 54 +++++++++++++++------ cirq-core/cirq/ops/controlled_operation.py | 35 ++++++++++++- 3 files changed, 116 insertions(+), 17 deletions(-) diff --git a/cirq-core/cirq/circuits/qasm_output_test.py b/cirq-core/cirq/circuits/qasm_output_test.py index 4264b65530d..09147e248bf 100644 --- a/cirq-core/cirq/circuits/qasm_output_test.py +++ b/cirq-core/cirq/circuits/qasm_output_test.py @@ -270,19 +270,39 @@ def __repr__(self): return 'DummyCompositeOperation()' return ( + cirq.I(q0), cirq.Z(q0), cirq.Z(q0) ** 0.625, + cirq.Z(q0) ** 0, cirq.Y(q0), cirq.Y(q0) ** 0.375, + cirq.Y(q0) ** 0, cirq.X(q0), cirq.X(q0) ** 0.875, - cirq.H(q1), + cirq.X(q0) ** 0, + cirq.H(q0), + cirq.H(q0) ** 0, cirq.X(q0) ** 0.5, cirq.X(q0) ** -0.5, + cirq.S(q0), + cirq.Z(q0) ** -0.5, + cirq.T(q0), + cirq.Z(q0) ** -0.25, + cirq.Rx(rads=np.pi)(q0), + cirq.Rx(rads=np.pi / 2)(q0), + cirq.Rx(rads=np.pi / 4)(q0), + cirq.Ry(rads=np.pi)(q0), + cirq.Ry(rads=np.pi / 2)(q0), + cirq.Ry(rads=np.pi / 4)(q0), + cirq.Rz(rads=np.pi)(q0), + cirq.Rz(rads=np.pi / 2)(q0), + cirq.Rz(rads=np.pi / 4)(q0), cirq.CZ(q0, q1), cirq.CZ(q0, q1) ** 0.25, # Requires 2-qubit decomposition cirq.CNOT(q0, q1), cirq.CNOT(q0, q1) ** 0.5, # Requires 2-qubit decomposition + cirq.ControlledGate(cirq.Y)(q0, q1), + cirq.ControlledGate(cirq.H)(q0, q1), cirq.SWAP(q0, q1), cirq.SWAP(q0, q1) ** 0.75, # Requires 2-qubit decomposition cirq.CCZ(q0, q1, q2), @@ -356,15 +376,33 @@ def filter_unpredictable_numbers(text): creg m_multi[3]; +id q[0]; z q[0]; rz(pi*0.625) q[0]; +id q[0]; y q[0]; ry(pi*0.375) q[0]; +id q[0]; x q[0]; rx(pi*0.875) q[0]; -h q[1]; +id q[0]; +h q[0]; +id q[0]; sx q[0]; sxdg q[0]; +s q[0]; +sdg q[0]; +t q[0]; +tdg q[0]; +rx(pi*1.0) q[0]; +rx(pi*0.5) q[0]; +rx(pi*0.25) q[0]; +ry(pi*1.0) q[0]; +ry(pi*0.5) q[0]; +ry(pi*0.25) q[0]; +rz(pi*1.0) q[0]; +rz(pi*0.5) q[0]; +rz(pi*0.25) q[0]; cz q[0],q[1]; // Gate: CZ**0.25 @@ -399,6 +437,8 @@ def filter_unpredictable_numbers(text): u3(pi*0.5,pi*0.5,pi*1.0) q[1]; ry(pi*0.5) q[1]; +cy q[0],q[1]; +ch q[0],q[1]; swap q[0],q[1]; // Gate: SWAP**0.75 diff --git a/cirq-core/cirq/ops/common_gates.py b/cirq-core/cirq/ops/common_gates.py index c846d16de2a..3b8a840995e 100644 --- a/cirq-core/cirq/ops/common_gates.py +++ b/cirq-core/cirq/ops/common_gates.py @@ -219,12 +219,15 @@ def _circuit_diagram_info_( def _qasm_(self, args: 'cirq.QasmArgs', qubits: Tuple['cirq.Qid', ...]) -> Optional[str]: args.validate_version('2.0') - if self._exponent == 1 and self._global_shift != -0.5: - return args.format('x {0};\n', qubits[0]) - elif self._exponent == 0.5: - return args.format('sx {0};\n', qubits[0]) - elif self._exponent == -0.5: - return args.format('sxdg {0};\n', qubits[0]) + if self._exponent == 0: + return args.format('id {0};\n', qubits[0]) + if self._global_shift != -0.5: + if self._exponent == 1: + return args.format('x {0};\n', qubits[0]) + elif self._exponent == 0.5: + return args.format('sx {0};\n', qubits[0]) + elif self._exponent == -0.5: + return args.format('sxdg {0};\n', qubits[0]) return args.format('rx({0:half_turns}) {1};\n', self._exponent, qubits[0]) def _quil_( @@ -308,6 +311,10 @@ def __str__(self) -> str: def __repr__(self) -> str: return f'cirq.Rx(rads={proper_repr(self._rads)})' + def _qasm_(self, args: 'cirq.QasmArgs', qubits: Tuple['cirq.Qid', ...]) -> Optional[str]: + args.validate_version('2.0') + return args.format('rx({0:half_turns}) {1};\n', self._exponent, qubits[0]) + def _json_dict_(self) -> Dict[str, Any]: return {'rads': self._rads} @@ -402,6 +409,8 @@ def _qasm_(self, args: 'cirq.QasmArgs', qubits: Tuple['cirq.Qid', ...]) -> Optio args.validate_version('2.0') if self._exponent == 1 and self.global_shift != -0.5: return args.format('y {0};\n', qubits[0]) + elif self._exponent == 0: + return args.format('id {0};\n', qubits[0]) return args.format('ry({0:half_turns}) {1};\n', self._exponent, qubits[0]) @@ -480,6 +489,10 @@ def __str__(self) -> str: def __repr__(self) -> str: return f'cirq.Ry(rads={proper_repr(self._rads)})' + def _qasm_(self, args: 'cirq.QasmArgs', qubits: Tuple['cirq.Qid', ...]) -> Optional[str]: + args.validate_version('2.0') + return args.format('ry({0:half_turns}) {1};\n', self._exponent, qubits[0]) + def _json_dict_(self) -> Dict[str, Any]: return {'rads': self._rads} @@ -654,13 +667,20 @@ def _circuit_diagram_info_( def _qasm_(self, args: 'cirq.QasmArgs', qubits: Tuple['cirq.Qid', ...]) -> Optional[str]: args.validate_version('2.0') - if self._exponent == 1 and self.global_shift != -0.5: - return args.format('z {0};\n', qubits[0]) - elif self._exponent == 0.5: - return args.format('s {0};\n', qubits[0]) - elif self._exponent == -0.5: - return args.format('sdg {0};\n', qubits[0]) - + if self._exponent == 0: + return args.format('id {0};\n', qubits[0]) + if self.global_shift != -0.5: + # Not an Rz gate. + if self._exponent == 1: + return args.format('z {0};\n', qubits[0]) + elif self._exponent == 0.5: + return args.format('s {0};\n', qubits[0]) + elif self._exponent == -0.5: + return args.format('sdg {0};\n', qubits[0]) + elif self._exponent == 0.25: + return args.format('t {0};\n', qubits[0]) + elif self._exponent == -0.25: + return args.format('tdg {0};\n', qubits[0]) return args.format('rz({0:half_turns}) {1};\n', self._exponent, qubits[0]) def _quil_( @@ -756,6 +776,10 @@ def __str__(self) -> str: def __repr__(self) -> str: return f'cirq.Rz(rads={proper_repr(self._rads)})' + def _qasm_(self, args: 'cirq.QasmArgs', qubits: Tuple['cirq.Qid', ...]) -> Optional[str]: + args.validate_version('2.0') + return args.format('rz({0:half_turns}) {1};\n', self._exponent, qubits[0]) + def _json_dict_(self) -> Dict[str, Any]: return {'rads': self._rads} @@ -860,7 +884,9 @@ def _circuit_diagram_info_( def _qasm_(self, args: 'cirq.QasmArgs', qubits: Tuple['cirq.Qid', ...]) -> Optional[str]: args.validate_version('2.0') - if self._exponent == 1: + if self._exponent == 0: + return args.format('id {0};\n', qubits[0]) + elif self._exponent == 1 and self._global_shift == 0: return args.format('h {0};\n', qubits[0]) return args.format( diff --git a/cirq-core/cirq/ops/controlled_operation.py b/cirq-core/cirq/ops/controlled_operation.py index ac4a15691a7..33a78df0150 100644 --- a/cirq-core/cirq/ops/controlled_operation.py +++ b/cirq-core/cirq/ops/controlled_operation.py @@ -30,7 +30,14 @@ import numpy as np from cirq import protocols, qis, value -from cirq.ops import raw_types, gate_operation, controlled_gate, matrix_gates +from cirq.ops import ( + controlled_gate, + common_gates, + eigen_gate, + gate_operation, + matrix_gates, + raw_types, +) from cirq.type_workarounds import NotImplementedType if TYPE_CHECKING: @@ -186,6 +193,32 @@ def _apply_unitary_(self, args: 'protocols.ApplyUnitaryArgs') -> np.ndarray: def _has_unitary_(self) -> bool: return protocols.has_unitary(self.sub_operation) + def _qasm_(self, args: 'cirq.QasmArgs') -> Optional[str]: + if ( + hasattr(self._sub_operation, "gate") + and len(self._controls) == 1 + and self._control_values == ((1,),) + ): + gate = self.sub_operation.gate + if ( + isinstance(gate, eigen_gate.EigenGate) + and gate.exponent == 1 + and gate.global_shift == 0 + ): + instr = None + if isinstance(gate, common_gates.XPowGate): + instr = 'cx {0},{1};\n' + elif isinstance(gate, common_gates.YPowGate): + instr = 'cy {0},{1};\n' + elif isinstance(gate, common_gates.ZPowGate): + instr = 'cz {0},{1};\n' + elif isinstance(gate, common_gates.HPowGate): + instr = 'ch {0},{1};\n' + if instr: + return args.format(instr, self._controls[0], self.sub_operation.qubits[0]) + # Fallback to decompose. + return None + def _extend_matrix(self, sub_matrix: np.ndarray) -> np.ndarray: qid_shape = protocols.qid_shape(self) sub_n = len(qid_shape) - len(self.controls) From 17df95c183126f10f6efbdf9dd079ac150daeeed Mon Sep 17 00:00:00 2001 From: Dave Bacon Date: Fri, 13 May 2022 20:55:35 +0000 Subject: [PATCH 2/3] fix identity --- cirq-core/cirq/circuits/qasm_output_test.py | 6 +++--- cirq-core/cirq/ops/common_gates.py | 7 +------ 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/cirq-core/cirq/circuits/qasm_output_test.py b/cirq-core/cirq/circuits/qasm_output_test.py index 09147e248bf..fcfe0580e02 100644 --- a/cirq-core/cirq/circuits/qasm_output_test.py +++ b/cirq-core/cirq/circuits/qasm_output_test.py @@ -379,13 +379,13 @@ def filter_unpredictable_numbers(text): id q[0]; z q[0]; rz(pi*0.625) q[0]; -id q[0]; +rz(0) q[0]; y q[0]; ry(pi*0.375) q[0]; -id q[0]; +ry(0) q[0]; x q[0]; rx(pi*0.875) q[0]; -id q[0]; +rx(0) q[0]; h q[0]; id q[0]; sx q[0]; diff --git a/cirq-core/cirq/ops/common_gates.py b/cirq-core/cirq/ops/common_gates.py index 3b8a840995e..29e63c586ea 100644 --- a/cirq-core/cirq/ops/common_gates.py +++ b/cirq-core/cirq/ops/common_gates.py @@ -219,8 +219,6 @@ def _circuit_diagram_info_( def _qasm_(self, args: 'cirq.QasmArgs', qubits: Tuple['cirq.Qid', ...]) -> Optional[str]: args.validate_version('2.0') - if self._exponent == 0: - return args.format('id {0};\n', qubits[0]) if self._global_shift != -0.5: if self._exponent == 1: return args.format('x {0};\n', qubits[0]) @@ -409,8 +407,6 @@ def _qasm_(self, args: 'cirq.QasmArgs', qubits: Tuple['cirq.Qid', ...]) -> Optio args.validate_version('2.0') if self._exponent == 1 and self.global_shift != -0.5: return args.format('y {0};\n', qubits[0]) - elif self._exponent == 0: - return args.format('id {0};\n', qubits[0]) return args.format('ry({0:half_turns}) {1};\n', self._exponent, qubits[0]) @@ -667,8 +663,7 @@ def _circuit_diagram_info_( def _qasm_(self, args: 'cirq.QasmArgs', qubits: Tuple['cirq.Qid', ...]) -> Optional[str]: args.validate_version('2.0') - if self._exponent == 0: - return args.format('id {0};\n', qubits[0]) + if self.global_shift != -0.5: # Not an Rz gate. if self._exponent == 1: From b163f36ea525ba2a0ae40b5fc75f07d845a2dd89 Mon Sep 17 00:00:00 2001 From: Dave Bacon Date: Fri, 13 May 2022 20:59:51 +0000 Subject: [PATCH 3/3] update conditions --- cirq-core/cirq/ops/common_gates.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/cirq-core/cirq/ops/common_gates.py b/cirq-core/cirq/ops/common_gates.py index 29e63c586ea..813d9e271af 100644 --- a/cirq-core/cirq/ops/common_gates.py +++ b/cirq-core/cirq/ops/common_gates.py @@ -219,7 +219,7 @@ def _circuit_diagram_info_( def _qasm_(self, args: 'cirq.QasmArgs', qubits: Tuple['cirq.Qid', ...]) -> Optional[str]: args.validate_version('2.0') - if self._global_shift != -0.5: + if self._global_shift == 0: if self._exponent == 1: return args.format('x {0};\n', qubits[0]) elif self._exponent == 0.5: @@ -664,8 +664,7 @@ def _circuit_diagram_info_( def _qasm_(self, args: 'cirq.QasmArgs', qubits: Tuple['cirq.Qid', ...]) -> Optional[str]: args.validate_version('2.0') - if self.global_shift != -0.5: - # Not an Rz gate. + if self.global_shift == 0: if self._exponent == 1: return args.format('z {0};\n', qubits[0]) elif self._exponent == 0.5: