|
| 1 | +// This code is part of Qiskit. |
| 2 | +// |
| 3 | +// (C) Copyright IBM 2024 |
| 4 | +// |
| 5 | +// This code is licensed under the Apache License, Version 2.0. You may |
| 6 | +// obtain a copy of this license in the LICENSE.txt file in the root directory |
| 7 | +// of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. |
| 8 | +// |
| 9 | +// Any modifications or derivative works of this code must retain this |
| 10 | +// copyright notice, and modified files need to carry a notice indicating |
| 11 | +// that they have been altered from the originals. |
| 12 | + |
| 13 | +use numpy::PyReadonlyArray1; |
| 14 | +use pyo3::prelude::*; |
| 15 | + |
| 16 | +use qiskit_circuit::dag_circuit::{DAGCircuit, NodeType}; |
| 17 | +use qiskit_circuit::operations::{Operation, Param}; |
| 18 | +use qiskit_circuit::Qubit; |
| 19 | + |
| 20 | +/// Run the ElidePermutations pass on `dag`. |
| 21 | +/// Args: |
| 22 | +/// dag (DAGCircuit): the DAG to be optimized. |
| 23 | +/// Returns: |
| 24 | +/// An `Option`: the value of `None` indicates that no optimization was |
| 25 | +/// performed and the original `dag` should be used, otherwise it's a |
| 26 | +/// tuple consisting of the optimized DAG and the induced qubit permutation. |
| 27 | +#[pyfunction] |
| 28 | +fn run(py: Python, dag: &mut DAGCircuit) -> PyResult<Option<(DAGCircuit, Vec<usize>)>> { |
| 29 | + let permutation_gate_names = ["swap".to_string(), "permutation".to_string()]; |
| 30 | + let op_counts = dag.count_ops(py, false)?; |
| 31 | + if !permutation_gate_names |
| 32 | + .iter() |
| 33 | + .any(|name| op_counts.contains_key(name)) |
| 34 | + { |
| 35 | + return Ok(None); |
| 36 | + } |
| 37 | + let mut mapping: Vec<usize> = (0..dag.num_qubits()).collect(); |
| 38 | + |
| 39 | + // note that DAGCircuit::copy_empty_like clones the interners |
| 40 | + let mut new_dag = dag.copy_empty_like(py, "alike")?; |
| 41 | + for node_index in dag.topological_op_nodes()? { |
| 42 | + if let NodeType::Operation(inst) = &dag.dag()[node_index] { |
| 43 | + match (inst.op.name(), inst.condition()) { |
| 44 | + ("swap", None) => { |
| 45 | + let qargs = dag.get_qargs(inst.qubits); |
| 46 | + let index0 = qargs[0].0 as usize; |
| 47 | + let index1 = qargs[1].0 as usize; |
| 48 | + mapping.swap(index0, index1); |
| 49 | + } |
| 50 | + ("permutation", None) => { |
| 51 | + if let Param::Obj(ref pyobj) = inst.params.as_ref().unwrap()[0] { |
| 52 | + let pyarray: PyReadonlyArray1<i32> = pyobj.extract(py)?; |
| 53 | + let pattern = pyarray.as_array(); |
| 54 | + |
| 55 | + let qindices: Vec<usize> = dag |
| 56 | + .get_qargs(inst.qubits) |
| 57 | + .iter() |
| 58 | + .map(|q| q.0 as usize) |
| 59 | + .collect(); |
| 60 | + |
| 61 | + let remapped_qindices: Vec<usize> = (0..qindices.len()) |
| 62 | + .map(|i| pattern[i]) |
| 63 | + .map(|i| qindices[i as usize]) |
| 64 | + .collect(); |
| 65 | + |
| 66 | + qindices |
| 67 | + .iter() |
| 68 | + .zip(remapped_qindices.iter()) |
| 69 | + .for_each(|(old, new)| { |
| 70 | + mapping[*old] = *new; |
| 71 | + }); |
| 72 | + } else { |
| 73 | + unreachable!(); |
| 74 | + } |
| 75 | + } |
| 76 | + _ => { |
| 77 | + // General instruction |
| 78 | + let qargs = dag.get_qargs(inst.qubits); |
| 79 | + let cargs = dag.get_cargs(inst.clbits); |
| 80 | + let mapped_qargs: Vec<Qubit> = qargs |
| 81 | + .iter() |
| 82 | + .map(|q| q.0 as usize) |
| 83 | + .map(|q| mapping[q]) |
| 84 | + .map(|q| Qubit(q.try_into().unwrap())) |
| 85 | + .collect(); |
| 86 | + |
| 87 | + new_dag.apply_operation_back( |
| 88 | + py, |
| 89 | + inst.op.clone(), |
| 90 | + &mapped_qargs, |
| 91 | + cargs, |
| 92 | + inst.params.as_deref().cloned(), |
| 93 | + inst.extra_attrs.clone(), |
| 94 | + #[cfg(feature = "cache_pygates")] |
| 95 | + None, |
| 96 | + )?; |
| 97 | + } |
| 98 | + } |
| 99 | + } else { |
| 100 | + unreachable!(); |
| 101 | + } |
| 102 | + } |
| 103 | + Ok(Some((new_dag, mapping))) |
| 104 | +} |
| 105 | + |
| 106 | +pub fn elide_permutations(m: &Bound<PyModule>) -> PyResult<()> { |
| 107 | + m.add_wrapped(wrap_pyfunction!(run))?; |
| 108 | + Ok(()) |
| 109 | +} |
0 commit comments