Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 88 additions & 0 deletions crates/cext/src/transpiler/angle_bound_registry.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
#![allow(clippy::missing_safety_doc)]

use std::ffi::CStr;
use std::os::raw::{c_char, c_int};

use crate::pointers::const_ptr_as_ref;

use qiskit_circuit::PhysicalQubit;
use qiskit_circuit::dag_circuit::DAGCircuit;
use qiskit_transpiler::angle_bound_registry::WrapAngleRegistry;

/// Return codes:
/// 0 -> success (a DAG was returned in out_dag)
/// 1 -> not found (no wrapper for this name)
/// -1 -> error while executing wrapper (exception)
#[unsafe(no_mangle)]
#[cfg(feature = "cbinding")]
pub unsafe extern "C" fn qk_wrap_angle_registry_substitute(
reg: *const WrapAngleRegistry,
name: *const c_char,
angles: *const f64,
num_angles: u32,
qubits: *const u32,
num_qubits: u32,
out_dag: *mut *mut DAGCircuit,
) -> c_int {
if reg.is_null() || name.is_null() || out_dag.is_null() {
return -1;
}

let reg = unsafe { const_ptr_as_ref(reg) };

let cstr = unsafe { CStr::from_ptr(name) };
let name_str = match cstr.to_str() {
Ok(s) => s,
Err(_) => return -1,
};

let angles_slice: &[f64] = if angles.is_null() || num_angles == 0 {
&[]
} else {
unsafe { std::slice::from_raw_parts(angles, num_angles as usize) }
};

let qubits_slice: Vec<PhysicalQubit> = if qubits.is_null() || num_qubits == 0 {
Vec::new()
} else {
let raw = unsafe { std::slice::from_raw_parts(qubits, num_qubits as usize) };
raw.iter().map(|&x| PhysicalQubit(x)).collect()
};

match reg.substitute_angle_bounds(name_str, angles_slice, &qubits_slice) {
Ok(opt_dag) => {
if let Some(dag) = opt_dag {
let boxed = Box::new(dag);
let raw = Box::into_raw(boxed);
unsafe {
*out_dag = raw;
}
0
} else {
1
}
}
Err(py_err) => {
let _ = py_err;
-1
}
}
}

/// Create a new WrapAngleRegistry and return pointer to it.
#[unsafe(no_mangle)]
#[cfg(feature = "cbinding")]
pub extern "C" fn qk_wrap_angle_registry_new() -> *mut WrapAngleRegistry {
Box::into_raw(Box::new(WrapAngleRegistry::new()))
}

#[unsafe(no_mangle)]
#[cfg(feature = "cbinding")]
pub unsafe extern "C" fn qk_wrap_angle_registry_free(reg: *mut WrapAngleRegistry) {
if reg.is_null() {
return;
}
unsafe {
let _ = Box::from_raw(reg);
};
}
1 change: 1 addition & 0 deletions crates/cext/src/transpiler/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
// copyright notice, and modified files need to carry a notice indicating
// that they have been altered from the originals.

pub mod angle_bound_registry;
pub mod neighbors;
pub mod passes;
pub mod target;
Expand Down
1 change: 1 addition & 0 deletions crates/cext/src/transpiler/passes/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,4 @@ pub mod sabre_layout;
pub mod split_2q_unitaries;
pub mod unitary_synthesis;
pub mod vf2;
pub mod wrap_angles;
59 changes: 59 additions & 0 deletions crates/cext/src/transpiler/passes/wrap_angles.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
use crate::pointers::{const_ptr_as_ref, mut_ptr_as_ref};

use qiskit_circuit::circuit_data::CircuitData;
use qiskit_circuit::converters::dag_to_circuit;
use qiskit_circuit::dag_circuit::DAGCircuit;

use qiskit_transpiler::angle_bound_registry::WrapAngleRegistry;
use qiskit_transpiler::passes::run_wrap_angles;
use qiskit_transpiler::target::Target;

/// Run the WrapAngles transpiler pass on a circuit.
///
/// This pass applies angle-bounds substitutions from a target's angle bounds using the provided
/// wrap-angle registry. The pass will scan the circuit and replace gates that violate the target's
/// angle bounds using the registry substitution callbacks. The function modifies `circuit` in place.
///
/// @param circuit A pointer to the circuit to run WrapAngles on. The circuit is changed in-place if
/// substitutions are performed. In case of modifications the original circuit's allocations will be
/// replaced by the converted circuit produced from the modified DAG.
///
/// @param target A pointer to a `Target` describing hardware constraints (angle bounds).
///
/// @param bounds_registry A pointer to a `WrapAngleRegistry` which provides substitution callbacks.
///
/// @return 0 on success; negative values indicate an error.
/// # Safety
/// - `circuit`, `target`, and `bounds_registry` must be valid, non-null, and properly aligned.
/// - `circuit` must point to a valid `CircuitData` instance that can be safely mutated.
/// - Behavior is undefined if the pointers passed are invalid or not properly aligned.
#[unsafe(no_mangle)]
#[cfg(feature = "cbinding")]
pub unsafe extern "C" fn qk_transpiler_pass_standalone_wrap_angles(
circuit: *mut CircuitData,
target: *const Target,
bounds_registry: *const WrapAngleRegistry,
) -> i32 {
let circuit = unsafe { mut_ptr_as_ref(circuit) };
let target = unsafe { const_ptr_as_ref(target) };
let registry = unsafe { const_ptr_as_ref(bounds_registry) };

// Convert circuit to DAG
let mut dag = match DAGCircuit::from_circuit_data(circuit, false, None, None, None, None) {
Ok(d) => d,
Err(_) => return -4,
};

// Run the pass; run_wrap_angles returns a PyResult<()>, so map errors to error code.
if run_wrap_angles(&mut dag, target, registry).is_err() {
return -5;
}

let out_circuit = match dag_to_circuit(&dag, false) {
Ok(c) => c,
Err(_) => return -6,
};

*circuit = out_circuit;
0
}
47 changes: 47 additions & 0 deletions test/c/test_angle_bounds.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#include "common.h"
#include <stdio.h>
#include <stdlib.h>
#include <qiskit.h>

static int test_basic_wrapangles_linkage(void) {
printf("Running basic WrapAngles linkage test...\n");

QkCircuit *qc = qk_circuit_new(0, 0);

QkTarget *target = qk_target_new(0);
if (!target) {
fprintf(stderr, "Failed to allocate Target\n");
qk_circuit_free(qc);
return 1;
}

QkWrapAngleRegistry* registry = qk_wrap_angle_registry_new();
if (!registry) {
fprintf(stderr, "Failed to allocate WrapAngleRegistry\n");
qk_target_free(target);
qk_circuit_free(qc);
return 1;
}

int rc = qk_transpiler_pass_standalone_wrap_angles(qc, target, registry);
printf("Return code: %d\n", rc);

qk_wrap_angle_registry_free(registry);
qk_target_free(target);
qk_circuit_free(qc);

if (rc != 0) {
fprintf(stderr, "Expected 0, got %d\n", rc);
return 1;
}

return Ok;
}

int test_angle_bounds(void) {
int num_failed = 0;
num_failed += RUN_TEST(test_basic_wrapangles_linkage);
fprintf(stderr, "=== Number of failed subtests: %i\n", num_failed);
fflush(stderr);
return num_failed;
}