Skip to content

Commit 32ac8c3

Browse files
Add QkDag instruction appliers and getter to C API (#15313)
* Add `QkDag` instruction appliers and getter to C API This adds `qk_dag_get_instruction` as a mirror to `qk_circuit_get_instruction` (unifying the code in anticipation of making it lower-allocation in the future), and the standard-instruction application functions `qk_dag_apply_measure`, `qk_dag_apply_barrier` and `qk_dag_apply_reset`. We're still missing `qk_dag_apply_unitary`, but that's for a different commit. * Update crates/cext/src/dag.rs * Fix incorrect cross references Co-authored-by: Raynel Sanchez <[email protected]> * Include examples in documentation Co-authored-by: Raynel Sanchez <[email protected]> --------- Co-authored-by: Raynel Sanchez <[email protected]>
1 parent 80250c2 commit 32ac8c3

File tree

3 files changed

+424
-58
lines changed

3 files changed

+424
-58
lines changed

crates/cext/src/circuit.rs

Lines changed: 48 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,11 @@ use qiskit_circuit::bit::{ClassicalRegister, QuantumRegister};
2323
use qiskit_circuit::bit::{ShareableClbit, ShareableQubit};
2424
use qiskit_circuit::circuit_data::CircuitData;
2525
use qiskit_circuit::dag_circuit::DAGCircuit;
26+
use qiskit_circuit::interner::Interner;
2627
use qiskit_circuit::operations::{
2728
ArrayType, DelayUnit, Operation, Param, StandardGate, StandardInstruction, UnitaryGate,
2829
};
29-
use qiskit_circuit::packed_instruction::PackedOperation;
30+
use qiskit_circuit::packed_instruction::{PackedInstruction, PackedOperation};
3031
use qiskit_circuit::{Clbit, Qubit};
3132

3233
#[cfg(feature = "python_binding")]
@@ -876,6 +877,44 @@ pub struct CInstruction {
876877
/// The number of parameters for this instruction.
877878
num_params: u32,
878879
}
880+
impl CInstruction {
881+
/// Create a `CInstruction` that owns pointers to copies of the information in the given
882+
/// `PackedInstruction`.
883+
///
884+
/// This must be cleared by a call to `qk_circuit_instruction_clear` to avoid leaking its
885+
/// allocations.
886+
///
887+
/// Panics if the operation name contains a nul, or if the instruction has non-float parameters.
888+
pub(crate) fn from_packed_instruction_with_floats(
889+
packed: &PackedInstruction,
890+
qargs_interner: &Interner<[Qubit]>,
891+
cargs_interner: &Interner<[Clbit]>,
892+
) -> Self {
893+
let name = CString::new(packed.op.name())
894+
.expect("names do not contain nul")
895+
.into_raw();
896+
let qargs = qargs_interner.get(packed.qubits);
897+
let cargs = cargs_interner.get(packed.clbits);
898+
let params = packed
899+
.params_view()
900+
.iter()
901+
.map(|p| match p {
902+
Param::Float(p) => Some(*p),
903+
_ => None,
904+
})
905+
.collect::<Option<Box<[f64]>>>()
906+
.expect("caller is responsible for ensuring all parameters are floats");
907+
Self {
908+
name,
909+
num_qubits: qargs.len() as u32,
910+
qubits: Box::leak(qargs.iter().map(|q| q.0).collect::<Box<[u32]>>()).as_mut_ptr(),
911+
num_clbits: cargs.len() as u32,
912+
clbits: Box::leak(cargs.iter().map(|c| c.0).collect::<Box<[u32]>>()).as_mut_ptr(),
913+
num_params: params.len() as u32,
914+
params: Box::leak(params).as_mut_ptr(),
915+
}
916+
}
917+
}
879918

880919
/// @ingroup QkCircuit
881920
/// Return the instruction details for an instruction in the circuit.
@@ -915,63 +954,15 @@ pub unsafe extern "C" fn qk_circuit_get_instruction(
915954
index: usize,
916955
instruction: *mut CInstruction,
917956
) {
918-
// SAFETY: Per documentation, the pointer is non-null and aligned.
957+
// SAFETY: Per documentation, `circuit` is a pointer to valid data.
919958
let circuit = unsafe { const_ptr_as_ref(circuit) };
920-
if index >= circuit.__len__() {
921-
panic!("Invalid index")
922-
}
923-
let packed_inst = &circuit.data()[index];
924-
let mut qargs = {
925-
let qargs = circuit.get_qargs(packed_inst.qubits);
926-
let qargs_vec: Vec<u32> = qargs.iter().map(|x| x.0).collect();
927-
qargs_vec.into_boxed_slice()
928-
};
929-
let mut cargs = {
930-
let cargs = circuit.get_cargs(packed_inst.clbits);
931-
let cargs_vec: Vec<u32> = cargs.iter().map(|x| x.0).collect();
932-
cargs_vec.into_boxed_slice()
933-
};
934-
let mut params = {
935-
let params = packed_inst.params_view();
936-
let params_vec: Vec<f64> = params
937-
.iter()
938-
.map(|x| match x {
939-
Param::Float(val) => *val,
940-
_ => panic!("Invalid parameter on instruction"),
941-
})
942-
.collect();
943-
params_vec.into_boxed_slice()
944-
};
945-
// These lists (e.g. 'qargs') are Box<[T]>, so we use .as_mut_ptr()
946-
// to get a mutable pointer to the underlying slice/array on
947-
// the heap and Box::into_raw() to consume the Box without freeing
948-
// it (so the underlying array doesn't get freed when we return)
949-
let out_qargs = qargs.as_mut_ptr();
950-
let out_qargs_len = qargs.len() as u32;
951-
let _ = Box::into_raw(qargs);
952-
let out_cargs = cargs.as_mut_ptr();
953-
let out_cargs_len = cargs.len() as u32;
954-
let _ = Box::into_raw(cargs);
955-
let out_params = params.as_mut_ptr();
956-
let out_params_len = params.len() as u32;
957-
let _ = Box::into_raw(params);
958-
959-
// SAFETY: The pointer must point to a CInstruction size allocation
960-
// per the docstring.
961-
unsafe {
962-
std::ptr::write(
963-
instruction,
964-
CInstruction {
965-
name: CString::new(packed_inst.op.name()).unwrap().into_raw(),
966-
num_qubits: out_qargs_len,
967-
qubits: out_qargs,
968-
num_clbits: out_cargs_len,
969-
clbits: out_cargs,
970-
num_params: out_params_len,
971-
params: out_params,
972-
},
973-
);
974-
}
959+
let inst = CInstruction::from_packed_instruction_with_floats(
960+
&circuit.data()[index],
961+
circuit.qargs_interner(),
962+
circuit.cargs_interner(),
963+
);
964+
// SAFETY: per documentation, `instruction` is a pointer to a sufficient allocation.
965+
unsafe { instruction.write(inst) };
975966
}
976967

977968
/// @ingroup QkCircuit

0 commit comments

Comments
 (0)