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

Oxidize Commutation Analysis #12995

Merged
merged 118 commits into from
Sep 4, 2024
Merged
Show file tree
Hide file tree
Changes from 117 commits
Commits
Show all changes
118 commits
Select commit Hold shift + click to select a range
f421a26
init
sbrandhsn Jul 30, 2024
7425d18
up
sbrandhsn Jul 31, 2024
a7016b0
lint
sbrandhsn Jul 31, 2024
861b548
.
sbrandhsn Aug 1, 2024
d310746
up
sbrandhsn Aug 6, 2024
878e3d6
before cache
sbrandhsn Aug 6, 2024
f01ddfa
with cache
sbrandhsn Aug 7, 2024
4bd7391
correct
sbrandhsn Aug 7, 2024
c60bb8d
cleaned up
sbrandhsn Aug 8, 2024
24659c5
lint reno
sbrandhsn Aug 8, 2024
1c3521e
Update Cargo.lock
sbrandhsn Aug 8, 2024
bab0ee0
Merge branch 'main' into oxidize-commutation-checker
sbrandhsn Aug 8, 2024
7e52136
.
sbrandhsn Aug 8, 2024
665c6b3
Merge branch 'main' into oxidize-commutation-checker
sbrandhsn Aug 8, 2024
ce512b2
up
sbrandhsn Aug 8, 2024
cc2ba14
.
sbrandhsn Aug 8, 2024
e6c0637
revert op
sbrandhsn Aug 8, 2024
b4c2ee3
.
sbrandhsn Aug 8, 2024
b24ae43
.
sbrandhsn Aug 8, 2024
e34c3ec
.
sbrandhsn Aug 8, 2024
1cbbf84
.
sbrandhsn Aug 8, 2024
85041c7
Delete Cargo.lock
sbrandhsn Aug 8, 2024
e878608
.
sbrandhsn Aug 8, 2024
f7de4e4
corrected string comparison
sbrandhsn Aug 8, 2024
2a4c864
removed Operator class from operation.rs
sbrandhsn Aug 9, 2024
b59499a
.
sbrandhsn Aug 9, 2024
f5bb696
Apply suggestions from code review
sbrandhsn Aug 12, 2024
93b2927
comments from code review
sbrandhsn Aug 12, 2024
c911205
Port DAGCircuit to Rust
kevinhartman Aug 12, 2024
1e4e6f3
Update visual mpl circuit drawer references
mtreinish Aug 12, 2024
463b86a
Ensure DAGNode.sort_key is always a string
mtreinish Aug 12, 2024
01c06e5
Merge branch 'main' into oxidize-dag
mtreinish Aug 12, 2024
f6b27ff
Make Python argument first in Param::eq and Param::is_close
mtreinish Aug 12, 2024
0e62ad0
Merge branch 'main' into oxidize-dag
mtreinish Aug 12, 2024
aaf38b9
Fix merge conflict with #12943
mtreinish Aug 12, 2024
28f6de1
Add release notes and test for invalid args on apply methods
mtreinish Aug 12, 2024
11c52ab
Restore `inplace` argument functionality for substitute_node()
mtreinish Aug 12, 2024
d68743f
Revert needless dict() cast on metadata in dag_to_circuit()
mtreinish Aug 12, 2024
e07f3d5
Add code comment for DAGOpNode.__eq__ parameter checking
mtreinish Aug 12, 2024
30a4a1a
Raise a ValueError on DAGNode creation with invalid index
mtreinish Aug 12, 2024
6dec768
Use macro argument to set python getter/setter name
mtreinish Aug 12, 2024
1de3290
Remove Ord and PartialOrd derives from interner::Index
mtreinish Aug 12, 2024
5c6f006
Fix missing nodes in matplotlib drawer.
kevinhartman Aug 12, 2024
b90b660
Revert "Update visual mpl circuit drawer references"
mtreinish Aug 13, 2024
9a6f953
Update visual mpl circuit drawer references for control flow circuits
mtreinish Aug 13, 2024
7970e3d
Apply suggestions from code review
sbrandhsn Aug 13, 2024
f317077
code review
sbrandhsn Aug 13, 2024
701c980
Fix edge cases in DAGOpNode.__eq__
mtreinish Aug 13, 2024
464e87b
Remove Param::add() for global phase addition
mtreinish Aug 13, 2024
7f1b451
More complete fix for matplotlib drawer.
kevinhartman Aug 13, 2024
6aab4a6
Revert "Update visual mpl circuit drawer references for control flow …
kevinhartman Aug 13, 2024
e5e57c6
Unify rayon versions in workspace
mtreinish Aug 13, 2024
584ee9b
Remove unused _GLOBAL_NID.
kevinhartman Aug 13, 2024
bdeb5f6
Merge branch 'main' into oxidize-dag
mtreinish Aug 13, 2024
1d249ab
Use global monotonic ID counter for ids in drawer
mtreinish Aug 14, 2024
40c15ec
Merge remote-tracking branch 'origin/main' into oxidize-dag
mtreinish Aug 14, 2024
39586ba
Merge branch 'main' into oxidize-dag
mtreinish Aug 14, 2024
6c5e1e9
Remove unused BitData iterator stuff.
kevinhartman Aug 14, 2024
5c9d285
Fully port Optimize1qGatesDecomposition to Rust
mtreinish Aug 13, 2024
db89660
remove with_gil in favor of passing python tokens as params
sbrandhsn Aug 15, 2024
e9e4a27
Merge branch 'pr/12959' into oxidize-commutative-analysis
sbrandhsn Aug 15, 2024
6a5f389
Apply suggestions from code review
sbrandhsn Aug 16, 2024
de1ee92
fmt
sbrandhsn Aug 16, 2024
eeded61
python serialization
sbrandhsn Aug 19, 2024
a0501d3
deprecation
sbrandhsn Aug 19, 2024
1225061
Update commutation_checker.py
sbrandhsn Aug 19, 2024
21dec35
heh
sbrandhsn Aug 19, 2024
e37c5b0
init
sbrandhsn Aug 20, 2024
c0adbb8
let Pytuple collect
sbrandhsn Aug 20, 2024
d76fd80
lint
sbrandhsn Aug 20, 2024
22e0044
Merge branch 'main' into oxidize-commutation-checker
sbrandhsn Aug 20, 2024
ee3f4d3
Merge remote-tracking branch 'origin/main' into oxidize-commutation-c…
mtreinish Aug 26, 2024
454208c
First set of comments
Cryoris Aug 27, 2024
847b425
Second part
Cryoris Aug 29, 2024
3724873
Matrix speed & fix string sort
Cryoris Aug 29, 2024
cd9c119
have the Python implementation use Rust
Cryoris Aug 29, 2024
5266807
lint & tools
Cryoris Aug 29, 2024
53c902c
remove unsafe blocks
Cryoris Aug 29, 2024
6e33d38
One more try to avoid segfaulty windows
Cryoris Aug 30, 2024
2b45b04
Merge branch 'main' into oxidize-commutation-checker
Cryoris Aug 30, 2024
18c406d
Original version
Cryoris Aug 30, 2024
b42f7e4
Sync with updated CommutationChecker
Cryoris Aug 30, 2024
c564b80
Debug: disable cache
Cryoris Aug 30, 2024
6292e93
... second try
Cryoris Aug 30, 2024
8f0f7d2
Update crates/accelerate/src/commutation_checker.rs
mtreinish Aug 30, 2024
6636860
Restore azure config
mtreinish Aug 30, 2024
2504d1c
Merge remote-tracking branch 'origin/main' into oxidize-commutation-c…
mtreinish Aug 30, 2024
52cf8bf
Remove unused import
mtreinish Aug 30, 2024
ac75c91
Revert "Debug: disable cache"
mtreinish Aug 30, 2024
816f5eb
Don't overallocate cache
mtreinish Aug 30, 2024
6e8f779
Cleanup parameter key type to handle edge conditions better
mtreinish Aug 30, 2024
d312791
Remove unnecessary cache hit rate tracking
mtreinish Aug 30, 2024
f4bfa9e
Undo test assertion changes
mtreinish Aug 30, 2024
29f1c07
Undo unrelated test changes
mtreinish Aug 30, 2024
c554666
Merge remote-tracking branch 'origin/main' into oxidize-commutation-c…
mtreinish Aug 30, 2024
0041098
Undo pending deprecation and unify commutation classes
mtreinish Aug 30, 2024
a05df8c
Undo gha config changes
mtreinish Aug 30, 2024
f231c83
Make serialization explicit
mtreinish Aug 30, 2024
2bbf36b
Remove stray SAFETY comment
mtreinish Aug 30, 2024
f8afd5d
Remove ddt usage from the tests
mtreinish Aug 30, 2024
ea09d48
Update release note
mtreinish Aug 30, 2024
62d2cdb
Fix CommutationChecker class import
mtreinish Aug 30, 2024
71ac2fd
Remove invalid test assertion for no longer public attribute
mtreinish Aug 30, 2024
12d0a05
Ray's review comments
Cryoris Sep 2, 2024
9235004
Handle ``atol/rtol``, more error propagation
Cryoris Sep 2, 2024
af74cb9
Merge branch 'oxidize-commutation-checker' into rust/commutation-anal…
Cryoris Sep 2, 2024
d625539
update to latest changes in commchecker
Cryoris Sep 2, 2024
e19f7c6
Merge branch 'rust/commutation-analysis' into oxidize-commutative-ana…
Cryoris Sep 2, 2024
52ba2e0
Merge branch 'main' into oxidize-commutative-analysis
Cryoris Sep 2, 2024
6835c8f
fix merge conflict remnants
Cryoris Sep 2, 2024
c7dde73
re-use expensive quantities
Cryoris Sep 2, 2024
34a8a14
Merge branch 'oxidize-commutation-checker' into oxidize-commutative-a…
Cryoris Sep 3, 2024
d7cf2cc
Merge branch 'main' into oxidize-commutative-analysis
Cryoris Sep 3, 2024
8b49c19
add missing header
Cryoris Sep 3, 2024
7207998
gentler error handling
Cryoris Sep 4, 2024
c08cfae
review comments & more docs
Cryoris Sep 4, 2024
7073570
Use vec over IndexSet + clippy
Cryoris Sep 4, 2024
68f4f4b
Simplify python class construction
mtreinish Sep 4, 2024
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
192 changes: 192 additions & 0 deletions crates/accelerate/src/commutation_analysis.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
// This code is part of Qiskit.
//
// (C) Copyright IBM 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
// 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.

use pyo3::exceptions::PyValueError;
use pyo3::prelude::PyModule;
use pyo3::{pyfunction, pymodule, wrap_pyfunction, Bound, PyResult, Python};
use qiskit_circuit::Qubit;

use crate::commutation_checker::CommutationChecker;
use hashbrown::HashMap;
use pyo3::prelude::*;

use pyo3::types::{PyDict, PyList};
use qiskit_circuit::dag_circuit::{DAGCircuit, NodeType, Wire};
use rustworkx_core::petgraph::stable_graph::NodeIndex;

// Custom types to store the commutation sets and node indices,
// see the docstring below for more information.
type CommutationSet = HashMap<Wire, Vec<Vec<NodeIndex>>>;
type NodeIndices = HashMap<(NodeIndex, Wire), usize>;

// the maximum number of qubits we check commutativity for
const MAX_NUM_QUBITS: u32 = 3;

/// Compute the commutation sets for a given DAG.
///
/// We return two HashMaps:
/// * {wire: commutation_sets}: For each wire, we keep a vector of index sets, where each index
/// set contains mutually commuting nodes. Note that these include the input and output nodes
/// which do not commute with anything.
/// * {(node, wire): index}: For each (node, wire) pair we store the index indicating in which
/// commutation set the node appears on a given wire.
///
/// For example, if we have a circuit
///
/// |0> -- X -- SX -- Z (out)
/// 0 2 3 4 1 <-- node indices including input (0) and output (1) nodes
///
/// Then we would have
///
/// commutation_set = {0: [[0], [2, 3], [4], [1]]}
/// node_indices = {(0, 0): 0, (1, 0): 3, (2, 0): 1, (3, 0): 1, (4, 0): 2}
///
fn analyze_commutations_inner(
py: Python,
dag: &mut DAGCircuit,
commutation_checker: &mut CommutationChecker,
) -> PyResult<(CommutationSet, NodeIndices)> {
let mut commutation_set: CommutationSet = HashMap::new();
let mut node_indices: NodeIndices = HashMap::new();

for qubit in 0..dag.num_qubits() {
let wire = Wire::Qubit(Qubit(qubit as u32));

for current_gate_idx in dag.nodes_on_wire(py, &wire, false) {
// get the commutation set associated with the current wire, or create a new
// index set containing the current gate
let commutation_entry = commutation_set
.entry(wire.clone())
.or_insert_with(|| vec![vec![current_gate_idx]]);

// we can unwrap as we know the commutation entry has at least one element
let last = commutation_entry.last_mut().unwrap();

// if the current gate index is not in the set, check whether it commutes with
// the previous nodes -- if yes, add it to the commutation set
if !last.contains(&current_gate_idx) {
let mut all_commute = true;

for prev_gate_idx in last.iter() {
// if the node is an input/output node, they do not commute, so we only
// continue if the nodes are operation nodes
if let (NodeType::Operation(packed_inst0), NodeType::Operation(packed_inst1)) =
(&dag.dag[current_gate_idx], &dag.dag[*prev_gate_idx])
mtreinish marked this conversation as resolved.
Show resolved Hide resolved
{
let op1 = packed_inst0.op.view();
let op2 = packed_inst1.op.view();
let params1 = packed_inst0.params_view();
let params2 = packed_inst1.params_view();
let qargs1 = dag.get_qargs(packed_inst0.qubits);
let qargs2 = dag.get_qargs(packed_inst1.qubits);
let cargs1 = dag.get_cargs(packed_inst0.clbits);
let cargs2 = dag.get_cargs(packed_inst1.clbits);

all_commute = commutation_checker.commute_inner(
py,
&op1,
params1,
packed_inst0.extra_attrs.as_deref(),
qargs1,
cargs1,
&op2,
params2,
packed_inst1.extra_attrs.as_deref(),
qargs2,
cargs2,
MAX_NUM_QUBITS,
)?;
if !all_commute {
break;
}
} else {
all_commute = false;
break;
}
}

if all_commute {
// all commute, add to current list
last.push(current_gate_idx);
} else {
// does not commute, create new list
commutation_entry.push(vec![current_gate_idx]);
}
}

node_indices.insert(
(current_gate_idx, wire.clone()),
commutation_entry.len() - 1,
);
}
}

Ok((commutation_set, node_indices))
}

#[pyfunction]
#[pyo3(signature = (dag, commutation_checker))]
pub(crate) fn analyze_commutations(
py: Python,
dag: &mut DAGCircuit,
commutation_checker: &mut CommutationChecker,
) -> PyResult<Py<PyDict>> {
// This returns two HashMaps:
// * The commuting nodes per wire: {wire: [commuting_nodes_1, commuting_nodes_2, ...]}
// * The index in which commutation set a given node is located on a wire: {(node, wire): index}
// The Python dict will store both of these dictionaries in one.
let (commutation_set, node_indices) = analyze_commutations_inner(py, dag, commutation_checker)?;

let out_dict = PyDict::new_bound(py);

// First set the {wire: [commuting_nodes_1, ...]} bit
for (wire, commutations) in commutation_set {
// we know all wires are of type Wire::Qubit, since in analyze_commutations_inner
// we only iterater over the qubits
let py_wire = match wire {
Wire::Qubit(q) => dag.qubits.get(q).unwrap().to_object(py),
_ => return Err(PyValueError::new_err("Unexpected wire type.")),
};

out_dict.set_item(
py_wire,
PyList::new_bound(
py,
commutations.iter().map(|inner| {
PyList::new_bound(
py,
inner
.iter()
.map(|node_index| dag.get_node(py, *node_index).unwrap()),
)
}),
),
)?;
}

// Then we add the {(node, wire): index} dictionary
for ((node_index, wire), index) in node_indices {
let py_wire = match wire {
Wire::Qubit(q) => dag.qubits.get(q).unwrap().to_object(py),
_ => return Err(PyValueError::new_err("Unexpected wire type.")),
};
out_dict.set_item((dag.get_node(py, node_index)?, py_wire), index)?;
}

Ok(out_dict.unbind())
}

#[pymodule]
pub fn commutation_analysis(m: &Bound<PyModule>) -> PyResult<()> {
m.add_wrapped(wrap_pyfunction!(analyze_commutations))?;
Ok(())
}
4 changes: 2 additions & 2 deletions crates/accelerate/src/commutation_checker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ where
/// lookups. It's not meant to be a public facing Python object though and only used
/// internally by the Python class.
#[pyclass(module = "qiskit._accelerate.commutation_checker")]
struct CommutationChecker {
pub struct CommutationChecker {
library: CommutationLibrary,
cache_max_entries: usize,
cache: HashMap<(String, String), CommutationCacheEntry>,
Expand Down Expand Up @@ -227,7 +227,7 @@ impl CommutationChecker {

impl CommutationChecker {
#[allow(clippy::too_many_arguments)]
fn commute_inner(
pub fn commute_inner(
&mut self,
py: Python,
op1: &OperationRef,
Expand Down
1 change: 1 addition & 0 deletions crates/accelerate/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use std::env;
use pyo3::import_exception;

pub mod circuit_library;
pub mod commutation_analysis;
pub mod commutation_checker;
pub mod convert_2q_block_matrix;
pub mod dense_layout;
Expand Down
2 changes: 1 addition & 1 deletion crates/circuit/src/dag_circuit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5523,7 +5523,7 @@ impl DAGCircuit {
/// Get the nodes on the given wire.
///
/// Note: result is empty if the wire is not in the DAG.
fn nodes_on_wire(&self, py: Python, wire: &Wire, only_ops: bool) -> Vec<NodeIndex> {
pub fn nodes_on_wire(&self, py: Python, wire: &Wire, only_ops: bool) -> Vec<NodeIndex> {
let mut nodes = Vec::new();
let mut current_node = match wire {
Wire::Qubit(qubit) => self.qubit_io_map.get(qubit.0 as usize).map(|x| x[0]),
Expand Down
19 changes: 10 additions & 9 deletions crates/pyext/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@
use pyo3::prelude::*;

use qiskit_accelerate::{
circuit_library::circuit_library, commutation_checker::commutation_checker,
convert_2q_block_matrix::convert_2q_block_matrix, dense_layout::dense_layout,
error_map::error_map, euler_one_qubit_decomposer::euler_one_qubit_decomposer,
isometry::isometry, nlayout::nlayout, optimize_1q_gates::optimize_1q_gates,
pauli_exp_val::pauli_expval, results::results, sabre::sabre, sampled_exp_val::sampled_exp_val,
sparse_pauli_op::sparse_pauli_op, star_prerouting::star_prerouting,
stochastic_swap::stochastic_swap, synthesis::synthesis, target_transpiler::target,
two_qubit_decompose::two_qubit_decompose, uc_gate::uc_gate, utils::utils,
vf2_layout::vf2_layout,
circuit_library::circuit_library, commutation_analysis::commutation_analysis,
commutation_checker::commutation_checker, convert_2q_block_matrix::convert_2q_block_matrix,
dense_layout::dense_layout, error_map::error_map,
euler_one_qubit_decomposer::euler_one_qubit_decomposer, isometry::isometry, nlayout::nlayout,
optimize_1q_gates::optimize_1q_gates, pauli_exp_val::pauli_expval, results::results,
sabre::sabre, sampled_exp_val::sampled_exp_val, sparse_pauli_op::sparse_pauli_op,
star_prerouting::star_prerouting, stochastic_swap::stochastic_swap, synthesis::synthesis,
target_transpiler::target, two_qubit_decompose::two_qubit_decompose, uc_gate::uc_gate,
utils::utils, vf2_layout::vf2_layout,
};

#[inline(always)]
Expand Down Expand Up @@ -62,5 +62,6 @@ fn _accelerate(m: &Bound<PyModule>) -> PyResult<()> {
add_submodule(m, utils, "utils")?;
add_submodule(m, vf2_layout, "vf2_layout")?;
add_submodule(m, commutation_checker, "commutation_checker")?;
add_submodule(m, commutation_analysis, "commutation_analysis")?;
Ok(())
}
1 change: 1 addition & 0 deletions qiskit/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@
sys.modules["qiskit._accelerate.synthesis.linear"] = _accelerate.synthesis.linear
sys.modules["qiskit._accelerate.synthesis.clifford"] = _accelerate.synthesis.clifford
sys.modules["qiskit._accelerate.commutation_checker"] = _accelerate.commutation_checker
sys.modules["qiskit._accelerate.commutation_analysis"] = _accelerate.commutation_analysis
sys.modules["qiskit._accelerate.synthesis.linear_phase"] = _accelerate.synthesis.linear_phase

from qiskit.exceptions import QiskitError, MissingOptionalLibraryError
Expand Down
59 changes: 10 additions & 49 deletions qiskit/transpiler/passes/optimization/commutation_analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,10 @@

"""Analysis pass to find commutation relations between DAG nodes."""

from collections import defaultdict

from qiskit.circuit.commutation_library import SessionCommutationChecker as scc
from qiskit.dagcircuit import DAGOpNode
from qiskit.circuit.commutation_checker import CommutationChecker as PyCommutationChecker
from qiskit.transpiler.basepasses import AnalysisPass
from qiskit._accelerate.commutation_analysis import analyze_commutations


class CommutationAnalysis(AnalysisPass):
Expand All @@ -33,6 +32,13 @@ def __init__(self, *, _commutation_checker=None):
# do not care about commutations of all gates, but just a subset
if _commutation_checker is None:
_commutation_checker = scc

if isinstance(_commutation_checker, PyCommutationChecker):
# If an instance of the to-be deprecated Python implementation of the CommutationChecker
# is passed, defer to the internally held Rust implementation instead. This is needed
# as the ``analyze_commutations`` function expects the Rust implementation.
_commutation_checker = _commutation_checker.cc

mtreinish marked this conversation as resolved.
Show resolved Hide resolved
self.comm_checker = _commutation_checker

def run(self, dag):
Expand All @@ -42,49 +48,4 @@ def run(self, dag):
into the ``property_set``.
"""
# Initiate the commutation set
self.property_set["commutation_set"] = defaultdict(list)

# Build a dictionary to keep track of the gates on each qubit
# The key with format (wire) will store the lists of commutation sets
# The key with format (node, wire) will store the index of the commutation set
# on the specified wire, thus, for example:
# self.property_set['commutation_set'][wire][(node, wire)] will give the
# commutation set that contains node.

for wire in dag.qubits:
self.property_set["commutation_set"][wire] = []

# Add edges to the dictionary for each qubit
for node in dag.topological_op_nodes():
for _, _, edge_wire in dag.edges(node):
self.property_set["commutation_set"][(node, edge_wire)] = -1

# Construct the commutation set
for wire in dag.qubits:

for current_gate in dag.nodes_on_wire(wire):

current_comm_set = self.property_set["commutation_set"][wire]
if not current_comm_set:
current_comm_set.append([current_gate])

if current_gate not in current_comm_set[-1]:
does_commute = True

# Check if the current gate commutes with all the gates in the current block
for prev_gate in current_comm_set[-1]:
does_commute = (
isinstance(current_gate, DAGOpNode)
and isinstance(prev_gate, DAGOpNode)
and self.comm_checker.commute_nodes(current_gate, prev_gate)
)
if not does_commute:
break

if does_commute:
current_comm_set[-1].append(current_gate)
else:
current_comm_set.append([current_gate])

temp_len = len(current_comm_set)
self.property_set["commutation_set"][(current_gate, wire)] = temp_len - 1
self.property_set["commutation_set"] = analyze_commutations(dag, self.comm_checker)
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
features_transpiler:
- |
Added a Rust implementation of :class:`.CommutationAnalysis` in :func:`.analyze_commutations`.
Loading