From b76bdba7acdb941fd43062fe762f6a3aceeb7dd0 Mon Sep 17 00:00:00 2001 From: Jake Lishman Date: Thu, 14 Dec 2023 17:18:49 +0000 Subject: [PATCH] Restrict version-number output of OpenQASM 3 exporter The exporter previously claimed to simply be `OPENQASM 3;`, though the OpenQASM project has made it clear that they may include minor versions of the language in the future. This updates the exporter to claim to be version `3.0`, so that it does not falsely imply that it matches syntax of a later version of the language specification. --- qiskit/qasm3/exporter.py | 2 +- .../qasm3-minor-version-2ae00ba8f72a1a53.yaml | 6 + test/python/qasm3/test_export.py | 118 +++++++++--------- 3 files changed, 66 insertions(+), 60 deletions(-) create mode 100644 releasenotes/notes/qasm3-minor-version-2ae00ba8f72a1a53.yaml diff --git a/qiskit/qasm3/exporter.py b/qiskit/qasm3/exporter.py index 44e46f9bea80..c1398aff747d 100644 --- a/qiskit/qasm3/exporter.py +++ b/qiskit/qasm3/exporter.py @@ -435,7 +435,7 @@ def _lookup_variable(self, variable) -> ast.Identifier: def build_header(self): """Builds a Header""" - version = ast.Version("3") + version = ast.Version("3.0") includes = self.build_includes() return ast.Header(version, includes) diff --git a/releasenotes/notes/qasm3-minor-version-2ae00ba8f72a1a53.yaml b/releasenotes/notes/qasm3-minor-version-2ae00ba8f72a1a53.yaml new file mode 100644 index 000000000000..138066932e0c --- /dev/null +++ b/releasenotes/notes/qasm3-minor-version-2ae00ba8f72a1a53.yaml @@ -0,0 +1,6 @@ +--- +fixes: + - | + The OpenQASM 3 exporters :func:`.qasm3.dump` and :func:`~.qasm3.dumps` will now correctly output + files claiming to be version ``3.0`` rather than the unqualified ``3``, since the OpenQASM 3 + project has now standardised on versioning. diff --git a/test/python/qasm3/test_export.py b/test/python/qasm3/test_export.py index 0f64fa6ba12b..3637923af6c0 100644 --- a/test/python/qasm3/test_export.py +++ b/test/python/qasm3/test_export.py @@ -45,7 +45,7 @@ def setUp(self): self.circuit.u(2 * pi, 3 * pi, -5 * pi, 0) self.expected_qasm = "\n".join( [ - "OPENQASM 3;", + "OPENQASM 3.0;", 'include "stdgates.inc";', "qubit[2] q;", "U(2*pi, 3*pi, -5*pi) q[0];", @@ -113,7 +113,7 @@ def test_regs_conds_qasm(self): qc.z(qr1[0]).c_if(cr, 2) expected_qasm = "\n".join( [ - "OPENQASM 3;", + "OPENQASM 3.0;", 'include "stdgates.inc";', "bit[3] cr;", "qubit[1] qr1;", @@ -145,7 +145,7 @@ def test_registers_as_aliases(self): qc = QuantumCircuit(qubits, first_four, last_five, alternate, sporadic) expected_qasm = "\n".join( [ - "OPENQASM 3;", + "OPENQASM 3.0;", 'include "stdgates.inc";', "qubit _qubit0;", "qubit _qubit1;", @@ -186,7 +186,7 @@ def test_composite_circuit(self): expected_qasm = "\n".join( [ - "OPENQASM 3;", + "OPENQASM 3.0;", 'include "stdgates.inc";', "gate composite_circ _gate_q_0, _gate_q_1 {", " h _gate_q_0;", @@ -226,7 +226,7 @@ def test_custom_gate(self): expected_qasm = "\n".join( [ - "OPENQASM 3;", + "OPENQASM 3.0;", 'include "stdgates.inc";', "gate composite_circ _gate_q_0, _gate_q_1 {", " h _gate_q_0;", @@ -267,7 +267,7 @@ def test_same_composite_circuits(self): expected_qasm = "\n".join( [ - "OPENQASM 3;", + "OPENQASM 3.0;", 'include "stdgates.inc";', "gate composite_circ _gate_q_0, _gate_q_1 {", " h _gate_q_0;", @@ -312,7 +312,7 @@ def test_composite_circuits_with_same_name(self): my_gate_inst3_id = id(circuit.data[-1].operation) expected_qasm = "\n".join( [ - "OPENQASM 3;", + "OPENQASM 3.0;", 'include "stdgates.inc";', "gate my_gate _gate_q_0 {", " h _gate_q_0;", @@ -338,7 +338,7 @@ def test_pi_disable_constants_false(self): circuit.u(2 * pi, 3 * pi, -5 * pi, 0) expected_qasm = "\n".join( [ - "OPENQASM 3;", + "OPENQASM 3.0;", 'include "stdgates.inc";', "qubit[2] q;", "U(2*pi, 3*pi, -5*pi) q[0];", @@ -353,7 +353,7 @@ def test_pi_disable_constants_true(self): circuit.u(2 * pi, 3 * pi, -5 * pi, 0) expected_qasm = "\n".join( [ - "OPENQASM 3;", + "OPENQASM 3.0;", 'include "stdgates.inc";', "qubit[2] q;", "U(6.283185307179586, 9.42477796076938, -15.707963267948966) q[0];", @@ -373,7 +373,7 @@ def test_custom_gate_with_unbound_parameter(self): expected_qasm = "\n".join( [ - "OPENQASM 3;", + "OPENQASM 3.0;", 'include "stdgates.inc";', "input float[64] a;", "gate custom(a) _gate_q_0 {", @@ -400,7 +400,7 @@ def test_custom_gate_with_bound_parameter(self): expected_qasm = "\n".join( [ - "OPENQASM 3;", + "OPENQASM 3.0;", 'include "stdgates.inc";', "gate custom _gate_q_0 {", " rx(0.5) _gate_q_0;", @@ -431,7 +431,7 @@ def test_custom_gate_with_params_bound_main_call(self): expected_qasm = "\n".join( [ - "OPENQASM 3;", + "OPENQASM 3.0;", 'include "stdgates.inc";', "gate custom(_gate_p_0, _gate_p_0) _gate_q_0, _gate_q_1 {", " rz(pi) _gate_q_0;", @@ -461,7 +461,7 @@ def test_reused_custom_parameter(self): expected_qasm = "\n".join( [ - "OPENQASM 3;", + "OPENQASM 3.0;", 'include "stdgates.inc";', f"gate {circuit_name_0} _gate_q_0 {{", " rx(0.5) _gate_q_0;", @@ -484,7 +484,7 @@ def test_unbound_circuit(self): qc.rz(theta, 0) expected_qasm = "\n".join( [ - "OPENQASM 3;", + "OPENQASM 3.0;", 'include "stdgates.inc";', "input float[64] θ;", "qubit[1] q;", @@ -505,7 +505,7 @@ def test_unknown_parameterized_gate_called_multiple_times(self): expected_qasm = "\n".join( [ - "OPENQASM 3;", + "OPENQASM 3.0;", "input float[64] x;", "input float[64] y;", "gate rzx(x) _gate_q_0, _gate_q_1 {", @@ -534,7 +534,7 @@ def test_gate_qasm_with_ctrl_state(self): expected_qasm = "\n".join( [ - "OPENQASM 3;", + "OPENQASM 3.0;", 'include "stdgates.inc";', "gate ch_o0 _gate_q_0, _gate_q_1 {", " x _gate_q_0;", @@ -559,7 +559,7 @@ def test_custom_gate_collision_with_stdlib(self): custom_gate_id = id(qc.data[-1].operation) expected_qasm = "\n".join( [ - "OPENQASM 3;", + "OPENQASM 3.0;", 'include "stdgates.inc";', f"gate cx_{custom_gate_id} _gate_q_0, _gate_q_1 {{", " cx _gate_q_0, _gate_q_1;", @@ -581,7 +581,7 @@ def test_no_include(self): circuit.cx(0, 1) expected_qasm = "\n".join( [ - "OPENQASM 3;", + "OPENQASM 3.0;", "gate u3(_gate_p_0, _gate_p_1, _gate_p_2) _gate_q_0 {", " U(0, 0, pi/2) _gate_q_0;", "}", @@ -635,7 +635,7 @@ def test_teleportation(self): transpiled = transpile(qc, initial_layout=[0, 1, 2]) expected_qasm = "\n".join( [ - "OPENQASM 3;", + "OPENQASM 3.0;", "gate u3(_gate_p_0, _gate_p_1, _gate_p_2) _gate_q_0 {", " U(pi/2, 0, pi) _gate_q_0;", "}", @@ -696,7 +696,7 @@ def test_basis_gates(self): transpiled = transpile(qc, initial_layout=[0, 1, 2]) expected_qasm = "\n".join( [ - "OPENQASM 3;", + "OPENQASM 3.0;", "gate u3(_gate_p_0, _gate_p_1, _gate_p_2) _gate_q_0 {", " U(pi/2, 0, pi) _gate_q_0;", "}", @@ -744,7 +744,7 @@ def test_opaque_instruction_in_basis_gates(self): transpiled = transpile(qc, initial_layout=[0]) expected_qasm = "\n".join( [ - "OPENQASM 3;", + "OPENQASM 3.0;", "x $0;", "my_gate $0;", "", @@ -764,7 +764,7 @@ def test_reset_statement(self): expected_qasm = "\n".join( [ - "OPENQASM 3;", + "OPENQASM 3.0;", "qubit[2] qr;", "reset qr[0];", "reset qr[0];", @@ -783,7 +783,7 @@ def test_delay_statement(self): expected_qasm = "\n".join( [ - "OPENQASM 3;", + "OPENQASM 3.0;", "qubit[2] qr;", "delay[100ms] qr[0];", "delay[2000ns] qr[1];", @@ -805,7 +805,7 @@ def test_loose_qubits(self): expected_qasm = "\n".join( [ - "OPENQASM 3;", + "OPENQASM 3.0;", 'include "stdgates.inc";', "bit[2] cr;", "qubit _qubit0;", @@ -837,7 +837,7 @@ def test_loose_clbits(self): expected_qasm = "\n".join( [ - "OPENQASM 3;", + "OPENQASM 3.0;", 'include "stdgates.inc";', "bit _bit0;", "bit _bit3;", @@ -876,7 +876,7 @@ def test_classical_register_aliasing(self): expected_qasm = "\n".join( [ - "OPENQASM 3;", + "OPENQASM 3.0;", 'include "stdgates.inc";', "bit _bit0;", "bit _bit1;", @@ -921,7 +921,7 @@ def test_old_alias_classical_registers_option(self): expected_qasm = "\n".join( [ - "OPENQASM 3;", + "OPENQASM 3.0;", 'include "stdgates.inc";', "bit _bit0;", "bit _bit1;", @@ -962,7 +962,7 @@ def test_simple_for_loop(self): expected_qasm = "\n".join( [ - "OPENQASM 3;", + "OPENQASM 3.0;", 'include "stdgates.inc";', f"qubit[2] {qr_name};", f"for {parameter.name} in {{0, 3, 4}} {{", @@ -1001,7 +1001,7 @@ def test_nested_for_loop(self): expected_qasm = "\n".join( [ - "OPENQASM 3;", + "OPENQASM 3.0;", 'include "stdgates.inc";', f"qubit[2] {qr_name};", f"for {outer_parameter.name} in [0:3] {{", @@ -1048,7 +1048,7 @@ def test_regular_parameter_in_nested_for_loop(self): expected_qasm = "\n".join( [ - "OPENQASM 3;", + "OPENQASM 3.0;", 'include "stdgates.inc";', # This next line will be missing until gh-7280 is fixed. f"input float[64] {regular_parameter.name};", @@ -1081,7 +1081,7 @@ def test_for_loop_with_no_parameter(self): expected_qasm = "\n".join( [ - "OPENQASM 3;", + "OPENQASM 3.0;", 'include "stdgates.inc";', f"qubit[2] {qr_name};", "for _ in {0, 3, 4} {", @@ -1107,7 +1107,7 @@ def test_simple_while_loop(self): expected_qasm = "\n".join( [ - "OPENQASM 3;", + "OPENQASM 3.0;", 'include "stdgates.inc";', "bit[2] cr;", "qubit[2] qr;", @@ -1144,7 +1144,7 @@ def test_nested_while_loop(self): expected_qasm = "\n".join( [ - "OPENQASM 3;", + "OPENQASM 3.0;", 'include "stdgates.inc";', "bit[2] cr;", "qubit[2] qr;", @@ -1178,7 +1178,7 @@ def test_simple_if_statement(self): expected_qasm = "\n".join( [ - "OPENQASM 3;", + "OPENQASM 3.0;", 'include "stdgates.inc";', "bit[2] cr;", "qubit[2] qr;", @@ -1206,7 +1206,7 @@ def test_simple_if_else_statement(self): expected_qasm = "\n".join( [ - "OPENQASM 3;", + "OPENQASM 3.0;", 'include "stdgates.inc";', "bit[2] cr;", "qubit[2] qr;", @@ -1245,7 +1245,7 @@ def test_nested_if_else_statement(self): expected_qasm = "\n".join( [ - "OPENQASM 3;", + "OPENQASM 3.0;", 'include "stdgates.inc";', "bit[2] cr;", "qubit[2] qr;", @@ -1292,7 +1292,7 @@ def test_chain_else_if(self): expected_qasm = "\n".join( [ - "OPENQASM 3;", + "OPENQASM 3.0;", 'include "stdgates.inc";', "bit[2] cr;", "qubit[2] qr;", @@ -1349,7 +1349,7 @@ def test_chain_else_if_does_not_chain_if_extra_instructions(self): expected_qasm = "\n".join( [ - "OPENQASM 3;", + "OPENQASM 3.0;", 'include "stdgates.inc";', "bit[2] cr;", "qubit[2] qr;", @@ -1401,7 +1401,7 @@ def test_custom_gate_used_in_loop_scope(self): expected_qasm = "\n".join( [ - "OPENQASM 3;", + "OPENQASM 3.0;", 'include "stdgates.inc";', "gate custom _gate_q_0 {", " rx(0.5) _gate_q_0;", @@ -1444,7 +1444,7 @@ def test_parameter_expression_after_naming_escape(self): expected_qasm = "\n".join( [ - "OPENQASM 3;", + "OPENQASM 3.0;", 'include "stdgates.inc";', "input float[64] _measure;", "qubit[1] q;", @@ -1508,7 +1508,7 @@ def test_expr_condition(self): qc.while_loop(expr.equal(cr, 3), while_body, [0], []) expected = """\ -OPENQASM 3; +OPENQASM 3.0; include "stdgates.inc"; bit _bit0; bit[2] cr; @@ -1543,7 +1543,7 @@ def test_expr_nested_condition(self): qc.while_loop(expr.equal(cr, 3), outer_while_body, [0], cr) expected = """\ -OPENQASM 3; +OPENQASM 3.0; include "stdgates.inc"; bit _bit0; bit _bit1; @@ -1580,7 +1580,7 @@ def test_expr_associativity_left(self): # Note that bitwise operations have lower priority than `==` so there's extra parentheses. # All these operators are left-associative in OQ3. expected = """\ -OPENQASM 3; +OPENQASM 3.0; include "stdgates.inc"; bit[3] cr1; bit[3] cr2; @@ -1618,7 +1618,7 @@ def test_expr_associativity_right(self): # parsed correctly. Mathematically, they're all actually associative in general, so the # order doesn't _technically_ matter. expected = """\ -OPENQASM 3; +OPENQASM 3.0; include "stdgates.inc"; bit[3] cr1; bit[3] cr2; @@ -1645,7 +1645,7 @@ def test_expr_binding_unary(self): qc.if_test(expr.logic_not(expr.logic_not(cr[0])), body.copy(), [], []) expected = """\ -OPENQASM 3; +OPENQASM 3.0; include "stdgates.inc"; bit[2] cr; if (~~cr == 3) { @@ -1704,7 +1704,7 @@ def test_expr_precedence(self): qc.if_test(logics, body.copy(), [], []) expected = """\ -OPENQASM 3; +OPENQASM 3.0; include "stdgates.inc"; bit[2] cr; if (!((((cr | cr) ^ (cr | cr)) & ((cr | cr) ^ (cr | cr)))\ @@ -1731,7 +1731,7 @@ def test_no_unnecessary_cast(self): qc.if_test(expr.equal(cr, 1), QuantumCircuit(), [], []) expected = """\ -OPENQASM 3; +OPENQASM 3.0; include "stdgates.inc"; bit[8] cr; if (cr == 1) { @@ -1770,7 +1770,7 @@ def test_basis_gates(self): expected_qasm = "\n".join( [ - "OPENQASM 3;", + "OPENQASM 3.0;", f"gate u3_{id(u3_1)}(_gate_p_0, _gate_p_1, _gate_p_2) _gate_q_0 {{", " U(pi/2, 0, pi) _gate_q_0;", "}", @@ -1837,7 +1837,7 @@ def test_teleportation(self): expected_qasm = "\n".join( [ - "OPENQASM 3;", + "OPENQASM 3.0;", f"gate u3_{id(u3_1)}(_gate_p_0, _gate_p_1, _gate_p_2) _gate_q_0 {{", " U(pi/2, 0, pi) _gate_q_0;", "}", @@ -1905,7 +1905,7 @@ def test_custom_gate_with_params_bound_main_call(self): expected_qasm = "\n".join( [ - "OPENQASM 3;", + "OPENQASM 3.0;", 'include "stdgates.inc";', f"gate custom_{custom_id}(_gate_p_0, _gate_p_1) _gate_q_0, _gate_q_1 {{", " rz(pi) _gate_q_0;", @@ -1939,7 +1939,7 @@ def test_no_include(self): u3_3 = u2_1.definition.data[0].operation expected_qasm = "\n".join( [ - "OPENQASM 3;", + "OPENQASM 3.0;", f"gate u3_{id(u3_1)}(_gate_p_0, _gate_p_1, _gate_p_2) _gate_q_0 {{", " U(0, 0, pi/2) _gate_q_0;", "}", @@ -2006,7 +2006,7 @@ def test_unusual_conditions(self): qc.append(barrier, [0, 1], []) expected = """ -OPENQASM 3; +OPENQASM 3.0; include "stdgates.inc"; bit[2] c; qubit[3] q; @@ -2064,7 +2064,7 @@ def test_switch_clbit(self): test = dumps(circuit, experimental=ExperimentalFeatures.SWITCH_CASE_V1) expected = """\ -OPENQASM 3; +OPENQASM 3.0; include "stdgates.inc"; bit _bit0; int switch_dummy; @@ -2099,7 +2099,7 @@ def test_switch_register(self): test = dumps(circuit, experimental=ExperimentalFeatures.SWITCH_CASE_V1) expected = """\ -OPENQASM 3; +OPENQASM 3.0; include "stdgates.inc"; bit[2] c; int switch_dummy; @@ -2138,7 +2138,7 @@ def test_switch_with_default(self): test = dumps(circuit, experimental=ExperimentalFeatures.SWITCH_CASE_V1) expected = """\ -OPENQASM 3; +OPENQASM 3.0; include "stdgates.inc"; bit[2] c; int switch_dummy; @@ -2176,7 +2176,7 @@ def test_switch_multiple_cases_to_same_block(self): test = dumps(circuit, experimental=ExperimentalFeatures.SWITCH_CASE_V1) expected = """\ -OPENQASM 3; +OPENQASM 3.0; include "stdgates.inc"; bit[2] c; int switch_dummy; @@ -2212,7 +2212,7 @@ def test_multiple_switches_dont_clash_on_dummy(self): test = dumps(circuit, experimental=ExperimentalFeatures.SWITCH_CASE_V1) expected = """\ -OPENQASM 3; +OPENQASM 3.0; include "stdgates.inc"; bit[2] switch_dummy; int switch_dummy__generated0; @@ -2265,7 +2265,7 @@ def test_switch_nested_in_if(self): test = dumps(circuit, experimental=ExperimentalFeatures.SWITCH_CASE_V1) expected = """\ -OPENQASM 3; +OPENQASM 3.0; include "stdgates.inc"; bit[2] c; int switch_dummy; @@ -2314,7 +2314,7 @@ def test_expr_target(self): qc.switch(expr.bit_and(cr, 3), [(3, case1)], [0], []) expected = """\ -OPENQASM 3; +OPENQASM 3.0; include "stdgates.inc"; bit _bit0; bit[2] cr;