From e9f2f7cbb02d8c7a24fa5864edf4440860f71a54 Mon Sep 17 00:00:00 2001 From: Jake Lishman Date: Fri, 13 Sep 2024 11:54:01 +0100 Subject: [PATCH] Re-allow non-standard includes in OpenQASM 3 exporter Swapping the behaviour of the exporter to error on unknown includes was a mistake, and a breaking change from previous versions of the OpenQASM 3 exporter, since the `basis_gates` argument can be used to emulate the expected behaviour of this option, even though the previously documented behaviour was never actually fulfilled. This restores the previous non-erroring behaviour, and corrects the documentation to note how the ``includes`` argument should be used in the near term. --- qiskit/qasm3/exporter.py | 19 +++++++++++++------ .../qasm3-includes-ceb56f49b8c190ff.yaml | 11 +++++++++++ test/python/qasm3/test_export.py | 19 +++++++++++++++++++ 3 files changed, 43 insertions(+), 6 deletions(-) create mode 100644 releasenotes/notes/qasm3-includes-ceb56f49b8c190ff.yaml diff --git a/qiskit/qasm3/exporter.py b/qiskit/qasm3/exporter.py index 561e59a72242..c76e296d6bab 100644 --- a/qiskit/qasm3/exporter.py +++ b/qiskit/qasm3/exporter.py @@ -140,9 +140,16 @@ def __init__( ): """ Args: - includes: the filenames that should be emitted as includes. These files will be parsed - for gates, and any objects dumped from this exporter will use those definitions - where possible. + includes: the filenames that should be emitted as includes. + + .. note:: + + At present, only the standard-library file ``stdgates.inc`` is properly + understood by the exporter, in the sense that it knows the gates it defines. + You can specify other includes, but you will need to pass the names of the gates + they define in the ``basis_gates`` argument to avoid the exporter outputting a + separate ``gate`` definition. + basis_gates: the basic defined gate set of the backend. disable_constants: if ``True``, always emit floating-point constants for numeric parameter values. If ``False`` (the default), then values close to multiples of @@ -675,9 +682,9 @@ def build_program(self): def build_includes(self): """Builds a list of included files.""" for filename in self.includes: - if (definitions := _KNOWN_INCLUDES.get(filename)) is None: - raise QASM3ExporterError(f"Unknown OpenQASM 3 include file: '{filename}'") - for name, gate in definitions.items(): + # Note: unknown include files have a corresponding `include` statement generated, but do + # not actually define any gates; we rely on the user to pass those in `basis_gates`. + for name, gate in _KNOWN_INCLUDES.get(filename, {}).items(): self.symbols.register_gate_without_definition(name, gate) yield ast.Include(filename) diff --git a/releasenotes/notes/qasm3-includes-ceb56f49b8c190ff.yaml b/releasenotes/notes/qasm3-includes-ceb56f49b8c190ff.yaml new file mode 100644 index 000000000000..25270fcb6359 --- /dev/null +++ b/releasenotes/notes/qasm3-includes-ceb56f49b8c190ff.yaml @@ -0,0 +1,11 @@ +--- +fixes: + - | + The OpenQASM 3 exporter has restored its behavior of accepting non-standard-library include + files in the ``includes`` argument to :func:`.qasm3.dump`, :func:`~.qasm3.dumps`, and + :class:`~.qasm3.Exporter`. These will insert a suitable ``include`` statement into the output + as before, and the exporter remains unaware of the intended gates in that include file; you + should pass the gates you expect it to define in the ``basis_gates`` argument to the same functions. + + We expect to improve the export mechanism against non-standard include files in a future release + of Qiskit. diff --git a/test/python/qasm3/test_export.py b/test/python/qasm3/test_export.py index 868846925753..eefc0a2cc53c 100644 --- a/test/python/qasm3/test_export.py +++ b/test/python/qasm3/test_export.py @@ -693,6 +693,25 @@ def test_no_include(self): """ self.assertEqual(Exporter(includes=[]).dumps(circuit), expected_qasm) + def test_include_unknown_file(self): + """Test export can target a non-standard include without complaints.""" + qc = QuantumCircuit(2, 2) + qc.h(0) + qc.cx(0, 1) + qc.measure([0, 1], [0, 1]) + + expected = """\ +OPENQASM 3.0; +include "mygates.inc"; +bit[2] c; +qubit[2] q; +h q[0]; +cx q[0], q[1]; +c[0] = measure q[0]; +c[1] = measure q[1]; +""" + self.assertEqual(dumps(qc, includes=["mygates.inc"], basis_gates=["h", "cx"]), expected) + def test_teleportation(self): """Teleportation with physical qubits""" qc = QuantumCircuit(3, 2)