forked from Qiskit/qiskit
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'Qiskit:main' into mlapson/deprecate_cu1
- Loading branch information
Showing
30 changed files
with
1,209 additions
and
455 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
// 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 hashbrown::HashSet; | ||
use pyo3::intern; | ||
use pyo3::prelude::*; | ||
use pyo3::wrap_pyfunction; | ||
|
||
use qiskit_circuit::circuit_data::CircuitData; | ||
use qiskit_circuit::dag_circuit::{DAGCircuit, NodeType}; | ||
use qiskit_circuit::imports::CIRCUIT_TO_DAG; | ||
use qiskit_circuit::operations::{Operation, OperationRef}; | ||
use qiskit_circuit::Qubit; | ||
|
||
fn recurse<'py>( | ||
py: Python<'py>, | ||
dag: &'py DAGCircuit, | ||
edge_set: &'py HashSet<[u32; 2]>, | ||
wire_map: Option<&'py [Qubit]>, | ||
) -> PyResult<Option<(String, [u32; 2])>> { | ||
let check_qubits = |qubits: &[Qubit]| -> bool { | ||
match wire_map { | ||
Some(wire_map) => { | ||
let mapped_bits = [ | ||
wire_map[qubits[0].0 as usize], | ||
wire_map[qubits[1].0 as usize], | ||
]; | ||
edge_set.contains(&[mapped_bits[0].into(), mapped_bits[1].into()]) | ||
} | ||
None => edge_set.contains(&[qubits[0].into(), qubits[1].into()]), | ||
} | ||
}; | ||
for node in dag.op_nodes(false) { | ||
if let NodeType::Operation(inst) = &dag.dag[node] { | ||
let qubits = dag.get_qargs(inst.qubits); | ||
if inst.op.control_flow() { | ||
if let OperationRef::Instruction(py_inst) = inst.op.view() { | ||
let raw_blocks = py_inst.instruction.getattr(py, "blocks")?; | ||
let circuit_to_dag = CIRCUIT_TO_DAG.get_bound(py); | ||
for raw_block in raw_blocks.bind(py).iter().unwrap() { | ||
let block_obj = raw_block?; | ||
let block = block_obj | ||
.getattr(intern!(py, "_data"))? | ||
.downcast::<CircuitData>()? | ||
.borrow(); | ||
let new_dag: DAGCircuit = | ||
circuit_to_dag.call1((block_obj.clone(),))?.extract()?; | ||
let wire_map = (0..block.num_qubits()) | ||
.map(|inner| { | ||
let outer = qubits[inner]; | ||
match wire_map { | ||
Some(wire_map) => wire_map[outer.0 as usize], | ||
None => outer, | ||
} | ||
}) | ||
.collect::<Vec<_>>(); | ||
let res = recurse(py, &new_dag, edge_set, Some(&wire_map))?; | ||
if res.is_some() { | ||
return Ok(res); | ||
} | ||
} | ||
} | ||
} else if qubits.len() == 2 | ||
&& (dag.calibrations_empty() || !dag.has_calibration_for_index(py, node)?) | ||
&& !check_qubits(qubits) | ||
{ | ||
return Ok(Some(( | ||
inst.op.name().to_string(), | ||
[qubits[0].0, qubits[1].0], | ||
))); | ||
} | ||
} | ||
} | ||
Ok(None) | ||
} | ||
|
||
#[pyfunction] | ||
pub fn check_map( | ||
py: Python, | ||
dag: &DAGCircuit, | ||
edge_set: HashSet<[u32; 2]>, | ||
) -> PyResult<Option<(String, [u32; 2])>> { | ||
recurse(py, dag, &edge_set, None) | ||
} | ||
|
||
pub fn check_map_mod(m: &Bound<PyModule>) -> PyResult<()> { | ||
m.add_wrapped(wrap_pyfunction!(check_map))?; | ||
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
// 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::prelude::*; | ||
use pyo3::wrap_pyfunction; | ||
|
||
use qiskit_circuit::dag_circuit::DAGCircuit; | ||
use qiskit_circuit::packed_instruction::PackedInstruction; | ||
use rustworkx_core::petgraph::stable_graph::NodeIndex; | ||
|
||
#[pyfunction] | ||
#[pyo3(name = "filter_op_nodes")] | ||
pub fn py_filter_op_nodes( | ||
py: Python, | ||
dag: &mut DAGCircuit, | ||
predicate: &Bound<PyAny>, | ||
) -> PyResult<()> { | ||
let callable = |node: NodeIndex| -> PyResult<bool> { | ||
let dag_op_node = dag.get_node(py, node)?; | ||
predicate.call1((dag_op_node,))?.extract() | ||
}; | ||
let mut remove_nodes: Vec<NodeIndex> = Vec::new(); | ||
for node in dag.op_nodes(true) { | ||
if !callable(node)? { | ||
remove_nodes.push(node); | ||
} | ||
} | ||
for node in remove_nodes { | ||
dag.remove_op_node(node); | ||
} | ||
Ok(()) | ||
} | ||
|
||
/// Remove any nodes that have the provided label set | ||
/// | ||
/// Args: | ||
/// dag (DAGCircuit): The dag circuit to filter the ops from | ||
/// label (str): The label to filter nodes on | ||
#[pyfunction] | ||
pub fn filter_labeled_op(dag: &mut DAGCircuit, label: String) { | ||
let predicate = |node: &PackedInstruction| -> bool { | ||
match node.label() { | ||
Some(inst_label) => inst_label != label, | ||
None => false, | ||
} | ||
}; | ||
dag.filter_op_nodes(predicate); | ||
} | ||
|
||
pub fn filter_op_nodes_mod(m: &Bound<PyModule>) -> PyResult<()> { | ||
m.add_wrapped(wrap_pyfunction!(py_filter_op_nodes))?; | ||
m.add_wrapped(wrap_pyfunction!(filter_labeled_op))?; | ||
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
// 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 crate::nlayout::PhysicalQubit; | ||
use crate::target_transpiler::Target; | ||
use hashbrown::HashSet; | ||
use pyo3::prelude::*; | ||
use qiskit_circuit::imports; | ||
use qiskit_circuit::operations::OperationRef; | ||
use qiskit_circuit::{ | ||
dag_circuit::{DAGCircuit, NodeType}, | ||
operations::Operation, | ||
packed_instruction::PackedInstruction, | ||
Qubit, | ||
}; | ||
use smallvec::smallvec; | ||
|
||
/// Check if the two-qubit gates follow the right direction with respect to the coupling map. | ||
/// | ||
/// Args: | ||
/// dag: the DAGCircuit to analyze | ||
/// | ||
/// coupling_edges: set of edge pairs representing a directed coupling map, against which gate directionality is checked | ||
/// | ||
/// Returns: | ||
/// true iff all two-qubit gates comply with the coupling constraints | ||
#[pyfunction] | ||
#[pyo3(name = "check_gate_direction_coupling")] | ||
fn py_check_with_coupling_map( | ||
py: Python, | ||
dag: &DAGCircuit, | ||
coupling_edges: HashSet<[Qubit; 2]>, | ||
) -> PyResult<bool> { | ||
let coupling_map_check = | ||
|_: &PackedInstruction, op_args: &[Qubit]| -> bool { coupling_edges.contains(op_args) }; | ||
|
||
check_gate_direction(py, dag, &coupling_map_check, None) | ||
} | ||
|
||
/// Check if the two-qubit gates follow the right direction with respect to instructions supported in the given target. | ||
/// | ||
/// Args: | ||
/// dag: the DAGCircuit to analyze | ||
/// | ||
/// target: the Target against which gate directionality compliance is checked | ||
/// | ||
/// Returns: | ||
/// true iff all two-qubit gates comply with the target's coupling constraints | ||
#[pyfunction] | ||
#[pyo3(name = "check_gate_direction_target")] | ||
fn py_check_with_target(py: Python, dag: &DAGCircuit, target: &Target) -> PyResult<bool> { | ||
let target_check = |inst: &PackedInstruction, op_args: &[Qubit]| -> bool { | ||
let qargs = smallvec![ | ||
PhysicalQubit::new(op_args[0].0), | ||
PhysicalQubit::new(op_args[1].0) | ||
]; | ||
|
||
target.instruction_supported(inst.op.name(), Some(&qargs)) | ||
}; | ||
|
||
check_gate_direction(py, dag, &target_check, None) | ||
} | ||
|
||
// The main routine for checking gate directionality. | ||
// | ||
// gate_complies: a function returning true iff the two-qubit gate direction complies with directionality constraints | ||
// | ||
// qubit_mapping: used for mapping the index of a given qubit within an instruction qargs vector to the corresponding qubit index of the | ||
// original DAGCircuit the pass was called with. This mapping is required since control flow blocks are represented by nested DAGCircuit | ||
// objects whose instruction qubit indices are relative to the parent DAGCircuit they reside in, thus when we recurse into nested DAGs, we need | ||
// to carry the mapping context relative to the original DAG. | ||
// When qubit_mapping is None, the identity mapping is assumed | ||
fn check_gate_direction<T>( | ||
py: Python, | ||
dag: &DAGCircuit, | ||
gate_complies: &T, | ||
qubit_mapping: Option<&[Qubit]>, | ||
) -> PyResult<bool> | ||
where | ||
T: Fn(&PackedInstruction, &[Qubit]) -> bool, | ||
{ | ||
for node in dag.op_nodes(false) { | ||
let NodeType::Operation(packed_inst) = &dag.dag[node] else { | ||
panic!("PackedInstruction is expected"); | ||
}; | ||
|
||
let inst_qargs = dag.get_qargs(packed_inst.qubits); | ||
|
||
if let OperationRef::Instruction(py_inst) = packed_inst.op.view() { | ||
if py_inst.control_flow() { | ||
let circuit_to_dag = imports::CIRCUIT_TO_DAG.get_bound(py); // TODO: Take out of the recursion | ||
let py_inst = py_inst.instruction.bind(py); | ||
|
||
for block in py_inst.getattr("blocks")?.iter()? { | ||
let inner_dag: DAGCircuit = circuit_to_dag.call1((block?,))?.extract()?; | ||
|
||
let block_ok = if let Some(mapping) = qubit_mapping { | ||
let mapping = inst_qargs // Create a temp mapping for the recursive call | ||
.iter() | ||
.map(|q| mapping[q.0 as usize]) | ||
.collect::<Vec<Qubit>>(); | ||
|
||
check_gate_direction(py, &inner_dag, gate_complies, Some(&mapping))? | ||
} else { | ||
check_gate_direction(py, &inner_dag, gate_complies, Some(inst_qargs))? | ||
}; | ||
|
||
if !block_ok { | ||
return Ok(false); | ||
} | ||
} | ||
continue; | ||
} | ||
} | ||
|
||
if inst_qargs.len() == 2 | ||
&& !match qubit_mapping { | ||
// Check gate direction based either on a given custom mapping or the identity mapping | ||
Some(mapping) => gate_complies( | ||
packed_inst, | ||
&[ | ||
mapping[inst_qargs[0].0 as usize], | ||
mapping[inst_qargs[1].0 as usize], | ||
], | ||
), | ||
None => gate_complies(packed_inst, inst_qargs), | ||
} | ||
{ | ||
return Ok(false); | ||
} | ||
} | ||
|
||
Ok(true) | ||
} | ||
|
||
#[pymodule] | ||
pub fn gate_direction(m: &Bound<PyModule>) -> PyResult<()> { | ||
m.add_wrapped(wrap_pyfunction!(py_check_with_coupling_map))?; | ||
m.add_wrapped(wrap_pyfunction!(py_check_with_target))?; | ||
Ok(()) | ||
} |
Oops, something went wrong.