Skip to content
This repository has been archived by the owner on Jul 24, 2024. It is now read-only.

Copy qpy updates from terra/0.24.2 #676

Merged
merged 5 commits into from
Jul 24, 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
149 changes: 148 additions & 1 deletion qiskit_ibm_provider/qpy/binary_io/circuits.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import uuid
import warnings

from collections import defaultdict
import numpy as np

from qiskit import circuit as circuit_mod
Expand All @@ -34,6 +35,7 @@
from qiskit.extensions import quantum_initializer
from qiskit.quantum_info.operators import SparsePauliOp
from qiskit.synthesis import evolution as evo_synth
from qiskit.transpiler.layout import Layout, TranspileLayout
from .. import common, formats, type_keys
from . import value, schedules

Expand Down Expand Up @@ -298,6 +300,7 @@ def _read_instruction( # type: ignore[no-untyped-def]
"UCRXGate",
"UCRYGate",
"UCRZGate",
"DiagonalGate",
}:
gate = gate_class(params)
else:
Expand Down Expand Up @@ -814,6 +817,148 @@ def _write_registers(file_obj, in_circ_regs, full_bits): # type: ignore[no-unty
return len(in_circ_regs) + len(out_circ_regs)


def _write_layout(file_obj, circuit): # type: ignore[no-untyped-def]
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))
return
initial_size = -1
input_qubit_mapping = {}
initial_layout_array = []
extra_registers = defaultdict(list)
if circuit.layout.initial_layout is not None:
initial_size = len(circuit.layout.initial_layout)
layout_mapping = circuit.layout.initial_layout.get_physical_bits()
for i in range(circuit.num_qubits):
qubit = layout_mapping[i]
input_qubit_mapping[qubit] = i
if qubit._register is not None or qubit._index is not None:
if qubit._register not in circuit.qregs:
extra_registers[qubit._register].append(qubit)
initial_layout_array.append((qubit._index, qubit._register))
else:
initial_layout_array.append((None, None))
input_qubit_size = -1
input_qubit_mapping_array = []
if circuit.layout.input_qubit_mapping is not None:
input_qubit_size = len(circuit.layout.input_qubit_mapping)
input_qubit_mapping_array = [None] * input_qubit_size
layout_mapping = circuit.layout.initial_layout.get_virtual_bits()
for qubit, index in circuit.layout.input_qubit_mapping.items():
if (
getattr(qubit, "_register", None) is not None
and getattr(qubit, "_index", None) is not None
):
if qubit._register not in circuit.qregs:
extra_registers[qubit._register].append(qubit)
input_qubit_mapping_array[index] = layout_mapping[qubit]
else:
input_qubit_mapping_array[index] = layout_mapping[qubit]
final_layout_size = -1
final_layout_array = []
if circuit.layout.final_layout is not None:
final_layout_size = len(circuit.layout.final_layout)
final_layout_physical = circuit.layout.final_layout.get_physical_bits()
for i in range(circuit.num_qubits):
virtual_bit = final_layout_physical[i]
final_layout_array.append(circuit.find_bit(virtual_bit).index)

file_obj.write(
struct.pack(
formats.LAYOUT_PACK,
True,
initial_size,
input_qubit_size,
final_layout_size,
len(extra_registers),
)
)
_write_registers(
file_obj,
list(extra_registers),
[x for bits in extra_registers.values() for x in bits],
)
for index, register in initial_layout_array:
reg_name_bytes = (
None if register is None else register.name.encode(common.ENCODE)
)
file_obj.write(
struct.pack(
formats.INITIAL_LAYOUT_BIT_PACK,
-1 if index is None else index,
-1 if reg_name_bytes is None else len(reg_name_bytes),
)
)
if reg_name_bytes is not None:
file_obj.write(reg_name_bytes)
for i in input_qubit_mapping_array:
file_obj.write(struct.pack("!I", i))
for i in final_layout_array:
file_obj.write(struct.pack("!I", i))


def _read_layout(file_obj, circuit): # type: ignore[no-untyped-def]
header = formats.LAYOUT._make(
struct.unpack(formats.LAYOUT_PACK, file_obj.read(formats.LAYOUT_SIZE))
)
if not header.exists:
return
registers = {
name: QuantumRegister(len(v[1]), name)
for name, v in _read_registers_v4(file_obj, header.extra_registers)["q"].items()
}
initial_layout = None
initial_layout_virtual_bits = []
for _ in range(header.initial_layout_size):
virtual_bit = formats.INITIAL_LAYOUT_BIT._make(
struct.unpack(
formats.INITIAL_LAYOUT_BIT_PACK,
file_obj.read(formats.INITIAL_LAYOUT_BIT_SIZE),
)
)
if virtual_bit.index == -1 and virtual_bit.register_size == -1:
qubit = Qubit()
else:
register_name = file_obj.read(virtual_bit.register_size).decode(
common.ENCODE
)
if register_name in registers:
qubit = registers[register_name][virtual_bit.index]
else:
register = next(
filter(lambda x, name=register_name: x.name == name, circuit.qregs)
)
qubit = register[virtual_bit.index]
initial_layout_virtual_bits.append(qubit)
if initial_layout_virtual_bits:
initial_layout = Layout.from_qubit_list(initial_layout_virtual_bits)
input_qubit_mapping = None
input_qubit_mapping_array = []
for _ in range(header.input_mapping_size):
input_qubit_mapping_array.append(
struct.unpack("!I", file_obj.read(struct.calcsize("!I")))[0]
)
if input_qubit_mapping_array:
input_qubit_mapping = {}
physical_bits = initial_layout.get_physical_bits()
for index, bit in enumerate(input_qubit_mapping_array):
input_qubit_mapping[physical_bits[bit]] = index
final_layout = None
final_layout_array = []
for _ in range(header.final_layout_size):
final_layout_array.append(
struct.unpack("!I", file_obj.read(struct.calcsize("!I")))[0]
)

if final_layout_array:
layout_dict = {
circuit.qubits[bit]: index for index, bit in enumerate(final_layout_array)
}
final_layout = Layout(layout_dict)

circuit._layout = TranspileLayout(initial_layout, input_qubit_mapping, final_layout)


def write_circuit(file_obj, circuit, metadata_serializer=None): # type: ignore[no-untyped-def]
"""Write a single QuantumCircuit object in the file like object.

Expand Down Expand Up @@ -887,6 +1032,7 @@ def write_circuit(file_obj, circuit, metadata_serializer=None): # type: ignore[

# Write calibrations
_write_calibrations(file_obj, circuit.calibrations, metadata_serializer)
_write_layout(file_obj, circuit)


def read_circuit(file_obj, version, metadata_deserializer=None): # type: ignore[no-untyped-def]
Expand Down Expand Up @@ -1020,5 +1166,6 @@ def read_circuit(file_obj, version, metadata_deserializer=None): # type: ignore
f"as they weren't used in the circuit: {circ.name}",
UserWarning,
)

if version >= 8:
_read_layout(file_obj, circ)
return circ
2 changes: 1 addition & 1 deletion qiskit_ibm_provider/qpy/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

from . import formats

QPY_VERSION = 7
QPY_VERSION = 8
ENCODE = "utf8"


Expand Down
17 changes: 17 additions & 0 deletions qiskit_ibm_provider/qpy/formats.py
Original file line number Diff line number Diff line change
Expand Up @@ -275,3 +275,20 @@
MAP_ITEM = namedtuple("MAP_ITEM", ["key_size", "type", "size"])
MAP_ITEM_PACK = "!H1cH"
MAP_ITEM_SIZE = struct.calcsize(MAP_ITEM_PACK)

LAYOUT = namedtuple(
"LAYOUT",
[
"exists",
"initial_layout_size",
"input_mapping_size",
"final_layout_size",
"extra_registers",
],
)
LAYOUT_PACK = "!?iiiI"
LAYOUT_SIZE = struct.calcsize(LAYOUT_PACK)

INITIAL_LAYOUT_BIT = namedtuple("INITIAL_LAYOUT_BIT", ["index", "register_size"])
INITIAL_LAYOUT_BIT_PACK = "!ii"
INITIAL_LAYOUT_BIT_SIZE = struct.calcsize(INITIAL_LAYOUT_BIT_PACK)
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
qiskit-terra>=0.24.0
qiskit-terra>=0.24.2
requests>=2.19
requests_ntlm>=1.1.0
numpy>=1.13
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import setuptools

REQUIREMENTS = [
"qiskit-terra>=0.24.0",
"qiskit-terra>=0.24.2",
"requests>=2.19",
"requests-ntlm>=1.1.0",
"numpy>=1.13",
Expand Down
Loading