Skip to content

Commit

Permalink
Merge branch 'main' into check-gate-direction
Browse files Browse the repository at this point in the history
Also changing functions order in gate_direction.rs and adding Returns:
doc
  • Loading branch information
eliarbel committed Aug 28, 2024
2 parents 0b9c0d2 + 7b2d50c commit 69d6533
Show file tree
Hide file tree
Showing 5 changed files with 304 additions and 233 deletions.
114 changes: 60 additions & 54 deletions crates/accelerate/src/gate_direction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,66 @@ use qiskit_circuit::{
Qubit,
};

/// 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: &Bound<PySet>,
) -> PyResult<bool> {
let coupling_map_check =
|curr_dag: &DAGCircuit, _: &PackedInstruction, op_args: &[Qubit]| -> bool {
coupling_edges
.contains((
map_qubit(py, dag, curr_dag, op_args[0]).0,
map_qubit(py, dag, curr_dag, op_args[1]).0,
))
.unwrap_or(false)
};

check_gate_direction(py, dag, &coupling_map_check)
}

/// 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: &Bound<Target>) -> PyResult<bool> {
let target = target.borrow();

let target_check =
|curr_dag: &DAGCircuit, inst: &PackedInstruction, op_args: &[Qubit]| -> bool {
let mut qargs = Qargs::new();

qargs.push(PhysicalQubit::new(
map_qubit(py, dag, curr_dag, op_args[0]).0,
));
qargs.push(PhysicalQubit::new(
map_qubit(py, dag, curr_dag, op_args[1]).0,
));

target.instruction_supported(inst.op.name(), Some(&qargs))
};

check_gate_direction(py, dag, &target_check)
}

// Handle a control flow instruction, namely check recursively into its circuit blocks
fn check_gate_direction_control_flow<T>(
py: Python,
Expand Down Expand Up @@ -91,60 +151,6 @@ fn map_qubit(py: Python, orig_dag: &DAGCircuit, curr_dag: &DAGCircuit, qubit: Qu
orig_dag.qubits.find(qubit).expect("Qubit in orig_dag")
}

/// 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
#[pyfunction]
#[pyo3(name = "check_gate_direction_coupling")]
fn py_check_with_coupling_map(
py: Python,
dag: &DAGCircuit,
coupling_edges: &Bound<PySet>,
) -> PyResult<bool> {
let coupling_map_check =
|curr_dag: &DAGCircuit, _: &PackedInstruction, op_args: &[Qubit]| -> bool {
coupling_edges
.contains((
map_qubit(py, dag, curr_dag, op_args[0]).0,
map_qubit(py, dag, curr_dag, op_args[1]).0,
))
.unwrap_or(false)
};

check_gate_direction(py, dag, &coupling_map_check)
}

/// 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
#[pyfunction]
#[pyo3(name = "check_gate_direction_target")]
fn py_check_with_target(py: Python, dag: &DAGCircuit, target: &Bound<Target>) -> PyResult<bool> {
let target = target.borrow();

let target_check =
|curr_dag: &DAGCircuit, inst: &PackedInstruction, op_args: &[Qubit]| -> bool {
let mut qargs = Qargs::new();

qargs.push(PhysicalQubit::new(
map_qubit(py, dag, curr_dag, op_args[0]).0,
));
qargs.push(PhysicalQubit::new(
map_qubit(py, dag, curr_dag, op_args[1]).0,
));

target.instruction_supported(inst.op.name(), Some(&qargs))
};

check_gate_direction(py, dag, &target_check)
}

#[pymodule]
pub fn gate_direction(m: &Bound<PyModule>) -> PyResult<()> {
m.add_wrapped(wrap_pyfunction!(py_check_with_coupling_map))?;
Expand Down
70 changes: 34 additions & 36 deletions crates/circuit/src/circuit_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use std::cell::OnceCell;
use crate::bit_data::BitData;
use crate::circuit_instruction::{CircuitInstruction, OperationFromPython};
use crate::imports::{ANNOTATED_OPERATION, CLBIT, QUANTUM_CIRCUIT, QUBIT};
use crate::interner::{Index, IndexedInterner, Interner};
use crate::interner::{Interned, Interner};
use crate::operations::{Operation, OperationRef, Param, StandardGate};
use crate::packed_instruction::{PackedInstruction, PackedOperation};
use crate::parameter_table::{ParameterTable, ParameterTableError, ParameterUse, ParameterUuid};
Expand Down Expand Up @@ -91,9 +91,9 @@ pub struct CircuitData {
/// The packed instruction listing.
data: Vec<PackedInstruction>,
/// The cache used to intern instruction bits.
qargs_interner: IndexedInterner<Vec<Qubit>>,
qargs_interner: Interner<[Qubit]>,
/// The cache used to intern instruction bits.
cargs_interner: IndexedInterner<Vec<Clbit>>,
cargs_interner: Interner<[Clbit]>,
/// Qubits registered in the circuit.
qubits: BitData<Qubit>,
/// Clbits registered in the circuit.
Expand Down Expand Up @@ -148,8 +148,8 @@ impl CircuitData {
global_phase,
)?;
for (operation, params, qargs, cargs) in instruction_iter {
let qubits = (&mut res.qargs_interner).intern(qargs)?;
let clbits = (&mut res.cargs_interner).intern(cargs)?;
let qubits = res.qargs_interner.insert_owned(qargs);
let clbits = res.cargs_interner.insert_owned(cargs);
let params = (!params.is_empty()).then(|| Box::new(params));
res.data.push(PackedInstruction {
op: operation,
Expand Down Expand Up @@ -199,9 +199,9 @@ impl CircuitData {
instruction_iter.size_hint().0,
global_phase,
)?;
let no_clbit_index = (&mut res.cargs_interner).intern(Vec::new())?;
let no_clbit_index = res.cargs_interner.insert(&[]);
for (operation, params, qargs) in instruction_iter {
let qubits = (&mut res.qargs_interner).intern(qargs.to_vec())?;
let qubits = res.qargs_interner.insert(&qargs);
let params = (!params.is_empty()).then(|| Box::new(params));
res.data.push(PackedInstruction {
op: operation.into(),
Expand All @@ -227,8 +227,8 @@ impl CircuitData {
) -> PyResult<Self> {
let mut res = CircuitData {
data: Vec::with_capacity(instruction_capacity),
qargs_interner: IndexedInterner::new(),
cargs_interner: IndexedInterner::new(),
qargs_interner: Interner::new(),
cargs_interner: Interner::new(),
qubits: BitData::new(py, "qubits".to_string()),
clbits: BitData::new(py, "clbits".to_string()),
param_table: ParameterTable::new(),
Expand Down Expand Up @@ -258,9 +258,9 @@ impl CircuitData {
params: &[Param],
qargs: &[Qubit],
) -> PyResult<()> {
let no_clbit_index = (&mut self.cargs_interner).intern(Vec::new())?;
let no_clbit_index = self.cargs_interner.insert(&[]);
let params = (!params.is_empty()).then(|| Box::new(params.iter().cloned().collect()));
let qubits = (&mut self.qargs_interner).intern(qargs.to_vec())?;
let qubits = self.qargs_interner.insert(qargs);
self.data.push(PackedInstruction {
op: operation.into(),
qubits,
Expand Down Expand Up @@ -351,8 +351,8 @@ impl CircuitData {
) -> PyResult<Self> {
let mut self_ = CircuitData {
data: Vec::new(),
qargs_interner: IndexedInterner::new(),
cargs_interner: IndexedInterner::new(),
qargs_interner: Interner::new(),
cargs_interner: Interner::new(),
qubits: BitData::new(py, "qubits".to_string()),
clbits: BitData::new(py, "clbits".to_string()),
param_table: ParameterTable::new(),
Expand Down Expand Up @@ -572,10 +572,10 @@ impl CircuitData {
let qubits = PySet::empty_bound(py)?;
let clbits = PySet::empty_bound(py)?;
for inst in self.data.iter() {
for b in self.qargs_interner.intern(inst.qubits) {
for b in self.qargs_interner.get(inst.qubits) {
qubits.add(self.qubits.get(*b).unwrap().clone_ref(py))?;
}
for b in self.cargs_interner.intern(inst.clbits) {
for b in self.cargs_interner.get(inst.clbits) {
clbits.add(self.clbits.get(*b).unwrap().clone_ref(py))?;
}
}
Expand Down Expand Up @@ -737,8 +737,8 @@ impl CircuitData {
// Get a single item, assuming the index is validated as in bounds.
let get_single = |index: usize| {
let inst = &self.data[index];
let qubits = self.qargs_interner.intern(inst.qubits);
let clbits = self.cargs_interner.intern(inst.clbits);
let qubits = self.qargs_interner.get(inst.qubits);
let clbits = self.cargs_interner.get(inst.clbits);
CircuitInstruction {
operation: inst.op.clone(),
qubits: PyTuple::new_bound(py, self.qubits.map_indices(qubits)).unbind(),
Expand Down Expand Up @@ -894,7 +894,7 @@ impl CircuitData {
for inst in other.data.iter() {
let qubits = other
.qargs_interner
.intern(inst.qubits)
.get(inst.qubits)
.iter()
.map(|b| {
Ok(self
Expand All @@ -905,7 +905,7 @@ impl CircuitData {
.collect::<PyResult<Vec<Qubit>>>()?;
let clbits = other
.cargs_interner
.intern(inst.clbits)
.get(inst.clbits)
.iter()
.map(|b| {
Ok(self
Expand All @@ -915,8 +915,8 @@ impl CircuitData {
})
.collect::<PyResult<Vec<Clbit>>>()?;
let new_index = self.data.len();
let qubits_id = Interner::intern(&mut self.qargs_interner, qubits)?;
let clbits_id = Interner::intern(&mut self.cargs_interner, clbits)?;
let qubits_id = self.qargs_interner.insert_owned(qubits);
let clbits_id = self.cargs_interner.insert_owned(clbits);
self.data.push(PackedInstruction {
op: inst.op.clone(),
qubits: qubits_id,
Expand Down Expand Up @@ -1113,14 +1113,12 @@ impl CircuitData {
}

fn pack(&mut self, py: Python, inst: &CircuitInstruction) -> PyResult<PackedInstruction> {
let qubits = Interner::intern(
&mut self.qargs_interner,
self.qubits.map_bits(inst.qubits.bind(py))?.collect(),
)?;
let clbits = Interner::intern(
&mut self.cargs_interner,
self.clbits.map_bits(inst.clbits.bind(py))?.collect(),
)?;
let qubits = self
.qargs_interner
.insert_owned(self.qubits.map_bits(inst.qubits.bind(py))?.collect());
let clbits = self
.cargs_interner
.insert_owned(self.clbits.map_bits(inst.clbits.bind(py))?.collect());
Ok(PackedInstruction {
op: inst.operation.clone(),
qubits,
Expand All @@ -1138,12 +1136,12 @@ impl CircuitData {
}

/// Returns an immutable view of the Interner used for Qargs
pub fn qargs_interner(&self) -> &IndexedInterner<Vec<Qubit>> {
pub fn qargs_interner(&self) -> &Interner<[Qubit]> {
&self.qargs_interner
}

/// Returns an immutable view of the Interner used for Cargs
pub fn cargs_interner(&self) -> &IndexedInterner<Vec<Clbit>> {
pub fn cargs_interner(&self) -> &Interner<[Clbit]> {
&self.cargs_interner
}

Expand All @@ -1162,14 +1160,14 @@ impl CircuitData {
&self.clbits
}

/// Unpacks from InternerIndex to `[Qubit]`
pub fn get_qargs(&self, index: Index) -> &[Qubit] {
self.qargs_interner().intern(index)
/// Unpacks from interned value to `[Qubit]`
pub fn get_qargs(&self, index: Interned<[Qubit]>) -> &[Qubit] {
self.qargs_interner().get(index)
}

/// Unpacks from InternerIndex to `[Clbit]`
pub fn get_cargs(&self, index: Index) -> &[Clbit] {
self.cargs_interner().intern(index)
pub fn get_cargs(&self, index: Interned<[Clbit]>) -> &[Clbit] {
self.cargs_interner().get(index)
}

fn assign_parameters_inner<I>(&mut self, py: Python, iter: I) -> PyResult<()>
Expand Down
Loading

0 comments on commit 69d6533

Please sign in to comment.