-
Notifications
You must be signed in to change notification settings - Fork 2.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Generalize the pass for all gates and rewrite in rust
This pivots the pass to work for all gates by checking all the parameters in the standard gate library are within the specified tolerance, and the matrix is equivalent to an identity for any other gate defined in Python (with a matrix defined). To improve the performance of the pass it is written in rust now. Additionally the class is named to RemoveIdentityEquivalent to make the purpose of the pass slightly more clear.
- Loading branch information
Showing
10 changed files
with
209 additions
and
136 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
// 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 num_complex::Complex64; | ||
use num_complex::ComplexFloat; | ||
use pyo3::prelude::*; | ||
use rustworkx_core::petgraph::stable_graph::NodeIndex; | ||
|
||
use crate::nlayout::PhysicalQubit; | ||
use crate::target_transpiler::Target; | ||
use qiskit_circuit::dag_circuit::DAGCircuit; | ||
use qiskit_circuit::operations::Operation; | ||
use qiskit_circuit::operations::OperationRef; | ||
use qiskit_circuit::packed_instruction::PackedInstruction; | ||
|
||
#[pyfunction] | ||
#[pyo3(signature=(dag, approx_degree=Some(1.0), target=None))] | ||
fn remove_identity_equiv( | ||
dag: &mut DAGCircuit, | ||
approx_degree: Option<f64>, | ||
target: Option<&Target>, | ||
) { | ||
let mut remove_list: Vec<NodeIndex> = Vec::new(); | ||
|
||
let get_error_cutoff = |inst: &PackedInstruction| -> f64 { | ||
match approx_degree { | ||
Some(degree) => { | ||
if degree == 1.0 { | ||
f64::EPSILON | ||
} else { | ||
match target { | ||
Some(target) => { | ||
let qargs: Vec<PhysicalQubit> = dag | ||
.get_qargs(inst.qubits) | ||
.iter() | ||
.map(|x| PhysicalQubit::new(x.0)) | ||
.collect(); | ||
let error_rate = target.get_error(inst.op.name(), qargs.as_slice()); | ||
match error_rate { | ||
Some(err) => err * degree, | ||
None => degree, | ||
} | ||
} | ||
None => degree, | ||
} | ||
} | ||
} | ||
None => match target { | ||
Some(target) => { | ||
let qargs: Vec<PhysicalQubit> = dag | ||
.get_qargs(inst.qubits) | ||
.iter() | ||
.map(|x| PhysicalQubit::new(x.0)) | ||
.collect(); | ||
let error_rate = target.get_error(inst.op.name(), qargs.as_slice()); | ||
match error_rate { | ||
Some(err) => err, | ||
None => f64::EPSILON, | ||
} | ||
} | ||
None => f64::EPSILON, | ||
}, | ||
} | ||
}; | ||
|
||
for op_node in dag.op_nodes(false) { | ||
let inst = dag.dag()[op_node].unwrap_operation(); | ||
match inst.op.view() { | ||
OperationRef::Standard(gate) => { | ||
if let Some(matrix) = gate.matrix(inst.params_view()) { | ||
let error = get_error_cutoff(inst); | ||
let dim = matrix.shape()[0] as f64; | ||
let trace: Complex64 = matrix.diag().iter().sum(); | ||
let f_pro = (trace / dim).abs().powi(2); | ||
let gate_fidelity = (dim * f_pro + 1.) / (dim + 1.); | ||
if 1. - gate_fidelity < error { | ||
remove_list.push(op_node) | ||
} | ||
} | ||
} | ||
OperationRef::Gate(gate) => { | ||
if let Some(matrix) = gate.matrix(inst.params_view()) { | ||
let error = get_error_cutoff(inst); | ||
let dim = matrix.shape()[0] as f64; | ||
let trace: Complex64 = matrix.diag().iter().sum(); | ||
let f_pro = (trace / dim).abs().powi(2); | ||
let gate_fidelity = (dim * f_pro + 1.) / (dim + 1.); | ||
if 1. - gate_fidelity < error { | ||
remove_list.push(op_node) | ||
} | ||
} | ||
} | ||
_ => continue, | ||
} | ||
} | ||
for node in remove_list { | ||
dag.remove_op_node(node); | ||
} | ||
} | ||
|
||
pub fn remove_identity_equiv_mod(m: &Bound<PyModule>) -> PyResult<()> { | ||
m.add_wrapped(wrap_pyfunction!(remove_identity_equiv))?; | ||
Ok(()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
100 changes: 0 additions & 100 deletions
100
qiskit/transpiler/passes/optimization/drop_negligible.py
This file was deleted.
Oops, something went wrong.
56 changes: 56 additions & 0 deletions
56
qiskit/transpiler/passes/optimization/remove_identity_equiv.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
# 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. | ||
|
||
"""Transpiler pass to drop gates with negligible effects.""" | ||
|
||
from __future__ import annotations | ||
|
||
from qiskit.dagcircuit import DAGCircuit | ||
from qiskit.transpiler.target import Target | ||
from qiskit.transpiler.basepasses import TransformationPass | ||
from qiskit._accelerate.remove_identity_equiv import remove_identity_equiv | ||
|
||
|
||
class RemoveIdentityEquivalent(TransformationPass): | ||
"""Remove gates with negligible effects. | ||
Removes gates whose effect is close to an identity operation, up to the specified | ||
tolerance. | ||
""" | ||
|
||
def __init__( | ||
self, *, approximation_degree: float | None = 1.0, target: None | Target = None | ||
) -> None: | ||
"""Initialize the transpiler pass. | ||
Args: | ||
approximation_degree: The degree to approximate for the equivalence check. This can be a | ||
floating point value between 0 and 1, or ``None``. If the value is 1 this does not | ||
approximate above floating point precision. For a value < 1 this is used as a scaling | ||
factor for the target fidelity. If the value is ``None`` this approximates up to the | ||
fidelity for the gate specified in ``target``. | ||
target: If ``approximation_degree`` is set to ``None`` and a :class:`.Target` is provided | ||
for this field the tolerance for determining whether an operation is equivalent to | ||
identity will be set to the reported error rate in the target. If | ||
``approximation_degree`` (the default) this has no effect, if | ||
``approximation_degree=None`` it uses the error rate specified in the ``Target`` for | ||
the gate being evaluated, and a numeric value other than 1 with ``target`` set is | ||
used as a scaling factor of the target's error rate. | ||
""" | ||
super().__init__() | ||
self._approximation_degree = approximation_degree | ||
self._target = target | ||
|
||
def run(self, dag: DAGCircuit) -> DAGCircuit: | ||
remove_identity_equiv(dag, self._approximation_degree, self._target) | ||
return dag |
29 changes: 29 additions & 0 deletions
29
releasenotes/notes/remove_identity_equiv-9c627c8c35b2298a.yaml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
--- | ||
features_transpiler: | ||
- | | ||
Added a new transpiler pass, :class:`.RemoveIdentityEquivalent` that is used | ||
to remove gates that are equivalent to an identity up to some tolerance. | ||
For example if you had a circuit like: | ||
.. plot:: | ||
from qiskit.circuit import QuantumCircuit | ||
qc = QuantumCircuit(2) | ||
qc.cp(1e-20, [0, 1]) | ||
qc.draw("mpl") | ||
running the pass would eliminate the :class:`.CPhaseGate`: | ||
.. plot:: | ||
:include-source: | ||
from qiskit.circuit import QuantumCircuit | ||
from qiskit.transpiler.passes import RemoveIdentityEquivalent | ||
qc = QuantumCircuit(2) | ||
qc.cp(1e-20, [0, 1]) | ||
removal_pass = RemoveIdentityEquivalent() | ||
result = removal_pass(qc) | ||
result.draw("mpl") |
Oops, something went wrong.