Skip to content

Commit

Permalink
Merge branch 'Qiskit:main' into pr-add-mcrgates-to-library
Browse files Browse the repository at this point in the history
  • Loading branch information
king-p3nguin authored May 10, 2024
2 parents 9e34d41 + b80885d commit 6db6a79
Show file tree
Hide file tree
Showing 17 changed files with 155 additions and 43 deletions.
85 changes: 66 additions & 19 deletions docs/apidoc/index.rst
Original file line number Diff line number Diff line change
@@ -1,42 +1,89 @@
.. module:: qiskit
..
Within each section, the modules should be ordered alphabetically by
module name (not RST filename).
=============
API Reference
=============

Circuit construction:

.. toctree::
:maxdepth: 1

circuit
circuit_library
circuit_classical
circuit_singleton
compiler
visualization
classicalfunction
circuit_library
circuit_singleton

Quantum information:

.. toctree::
:maxdepth: 1

quantum_info

Transpilation:

.. toctree::
:maxdepth: 1

converters
assembler
dagcircuit
passmanager
synthesis
qiskit.synthesis.unitary.aqc
transpiler
transpiler_passes
transpiler_synthesis_plugins
transpiler_preset
transpiler_plugins

Primitives and providers:

.. toctree::
:maxdepth: 1

primitives
providers
providers_basic_provider
providers_fake_provider
providers_models
pulse
scheduler
synthesis
qiskit.synthesis.unitary.aqc
primitives

Results and visualizations:

.. toctree::
:maxdepth: 1

result
visualization

Serialization:

.. toctree::
:maxdepth: 1

qasm2
qasm3
qobj
qpy
quantum_info
result
transpiler
transpiler_passes
transpiler_preset
transpiler_plugins
transpiler_synthesis_plugins
utils

Pulse-level programming:

.. toctree::
:maxdepth: 1

pulse
scheduler

Other:

.. toctree::
:maxdepth: 1

assembler
compiler
exceptions
qobj
utils
2 changes: 0 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -218,15 +218,13 @@ disable = [
# with the rationale
"arguments-renamed",
"broad-exception-raised",
"consider-iterating-dictionary",
"consider-using-dict-items",
"consider-using-enumerate",
"consider-using-f-string",
"no-member",
"no-value-for-parameter",
"not-context-manager",
"unexpected-keyword-arg",
"unnecessary-dict-index-lookup",
"unnecessary-dunder-call",
"unnecessary-lambda-assignment",
"unspecified-encoding",
Expand Down
2 changes: 1 addition & 1 deletion qiskit/circuit/library/n_local/pauli_two_design.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ def _build_rotation_layer(self, circuit, param_iter, i):
qubits = range(self.num_qubits)

# if no gates for this layer were generated, generate them
if i not in self._gates.keys():
if i not in self._gates:
self._gates[i] = list(self._rng.choice(["rx", "ry", "rz"], self.num_qubits))
# if not enough gates exist, add more
elif len(self._gates[i]) < self.num_qubits:
Expand Down
4 changes: 2 additions & 2 deletions qiskit/pulse/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ def __call__(self, *args, **kwargs) -> complex | ast.Expression | PulseExpressio
if kwargs:
for key, val in kwargs.items():
if key in self.params:
if key not in self._locals_dict.keys():
if key not in self._locals_dict:
self._locals_dict[key] = val
else:
raise PulseError(
Expand Down Expand Up @@ -272,7 +272,7 @@ def visit_Call(self, node: ast.Call) -> ast.Call | ast.Constant:
node = copy.copy(node)
node.args = [self.visit(arg) for arg in node.args]
if all(isinstance(arg, ast.Constant) for arg in node.args):
if node.func.id not in self._math_ops.keys():
if node.func.id not in self._math_ops:
raise PulseError("Function %s is not supported." % node.func.id)
_args = [arg.value for arg in node.args]
_val = self._math_ops[node.func.id](*_args)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ def generate_basic_approximations(
basis = []
for gate in basis_gates:
if isinstance(gate, str):
if gate not in _1q_gates.keys():
if gate not in _1q_gates:
raise ValueError(f"Invalid gate identifier: {gate}")
basis.append(gate)
else: # gate is a qiskit.circuit.Gate
Expand Down
2 changes: 1 addition & 1 deletion qiskit/synthesis/discrete_basis/solovay_kitaev.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ def _remove_inverse_follows_gate(sequence):
while index < len(sequence.gates) - 1:
curr_gate = sequence.gates[index]
next_gate = sequence.gates[index + 1]
if curr_gate.name in _1q_inverses.keys():
if curr_gate.name in _1q_inverses:
remove = _1q_inverses[curr_gate.name] == next_gate.name
else:
remove = curr_gate.inverse() == next_gate
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,8 +160,13 @@ def run(self, dag: DAGCircuit) -> DAGCircuit:
if len(dag.qubits) != next(iter(dag.qregs.values())).size:
raise TranspilerError("Circuit has qubits not contained in the qubit register.")

new_dag = dag.copy_empty_like()
# Fix output permutation -- copied from ElidePermutations
input_qubit_mapping = {qubit: index for index, qubit in enumerate(dag.qubits)}
self.property_set["original_layout"] = Layout(input_qubit_mapping)
if self.property_set["original_qubit_indices"] is None:
self.property_set["original_qubit_indices"] = input_qubit_mapping

new_dag = dag.copy_empty_like()
current_layout = Layout.generate_trivial_layout(*dag.qregs.values())

# Used to keep track of nodes that do not decompose using swap strategies.
Expand All @@ -183,6 +188,8 @@ def run(self, dag: DAGCircuit) -> DAGCircuit:

self._compose_non_swap_nodes(accumulator, current_layout, new_dag)

self.property_set["virtual_permutation_layout"] = current_layout

return new_dag

def _compose_non_swap_nodes(
Expand Down
10 changes: 5 additions & 5 deletions qiskit/transpiler/passes/routing/star_prerouting.py
Original file line number Diff line number Diff line change
Expand Up @@ -329,13 +329,13 @@ def _apply_mapping(qargs, qubit_mapping, qubits):
last_2q_gate = None

int_digits = floor(log10(len(processing_order))) + 1
processing_order_s = set(processing_order)
processing_order_index_map = {
node: f"a{str(index).zfill(int(int_digits))}"
for index, node in enumerate(processing_order)
}

def tie_breaker_key(node):
if node in processing_order_s:
return "a" + str(processing_order.index(node)).zfill(int(int_digits))
else:
return node.sort_key
return processing_order_index_map.get(node, node.sort_key)

for node in dag.topological_op_nodes(key=tie_breaker_key):
block_id = node_to_block_id.get(node, None)
Expand Down
4 changes: 2 additions & 2 deletions qiskit/transpiler/passes/synthesis/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -698,13 +698,13 @@ def __init__(self):
self.plugins_by_op = {}
for plugin_name in self.plugins.names():
op_name, method_name = plugin_name.split(".")
if op_name not in self.plugins_by_op.keys():
if op_name not in self.plugins_by_op:
self.plugins_by_op[op_name] = []
self.plugins_by_op[op_name].append(method_name)

def method_names(self, op_name):
"""Returns plugin methods for op_name."""
if op_name in self.plugins_by_op.keys():
if op_name in self.plugins_by_op:
return self.plugins_by_op[op_name]
else:
return []
Expand Down
2 changes: 1 addition & 1 deletion qiskit/transpiler/target.py
Original file line number Diff line number Diff line change
Expand Up @@ -814,7 +814,7 @@ def check_obj_params(parameters, obj):
if qargs in self._gate_map[op_name]:
return True
if self._gate_map[op_name] is None or None in self._gate_map[op_name]:
return self._gate_name_map[op_name].num_qubits == len(qargs) and all(
return obj.num_qubits == len(qargs) and all(
x < self.num_qubits for x in qargs
)
return False
Expand Down
2 changes: 1 addition & 1 deletion qiskit/visualization/circuit/matplotlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -893,7 +893,7 @@ def _draw_regs_wires(self, num_folds, xmax, max_x_index, qubits_dict, clbits_dic
this_clbit_dict = {}
for clbit in clbits_dict.values():
y = clbit["y"] - fold_num * (glob_data["n_lines"] + 1)
if y not in this_clbit_dict.keys():
if y not in this_clbit_dict:
this_clbit_dict[y] = {
"val": 1,
"wire_label": clbit["wire_label"],
Expand Down
4 changes: 2 additions & 2 deletions qiskit/visualization/circuit/qcstyle.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ class StyleDict(dict):

def __setitem__(self, key: Any, value: Any) -> None:
# allow using field abbreviations
if key in self.ABBREVIATIONS.keys():
if key in self.ABBREVIATIONS:
key = self.ABBREVIATIONS[key]

if key not in self.VALID_FIELDS:
Expand All @@ -85,7 +85,7 @@ def __setitem__(self, key: Any, value: Any) -> None:

def __getitem__(self, key: Any) -> Any:
# allow using field abbreviations
if key in self.ABBREVIATIONS.keys():
if key in self.ABBREVIATIONS:
key = self.ABBREVIATIONS[key]

return super().__getitem__(key)
Expand Down
10 changes: 9 additions & 1 deletion qiskit/visualization/gate_map.py
Original file line number Diff line number Diff line change
Expand Up @@ -1039,7 +1039,9 @@ def plot_coupling_map(
graph = CouplingMap(coupling_map).graph

if not plot_directed:
line_color_map = dict(zip(graph.edge_list(), line_color))
graph = graph.to_undirected(multigraph=False)
line_color = [line_color_map[edge] for edge in graph.edge_list()]

for node in graph.node_indices():
graph[node] = node
Expand Down Expand Up @@ -1122,7 +1124,13 @@ def plot_circuit_layout(circuit, backend, view="virtual", qubit_coordinates=None
Args:
circuit (QuantumCircuit): Input quantum circuit.
backend (Backend): Target backend.
view (str): Layout view: either 'virtual' or 'physical'.
view (str): How to label qubits in the layout. Options:
- ``"virtual"``: Label each qubit with the index of the virtual qubit that
mapped to it.
- ``"physical"``: Label each qubit with the index of the physical qubit that it
corresponds to on the device.
qubit_coordinates (Sequence): An optional sequence input (list or array being the
most common) of 2d coordinates for each qubit. The length of the
sequence must match the number of qubits on the backend. The sequence
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
fixes:
- |
Fixed an oversight in the :class:`.Commuting2qGateRouter` transpiler pass where the qreg permutations
were not added to the pass property set, so they would have to be tracked manually by the user. Now it's
possible to access the permutation through the output circuit's ``layout`` property and plug the pass
into any transpilation pipeline without loss of information.
5 changes: 5 additions & 0 deletions releasenotes/notes/plot-circuit-layout-5935646107893c12.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
fixes:
- |
Fixed a bug in :func:`plot_coupling_map` that caused the edges of the coupling map to be colored incorrectly.
See https://github.com/Qiskit/qiskit/pull/12369 for details.
4 changes: 2 additions & 2 deletions test/python/transpiler/test_high_level_synthesis.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ class OpARepeatSynthesisPlugin(HighLevelSynthesisPlugin):
"""The repeat synthesis for opA"""

def run(self, high_level_object, coupling_map=None, target=None, qubits=None, **options):
if "n" not in options.keys():
if "n" not in options:
return None

qc = QuantumCircuit(1)
Expand Down Expand Up @@ -206,7 +206,7 @@ def __init__(self):

def method_names(self, op_name):
"""Returns plugin methods for op_name."""
if op_name in self.plugins_by_op.keys():
if op_name in self.plugins_by_op:
return self.plugins_by_op[op_name]
else:
return []
Expand Down
44 changes: 42 additions & 2 deletions test/python/transpiler/test_swap_strategy_router.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# This code is part of Qiskit.
#
# (C) Copyright IBM 2022, 2023.
# (C) Copyright IBM 2022, 2024.
#
# 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
Expand All @@ -15,12 +15,14 @@
from ddt import ddt, data

from qiskit.circuit import QuantumCircuit, Qubit, QuantumRegister
from qiskit.providers.fake_provider import GenericBackendV2
from qiskit.transpiler import PassManager, CouplingMap, Layout, TranspilerError
from qiskit.circuit.library import PauliEvolutionGate, CXGate
from qiskit.circuit.library.n_local import QAOAAnsatz
from qiskit.converters import circuit_to_dag
from qiskit.exceptions import QiskitError
from qiskit.quantum_info import Pauli, SparsePauliOp
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit.transpiler.passes import FullAncillaAllocation
from qiskit.transpiler.passes import EnlargeWithAncilla
from qiskit.transpiler.passes import ApplyLayout
Expand Down Expand Up @@ -562,9 +564,47 @@ def test_edge_coloring(self, edge_coloring):

self.assertEqual(pm_.run(circ), expected)

def test_permutation_tracking(self):
"""Test that circuit layout permutations are properly tracked in the pass property
set and returned with the output circuit."""

# We use the same scenario as the QAOA test above
mixer = QuantumCircuit(4)
for idx in range(4):
mixer.ry(-idx, idx)

op = SparsePauliOp.from_list([("IZZI", 1), ("ZIIZ", 2), ("ZIZI", 3)])
circ = QAOAAnsatz(op, reps=2, mixer_operator=mixer)

expected_swap_permutation = [3, 1, 2, 0]
expected_full_permutation = [1, 3, 2, 0]

cmap = CouplingMap(couplinglist=[(0, 1), (1, 2), (2, 3)])
swap_strat = SwapStrategy(cmap, swap_layers=[[(0, 1), (2, 3)], [(1, 2)]])

# test standalone
swap_pm = PassManager(
[
FindCommutingPauliEvolutions(),
Commuting2qGateRouter(swap_strat),
]
)
swapped = swap_pm.run(circ.decompose())

# test as pre-routing step
backend = GenericBackendV2(num_qubits=4, coupling_map=[[0, 1], [0, 2], [0, 3]], seed=42)
pm = generate_preset_pass_manager(
optimization_level=3, target=backend.target, seed_transpiler=40
)
pm.pre_routing = swap_pm
full = pm.run(circ.decompose())

self.assertEqual(swapped.layout.routing_permutation(), expected_swap_permutation)
self.assertEqual(full.layout.routing_permutation(), expected_full_permutation)


class TestSwapRouterExceptions(QiskitTestCase):
"""Test that exceptions are properly raises."""
"""Test that exceptions are properly raised."""

def setUp(self):
"""Setup useful variables."""
Expand Down

0 comments on commit 6db6a79

Please sign in to comment.