Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve the ergonomics of the TranspileLayout class #10835

Merged
merged 14 commits into from
Oct 3, 2023
Merged
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
9 changes: 6 additions & 3 deletions qiskit/passmanager/passrunner.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,11 +88,14 @@ def _to_passmanager_ir(self, in_program):
pass

@abstractmethod
def _to_target(self, passmanager_ir):
def _to_target(self, passmanager_ir, in_program):
"""Convert pass manager IR into output program.

Args:
passmanager_ir: Pass manager IR after optimization.
in_program: The input program, this can be used if you need
any metadata about the original input for the output. It
should not be mutated.

Returns:
Output program.
Expand Down Expand Up @@ -229,15 +232,15 @@ def run(
self.metadata = metadata

passmanager_ir = self._to_passmanager_ir(in_program)
del in_program

for controller in self.working_list:
passmanager_ir = self._run_pass_generic(
pass_sequence=controller,
passmanager_ir=passmanager_ir,
options=self.passmanager_options,
)
out_program = self._to_target(passmanager_ir)
out_program = self._to_target(passmanager_ir, in_program)
del in_program

if not isinstance(out_program, self.OUT_PROGRAM_TYPE):
raise TypeError(
Expand Down
24 changes: 23 additions & 1 deletion qiskit/qpy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,9 @@
==========

Version 10 adds support for symengine-native serialization for objects of type
:class:`~.ParameterExpression` as well as symbolic expressions in Pulse schedule blocks.
:class:`~.ParameterExpression` as well as symbolic expressions in Pulse schedule blocks. Version
10 also adds support for new fields in the :class:`~.TranspileLayout` class added in the Qiskit
0.45.0 release.

The symbolic_encoding field is added to the file header, and a new encoding type char
is introduced, mapped to each symbolic library as follows: ``p`` refers to sympy
Expand All @@ -171,6 +173,26 @@
char symbolic_encoding;
}

LAYOUT
------

The ``LAYOUT`` struct is updated to have an additional ``input_qubit_count`` field.
With version 10 the ``LAYOUT`` struct is now:

.. code-block:: c

struct {
char exists;
int32_t initial_layout_size;
int32_t input_mapping_size;
int32_t final_layout_size;
uint32_t extra_registers;
int32_t input_qubit_count;
}

The rest of the layout data after the ``LAYOUT`` struct is represented as in previous versions. If
``input qubit_count`` is < 0 that indicates that both ``_input_qubit_count``
and ``_output_qubit_list`` in the :class:`~.TranspileLayout` object are ``None``.

.. _qpy_version_9:

Expand Down
29 changes: 26 additions & 3 deletions qiskit/qpy/binary_io/circuits.py
Original file line number Diff line number Diff line change
Expand Up @@ -831,7 +831,7 @@ def _write_registers(file_obj, in_circ_regs, full_bits):
def _write_layout(file_obj, circuit):
if circuit.layout is None:
# Write a null header if there is no layout present
file_obj.write(struct.pack(formats.LAYOUT_PACK, False, -1, -1, -1, 0))
file_obj.write(struct.pack(formats.LAYOUT_V2_PACK, False, -1, -1, -1, 0, 0))
return
initial_size = -1
input_qubit_mapping = {}
Expand Down Expand Up @@ -874,14 +874,18 @@ def _write_layout(file_obj, circuit):
virtual_bit = final_layout_physical[i]
final_layout_array.append(circuit.find_bit(virtual_bit).index)

input_qubit_count = circuit._layout._input_qubit_count
if input_qubit_count is None:
input_qubit_count = -1
file_obj.write(
struct.pack(
formats.LAYOUT_PACK,
formats.LAYOUT_V2_PACK,
True,
initial_size,
input_qubit_size,
final_layout_size,
len(extra_registers),
input_qubit_count,
)
)
_write_registers(
Expand Down Expand Up @@ -910,6 +914,10 @@ def _read_layout(file_obj, circuit):
)
if not header.exists:
return
_read_common_layout(file_obj, header, circuit)


def _read_common_layout(file_obj, header, circuit):
registers = {
name: QuantumRegister(len(v[1]), name)
for name, v in _read_registers_v4(file_obj, header.extra_registers)["q"].items()
Expand Down Expand Up @@ -958,6 +966,18 @@ def _read_layout(file_obj, circuit):
circuit._layout = TranspileLayout(initial_layout, input_qubit_mapping, final_layout)


def _read_layout_v2(file_obj, circuit):
header = formats.LAYOUT_V2._make(
struct.unpack(formats.LAYOUT_V2_PACK, file_obj.read(formats.LAYOUT_V2_SIZE))
)
if not header.exists:
return
_read_common_layout(file_obj, header, circuit)
if header.input_qubit_count >= 0:
circuit._layout._input_qubit_count = header.input_qubit_count
circuit._layout._output_qubit_list = circuit.qubits


def write_circuit(file_obj, circuit, metadata_serializer=None, use_symengine=False):
"""Write a single QuantumCircuit object in the file like object.

Expand Down Expand Up @@ -1161,5 +1181,8 @@ def read_circuit(file_obj, version, metadata_deserializer=None, use_symengine=Fa
UserWarning,
)
if version >= 8:
_read_layout(file_obj, circ)
if version >= 10:
_read_layout_v2(file_obj, circ)
else:
_read_layout(file_obj, circ)
return circ
15 changes: 15 additions & 0 deletions qiskit/qpy/formats.py
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,21 @@
MAP_ITEM_PACK = "!H1cH"
MAP_ITEM_SIZE = struct.calcsize(MAP_ITEM_PACK)

LAYOUT_V2 = namedtuple(
"LAYOUT",
[
"exists",
"initial_layout_size",
"input_mapping_size",
"final_layout_size",
"extra_registers",
"input_qubit_count",
],
)
LAYOUT_V2_PACK = "!?iiiIi"
Comment on lines +281 to +292
Copy link
Member

Choose a reason for hiding this comment

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

Technically this struct format string doesn't match the definition of the C struct up top - exists is a char in the C definition and _Bool here - but in practice it would be pretty wild to have a system where _Bool wasn't ABI compatible with char.

Copy link
Member Author

Choose a reason for hiding this comment

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

I think I probably just copy and pasted both from V1 as this doesn't change between V1 and V2. IIRC we added this discrepancy between docs and the struct format string as part of your review in #10148: #10148 (comment) . I can update it to be _Bool in the docs if you prefer though.

Copy link
Member

Choose a reason for hiding this comment

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

Nah, it's fine, let's just leave it.

LAYOUT_V2_SIZE = struct.calcsize(LAYOUT_V2_PACK)


LAYOUT = namedtuple(
"LAYOUT",
["exists", "initial_layout_size", "input_mapping_size", "final_layout_size", "extra_registers"],
Expand Down
2 changes: 2 additions & 0 deletions qiskit/transpiler/basepasses.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ def __call__(self, circuit, property_set=None):
initial_layout=self.property_set["layout"],
input_qubit_mapping=self.property_set["original_qubit_indices"],
final_layout=self.property_set["final_layout"],
_input_qubit_count=len(circuit.qubits),
_output_qubit_list=result_circuit.qubits,
)
if self.property_set["clbit_write_latency"] is not None:
result_circuit._clbit_write_latency = self.property_set["clbit_write_latency"]
Expand Down
Loading
Loading