Skip to content

Commit

Permalink
Merge remote-tracking branch 'ibm/main' into concurrent-assign-parame…
Browse files Browse the repository at this point in the history
…ters
  • Loading branch information
jakelishman committed Aug 8, 2024
2 parents 8a1e217 + 154601b commit 98f0856
Show file tree
Hide file tree
Showing 11 changed files with 83 additions and 107 deletions.
4 changes: 2 additions & 2 deletions crates/accelerate/src/target_transpiler/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ impl Target {
match instruction {
TargetOperation::Variadic(_) => {
qargs_val = PropsMap::with_capacity(1);
qargs_val.extend([(None, None)].into_iter());
qargs_val.extend([(None, None)]);
self.variable_class_operations.insert(name.to_string());
}
TargetOperation::Normal(_) => {
Expand Down Expand Up @@ -872,7 +872,7 @@ impl Target {
.unwrap()
.extract::<GateMapState>()?
.into_iter()
.map(|(name, prop_map)| (name, PropsMap::from_iter(prop_map.into_iter()))),
.map(|(name, prop_map)| (name, PropsMap::from_iter(prop_map))),
);
self._gate_name_map = state
.get_item("gate_name_map")?
Expand Down
10 changes: 3 additions & 7 deletions crates/accelerate/src/target_transpiler/nullable_index_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ where
pub fn iter(&self) -> Iter<K, V> {
Iter {
map: self.map.iter(),
null_value: &self.null_val,
null_value: self.null_val.as_ref(),
}
}

Expand Down Expand Up @@ -209,7 +209,7 @@ where
/// Iterator for the key-value pairs in `NullableIndexMap`.
pub struct Iter<'a, K, V> {
map: BaseIter<'a, K, V>,
null_value: &'a Option<V>,
null_value: Option<&'a V>,
}

impl<'a, K, V> Iterator for Iter<'a, K, V> {
Expand All @@ -218,12 +218,8 @@ impl<'a, K, V> Iterator for Iter<'a, K, V> {
fn next(&mut self) -> Option<Self::Item> {
if let Some((key, val)) = self.map.next() {
Some((Some(key), val))
} else if let Some(value) = self.null_value {
let value = value;
self.null_value = &None;
Some((None, value))
} else {
None
self.null_value.take().map(|value| (None, value))
}
}

Expand Down
3 changes: 2 additions & 1 deletion qiskit/transpiler/passes/basis/basis_translator.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,8 @@ def run(self, dag):
# If the source basis is a subset of the target basis and we have no circuit
# instructions on qargs that have non-global operations there is nothing to
# translate and we can exit early.
if source_basis.issubset(target_basis) and not qargs_local_source_basis:
source_basis_names = {x[0] for x in source_basis}
if source_basis_names.issubset(target_basis) and not qargs_local_source_basis:
return dag

logger.info(
Expand Down
8 changes: 7 additions & 1 deletion qiskit/transpiler/passes/optimization/consolidate_blocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
from qiskit.transpiler.passes.synthesis import unitary_synthesis
from qiskit.circuit.controlflow import CONTROL_FLOW_OP_NAMES
from qiskit._accelerate.convert_2q_block_matrix import blocks_to_matrix
from qiskit.exceptions import QiskitError

from .collect_1q_runs import Collect1qRuns
from .collect_2q_blocks import Collect2qBlocks
Expand Down Expand Up @@ -125,7 +126,12 @@ def run(self, dag):
qc.append(nd.op, [q[block_index_map[i]] for i in nd.qargs])
unitary = UnitaryGate(Operator(qc), check_input=False)
else:
matrix = blocks_to_matrix(block, block_index_map)
try:
matrix = blocks_to_matrix(block, block_index_map)
except QiskitError:
# If building a matrix for the block fails we should not consolidate it
# because there is nothing we can do with it.
continue
unitary = UnitaryGate(matrix, check_input=False)

max_2q_depth = 20 # If depth > 20, there will be 1q gates to consolidate.
Expand Down
64 changes: 10 additions & 54 deletions qiskit/transpiler/preset_passmanagers/builtin_plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@

import os

from qiskit.circuit import Instruction
from qiskit.transpiler.passes.optimization.split_2q_unitaries import Split2QUnitaries
from qiskit.transpiler.passmanager import PassManager
from qiskit.transpiler.exceptions import TranspilerError
Expand Down Expand Up @@ -66,7 +65,6 @@
CYGate,
SXGate,
SXdgGate,
get_standard_gate_name_mapping,
)
from qiskit.utils.parallel import CPU_COUNT
from qiskit import user_config
Expand Down Expand Up @@ -173,58 +171,16 @@ def pass_manager(self, pass_manager_config, optimization_level=None) -> PassMana
)
)
init.append(CommutativeCancellation())
# skip peephole optimization before routing if target basis gate set is discrete,
# i.e. only consists of Cliffords that an user might want to keep
# use rz, sx, x, cx as basis, rely on physical optimziation to fix everything later one
stdgates = get_standard_gate_name_mapping()

def _is_one_op_non_discrete(ops):
"""Checks if one operation in `ops` is not discrete, i.e. is parameterizable
Args:
ops (List(Operation)): list of operations to check
Returns
True if at least one operation in `ops` is not discrete, False otherwise
"""
found_one_continuous_gate = False
for op in ops:
if isinstance(op, str):
if op in _discrete_skipped_ops:
continue
op = stdgates.get(op, None)

if op is not None and op.name in _discrete_skipped_ops:
continue

if op is None or not isinstance(op, Instruction):
return False

if len(op.params) > 0:
found_one_continuous_gate = True
return found_one_continuous_gate

target = pass_manager_config.target
basis = pass_manager_config.basis_gates
# consolidate gates before routing if the user did not specify a discrete basis gate, i.e.
# * no target or basis gate set has been specified
# * target has been specified, and we have one non-discrete gate in the target's spec
# * basis gates have been specified, and we have one non-discrete gate in that set
do_consolidate_blocks_init = target is None and basis is None
do_consolidate_blocks_init |= target is not None and _is_one_op_non_discrete(
target.operations
)
do_consolidate_blocks_init |= basis is not None and _is_one_op_non_discrete(basis)

if do_consolidate_blocks_init:
init.append(Collect2qBlocks())
init.append(ConsolidateBlocks())
# If approximation degree is None that indicates a request to approximate up to the
# error rates in the target. However, in the init stage we don't yet know the target
# qubits being used to figure out the fidelity so just use the default fidelity parameter
# in this case.
if pass_manager_config.approximation_degree is not None:
init.append(Split2QUnitaries(pass_manager_config.approximation_degree))
else:
init.append(Split2QUnitaries())
init.append(Collect2qBlocks())
init.append(ConsolidateBlocks())
# If approximation degree is None that indicates a request to approximate up to the
# error rates in the target. However, in the init stage we don't yet know the target
# qubits being used to figure out the fidelity so just use the default fidelity parameter
# in this case.
if pass_manager_config.approximation_degree is not None:
init.append(Split2QUnitaries(pass_manager_config.approximation_degree))
else:
init.append(Split2QUnitaries())
else:
raise TranspilerError(f"Invalid optimization level {optimization_level}")
return init
Expand Down
13 changes: 10 additions & 3 deletions qiskit/visualization/dag_visualization.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,10 +174,13 @@ def node_attr_func(node):
label = register_bit_labels.get(
node.wire, f"q_{dag.find_bit(node.wire).index}"
)
else:
elif isinstance(node.wire, Clbit):
label = register_bit_labels.get(
node.wire, f"c_{dag.find_bit(node.wire).index}"
)
else:
label = str(node.wire.name)

n["label"] = label
n["color"] = "black"
n["style"] = "filled"
Expand All @@ -187,10 +190,12 @@ def node_attr_func(node):
label = register_bit_labels.get(
node.wire, f"q[{dag.find_bit(node.wire).index}]"
)
else:
elif isinstance(node.wire, Clbit):
label = register_bit_labels.get(
node.wire, f"c[{dag.find_bit(node.wire).index}]"
)
else:
label = str(node.wire.name)
n["label"] = label
n["color"] = "black"
n["style"] = "filled"
Expand All @@ -203,8 +208,10 @@ def edge_attr_func(edge):
e = {}
if isinstance(edge, Qubit):
label = register_bit_labels.get(edge, f"q_{dag.find_bit(edge).index}")
else:
elif isinstance(edge, Clbit):
label = register_bit_labels.get(edge, f"c_{dag.find_bit(edge).index}")
else:
label = str(edge.name)
e["label"] = label
return e

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
fixes:
- |
Fixed a bug in the :class:`.ConsolidateBlocks` transpiler pass, when the
input circuit contains a custom opaque gate and neither the
``basis_gates`` or ``target`` options are set the pass would raise a
``QiskitError`` and fail. This has been corrected so that the in these
situations the transpiler pass will not consolidate the block identified
containing a custom gate instead of failing.
8 changes: 8 additions & 0 deletions releasenotes/notes/fix-var-wires-4ebc40e0b19df253.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
fixes:
- |
Fixed an issue with :func:`.dag_drawer` and :meth:`.DAGCircuit.draw`
when attempting to visualize a :class:`.DAGCircuit` instance that contained
:class:`.Var` wires. The visualizer would raise an exception trying to
do this which has been fixed so the expected visualization will be
generated.
37 changes: 0 additions & 37 deletions test/python/compiler/test_transpiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,6 @@
from qiskit.transpiler import CouplingMap, Layout, PassManager, TransformationPass
from qiskit.transpiler.exceptions import TranspilerError, CircuitTooWideForTarget
from qiskit.transpiler.passes import BarrierBeforeFinalMeasurements, GateDirection, VF2PostLayout
from qiskit.transpiler.passes.optimization.split_2q_unitaries import Split2QUnitaries

from qiskit.transpiler.passmanager_config import PassManagerConfig
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager, level_0_pass_manager
Expand Down Expand Up @@ -874,42 +873,6 @@ def test_do_not_run_gatedirection_with_symmetric_cm(self):
transpile(circ, coupling_map=coupling_map, initial_layout=layout)
self.assertFalse(mock_pass.called)

def tests_conditional_run_split_2q_unitaries(self):
"""Tests running `Split2QUnitaries` when basis gate set is (non-) discrete"""
qc = QuantumCircuit(3)
qc.sx(0)
qc.t(0)
qc.cx(0, 1)
qc.cx(1, 2)

orig_pass = Split2QUnitaries()
with patch.object(Split2QUnitaries, "run", wraps=orig_pass.run) as mock_pass:
basis = ["t", "sx", "cx"]
backend = GenericBackendV2(3, basis_gates=basis)
transpile(qc, backend=backend)
transpile(qc, basis_gates=basis)
transpile(qc, target=backend.target)
self.assertFalse(mock_pass.called)

orig_pass = Split2QUnitaries()
with patch.object(Split2QUnitaries, "run", wraps=orig_pass.run) as mock_pass:
basis = ["rz", "sx", "cx"]
backend = GenericBackendV2(3, basis_gates=basis)
transpile(qc, backend=backend, optimization_level=2)
self.assertTrue(mock_pass.called)
mock_pass.called = False
transpile(qc, basis_gates=basis, optimization_level=2)
self.assertTrue(mock_pass.called)
mock_pass.called = False
transpile(qc, target=backend.target, optimization_level=2)
self.assertTrue(mock_pass.called)
mock_pass.called = False
transpile(qc, backend=backend, optimization_level=3)
self.assertTrue(mock_pass.called)
mock_pass.called = False
transpile(qc, basis_gates=basis, optimization_level=3)
self.assertTrue(mock_pass.called)

def test_optimize_to_nothing(self):
"""Optimize gates up to fixed point in the default pipeline
See https://github.com/Qiskit/qiskit-terra/issues/2035
Expand Down
19 changes: 18 additions & 1 deletion test/python/transpiler/test_consolidate_blocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
import unittest
import numpy as np

from qiskit.circuit import QuantumCircuit, QuantumRegister, IfElseOp
from qiskit.circuit import QuantumCircuit, QuantumRegister, IfElseOp, Gate
from qiskit.circuit.library import U2Gate, SwapGate, CXGate, CZGate, UnitaryGate
from qiskit.converters import circuit_to_dag
from qiskit.transpiler.passes import ConsolidateBlocks
Expand Down Expand Up @@ -553,6 +553,23 @@ def test_inverted_order(self):
)
self.assertEqual(expected, actual)

def test_custom_no_target(self):
"""Test pass doesn't fail with custom gate."""

class MyCustomGate(Gate):
"""Custom gate."""

def __init__(self):
super().__init__(name="my_custom", num_qubits=2, params=[])

qc = QuantumCircuit(2)
qc.append(MyCustomGate(), [0, 1])

pm = PassManager([Collect2qBlocks(), ConsolidateBlocks()])
res = pm.run(qc)

self.assertEqual(res, qc)


if __name__ == "__main__":
unittest.main()
15 changes: 14 additions & 1 deletion test/python/visualization/test_dag_drawer.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@
import tempfile
import unittest

from qiskit.circuit import QuantumRegister, ClassicalRegister, QuantumCircuit, Qubit, Clbit
from qiskit.circuit import QuantumRegister, ClassicalRegister, QuantumCircuit, Qubit, Clbit, Store
from qiskit.visualization import dag_drawer
from qiskit.exceptions import InvalidFileError
from qiskit.visualization import VisualizationError
from qiskit.converters import circuit_to_dag, circuit_to_dagdependency
from qiskit.utils import optionals as _optionals
from qiskit.dagcircuit import DAGCircuit
from qiskit.circuit.classical import expr, types
from .visualization import path_to_diagram_reference, QiskitVisualizationTestCase


Expand Down Expand Up @@ -108,6 +110,17 @@ def test_dag_drawer_with_dag_dep(self):
image = Image.open(tmp_path)
self.assertImagesAreEqual(image, image_ref, 0.1)

@unittest.skipUnless(_optionals.HAS_GRAPHVIZ, "Graphviz not installed")
@unittest.skipUnless(_optionals.HAS_PIL, "PIL not installed")
def test_dag_drawer_with_var_wires(self):
"""Test visualization works with var nodes."""
a = expr.Var.new("a", types.Bool())
dag = DAGCircuit()
dag.add_input_var(a)
dag.apply_operation_back(Store(a, a), (), ())
image = dag_drawer(dag)
self.assertIsNotNone(image)


if __name__ == "__main__":
unittest.main(verbosity=2)

0 comments on commit 98f0856

Please sign in to comment.