Skip to content

Commit

Permalink
Qiskit 1.0 (#421)
Browse files Browse the repository at this point in the history
* 1.0!!!

* 1.0!!!!

* add test tools from qiskit <1.0

* fix linting issues for tests

* fix transpilation error

* fix small issues and deprecate echo_num for ARCs

* Fix: Include type for updated numpy routine in _to_cpx_matrix

* expand error message

* focus on delays in test

---------

Co-authored-by: grace-harper <119029214+grace-harper@users.noreply.github.com>
Co-authored-by: Drew Vandeth <drew.vandeth@ibm.com>
  • Loading branch information
3 people authored Jul 2, 2024
1 parent c580e06 commit e08b232
Show file tree
Hide file tree
Showing 66 changed files with 2,681 additions and 84 deletions.
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
qiskit>=0.43,<1
qiskit
qiskit-aer>=0.11.0
qiskit_ibm_provider>=0.5.0
pybind11<=2.9.1
PyMatching>=0.6.0,!=2.0.0
rustworkx>=0.12.0
networkx>=2.6.3
sympy>=1.9
testtools
numpy>=1.21.0
ipython
ipywidgets>=8.0.5
Expand Down
1 change: 1 addition & 0 deletions src/qiskit_qec/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
linear,
operators,
structures,
test,
utils,
)

Expand Down
65 changes: 8 additions & 57 deletions src/qiskit_qec/circuits/repetition_code.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,6 @@
import networkx as nx

from qiskit import ClassicalRegister, QuantumCircuit, QuantumRegister, transpile
from qiskit.circuit.library import XGate, RZGate
from qiskit.transpiler import PassManager, InstructionDurations
from qiskit_ibm_provider.transpiler.passes.scheduling import DynamicCircuitInstructionDurations
from qiskit_ibm_provider.transpiler.passes.scheduling import PadDynamicalDecoupling
from qiskit_ibm_provider.transpiler.passes.scheduling import ALAPScheduleAnalysis

from qiskit_qec.circuits.code_circuit import CodeCircuit
from qiskit_qec.utils import DecodingGraphEdge, DecodingGraphNode
Expand Down Expand Up @@ -979,6 +974,7 @@ def _process_string(self, string):
q_l = self.num_qubits[1] - 1 - j
qubit_l = self.links[q_l][1]
# the first results are themselves the changes
change = None
if t == 0:
change = syndrome_list[-1][j] != "0"
# if the link was involved in a just finished 202...
Expand Down Expand Up @@ -1222,19 +1218,16 @@ def is_cluster_neutral(self, nodes: dict):
self._linear,
)

def transpile(self, backend, echo=("X", "X"), echo_num=(2, 0)):
def transpile(self, backend, scheduling_method="alap"):
"""
Args:
backend (qiskit.providers.ibmq.IBMQBackend): Backend to transpile and schedule the
circuits for. The numbering of the qubits in this backend should correspond to
the numbering used in `self.links`.
echo (tuple): List of gate sequences (expressed as strings) to be used on code qubits and
link qubits, respectively. Valid strings are `'X'` and `'XZX'`.
echo_num (tuple): Number of times to repeat the sequences for code qubits and
link qubits, respectively.
scheduling_method (str): Name of scheduling pass. Arguemnt passed to `qiskit.transpile`.
Returns:
transpiled_circuit: As `self.circuit`, but with the circuits scheduled, transpiled and
with dynamical decoupling added.
transpiled_circuit: As `self.circuit`, but with the circuits scheduled and remapped
to the device connectivity.
"""

bases = list(self.circuit.keys())
Expand All @@ -1248,51 +1241,9 @@ def transpile(self, backend, echo=("X", "X"), echo_num=(2, 0)):
]

# transpile to backend
circuits = transpile(circuits, backend, initial_layout=initial_layout)

# then dynamical decoupling if needed
if any(echo_num):
if self.run_202:
durations = DynamicCircuitInstructionDurations().from_backend(backend)
else:
durations = InstructionDurations().from_backend(backend)

# set up the dd sequences
dd_sequences = []
spacings = []
for j in range(2):
if echo[j] == "X":
dd_sequences.append([XGate()] * echo_num[j])
spacings.append(None)
elif echo[j] == "XZX":
dd_sequences.append([XGate(), RZGate(np.pi), XGate()] * echo_num[j])
d = 1.0 / (2 * echo_num[j] - 1 + 1)
spacing = [d / 2] + ([0, d, d] * echo_num[j])[:-1] + [d / 2]
for _ in range(2):
spacing[0] += 1 - sum(spacing)
spacings.append(spacing)
else:
dd_sequences.append(None)
spacings.append(None)

# add in the dd sequences
for j, dd_sequence in enumerate(dd_sequences):
if dd_sequence:
if echo_num[j]:
qubits = self.qubits[j]
else:
qubits = None
pm = PassManager(
[
ALAPScheduleAnalysis(durations),
PadDynamicalDecoupling(
durations, dd_sequence, qubits=qubits, spacings=spacings[j]
),
]
)
circuits = pm.run(circuits)
if not isinstance(circuits, list):
circuits = [circuits]
circuits = transpile(
circuits, backend, initial_layout=initial_layout, scheduling_method=scheduling_method
)

return {basis: circuits[j] for j, basis in enumerate(bases)}

Expand Down
27 changes: 27 additions & 0 deletions src/qiskit_qec/test/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# This code is part of Qiskit.
#
# (C) Copyright IBM 2017, 2018.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.

"""Functionality and helpers for testing Qiskit."""

import warnings
from .base import QiskitTestCase
from .decorators import requires_aer_provider, online_test, slow_test
from .reference_circuits import ReferenceCircuits
from .utils import Path

warnings.warn(
"The `qiskit.test` module is deprecated since Qiskit 0.46, and will be removed in Qiskit 1.0."
" This module was internal to Qiskit's test suite; if you absolutely require any of its"
" functionality, consider vendoring the code into your own project.",
DeprecationWarning,
stacklevel=2,
)
125 changes: 125 additions & 0 deletions src/qiskit_qec/test/_canonical.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
# This code is part of Qiskit.
#
# (C) Copyright IBM 2022.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.

"""Utility methods for canonicalising various Qiskit objects, to help with testing."""

import threading

from qiskit.circuit import (
BreakLoopOp,
CircuitInstruction,
ContinueLoopOp,
ControlFlowOp,
ForLoopOp,
Parameter,
QuantumCircuit,
)


class _CanonicalParametersIterator:
"""An object that, when iterated through, will produce the same sequence of parameters as every
other instance of this iterator."""

__parameters = []
__mutex = threading.Lock()

def __init__(self):
self._counter = 0

def __iter__(self):
return self

def __next__(self):
with self.__mutex:
if len(self.__parameters) >= self._counter:
param = Parameter(f"_canonicalization_loop_{self._counter}")
self.__parameters.append(param)
out = self.__parameters[self._counter]
self._counter += 1
return out


def canonicalize_control_flow(circuit: QuantumCircuit) -> QuantumCircuit:
"""Canonicalize all control-flow operations in a circuit.
This is not an efficient operation, and does not affect any properties of the circuit. Its
intent is to normalise parts of circuits that have a non-deterministic construction. These are
the ordering of bit arguments in control-flow blocks output by the builder interface, and
automatically generated ``for``-loop variables.
The canonical form sorts the bits in the arguments of these operations so that they always
appear in the order they were originally added to the outer-most circuit. For-loop variables
are re-bound into new, cached auto-generated ones."""
params = iter(_CanonicalParametersIterator())
base_bit_order = {bit: i for i, bit in enumerate(circuit.qubits)}
base_bit_order.update((bit, i) for i, bit in enumerate(circuit.clbits))

def worker(circuit, bit_map=None):
if bit_map is None:
bit_map = {bit: bit for bits in (circuit.qubits, circuit.clbits) for bit in bits}

def bit_key(bit):
return base_bit_order[bit_map[bit]]

# This isn't quite QuantumCircuit.copy_empty_like because of the bit reordering.
out = QuantumCircuit(
sorted(circuit.qubits, key=bit_key),
sorted(circuit.clbits, key=bit_key),
*circuit.qregs,
*circuit.cregs,
name=circuit.name,
global_phase=circuit.global_phase,
metadata=circuit.metadata,
)
for instruction in circuit.data:
new_instruction = instruction
# Control-flow operations associated bits in the instruction arguments with bits in the
# circuit blocks just by sequencing. All blocks must have the same width.
if isinstance(new_instruction.operation, ControlFlowOp):
op = new_instruction.operation
first_block = op.blocks[0]
inner_bit_map = dict(
zip(first_block.qubits, (bit_map[bit] for bit in new_instruction.qubits))
)
inner_bit_map.update(
zip(first_block.clbits, (bit_map[bit] for bit in new_instruction.clbits))
)
new_instruction = CircuitInstruction(
operation=op.replace_blocks(
[worker(block, inner_bit_map) for block in op.blocks]
),
qubits=sorted(new_instruction.qubits, key=bit_key),
clbits=sorted(new_instruction.clbits, key=bit_key),
)
elif isinstance(new_instruction.operation, (BreakLoopOp, ContinueLoopOp)):
new_instruction = new_instruction.replace(
qubits=sorted(new_instruction.qubits, key=bit_key),
clbits=sorted(new_instruction.clbits, key=bit_key),
)
# For for loops specifically, the control-flow builders generate a loop parameter if one
# is needed but not explicitly supplied. We want the parameters to compare equal, so we
# replace them with those from a shared list.
if isinstance(new_instruction.operation, ForLoopOp):
old_op = new_instruction.operation
indexset, loop_param, body = old_op.params
if loop_param is not None:
new_loop_param = next(params)
new_op = ForLoopOp(
indexset,
new_loop_param,
body.assign_parameters({loop_param: new_loop_param}),
)
new_instruction = new_instruction.replace(operation=new_op)
out._append(new_instruction)
return out

return worker(circuit)
Loading

0 comments on commit e08b232

Please sign in to comment.