use std::{
ffi::{c_char, CStr, CString},
ptr::null,
};
use crate::{
c_api::c_ok,
error::KetError,
execution::{
BatchExecution, DumpData, ExpValueStrategy, GradientStrategy, LiveExecution, NativeGate,
NativeGateSet, QuantumExecution, SampleData,
},
ir::{gate::GateInstruction, hamiltonian::Hamiltonian},
matrix::Matrix,
process::QPUConfig,
};
#[repr(C)]
#[derive(Debug, Clone)]
pub struct CLiveExecution {
pub compute_gate: fn(json: *const c_char) -> i32,
pub compute_native_gates: fn(json: *const c_char) -> i32,
pub measure: fn(qubits: *const usize, len: usize, result: &mut u64) -> i32,
pub dump: fn(qubits: *const usize, len: usize, result_json: &mut *const c_char) -> i32,
pub sample:
fn(qubits: *const usize, len: usize, shots: usize, result_json: &mut *const c_char) -> i32,
pub exp_value: fn(hamiltonian_json: *const c_char, result: &mut f64) -> i32,
}
impl LiveExecution for CLiveExecution {
fn compute_gate(&mut self, gate: &GateInstruction) -> Result<(), KetError> {
let Ok(json) = serde_json::to_string(gate) else {
return Err(KetError::SerdeError);
};
let Ok(json) = CString::new(json) else {
return Err(KetError::SerdeError);
};
let err = KetError::from_error_code((self.compute_gate)(json.as_ptr()));
match err {
KetError::Success => Ok(()),
err => Err(err),
}
}
fn compute_native_gates(&mut self, gates: &[NativeGate]) -> Result<(), KetError> {
let Ok(gates) = serde_json::to_string(gates) else {
return Err(KetError::SerdeError);
};
let Ok(gates) = CString::new(gates) else {
return Err(KetError::SerdeError);
};
let err = KetError::from_error_code((self.compute_native_gates)(gates.as_ptr()));
match err {
KetError::Success => Ok(()),
err => Err(err),
}
}
fn measure(&mut self, qubits: &[usize]) -> Result<u64, KetError> {
let mut result = 42;
let err =
KetError::from_error_code((self.measure)(qubits.as_ptr(), qubits.len(), &mut result));
match err {
KetError::Success => Ok(result),
err => Err(err),
}
}
fn sample(&mut self, qubits: &[usize], shots: usize) -> Result<SampleData, KetError> {
let mut result_json = null();
let err = KetError::from_error_code((self.sample)(
qubits.as_ptr(),
qubits.len(),
shots,
&mut result_json,
));
match err {
KetError::Success => {
let Ok(result_json) = { unsafe { CStr::from_ptr(result_json) } }.to_str() else {
return Err(KetError::SerdeError);
};
match serde_json::from_str::<SampleData>(result_json) {
Ok(sample_date) => Ok(sample_date),
Err(_) => Err(KetError::SerdeError),
}
}
err => Err(err),
}
}
fn exp_value(&mut self, hamiltonian: Hamiltonian) -> Result<f64, KetError> {
let mut result = f64::NAN;
let Ok(json) = serde_json::to_string(&hamiltonian) else {
return Err(KetError::SerdeError);
};
let Ok(json) = CString::new(json) else {
return Err(KetError::SerdeError);
};
let err = KetError::from_error_code((self.exp_value)(json.as_ptr(), &mut result));
match err {
KetError::Success => Ok(result),
err => Err(err),
}
}
fn dump(&mut self, qubits: &[usize]) -> Result<DumpData, KetError> {
let mut result_json = null();
let err =
KetError::from_error_code((self.dump)(qubits.as_ptr(), qubits.len(), &mut result_json));
match err {
KetError::Success => {
let Ok(result_json) = { unsafe { CStr::from_ptr(result_json) } }.to_str() else {
return Err(KetError::SerdeError);
};
match serde_json::from_str::<DumpData>(result_json) {
Ok(dump_data) => Ok(dump_data),
Err(_) => Err(KetError::SerdeError),
}
}
err => Err(err),
}
}
}
#[repr(C)]
#[derive(Debug, Clone)]
pub struct CBatchExecution {
pub sample: fn(
gates_json: *const c_char,
qubits_to_sample: *const usize,
qubits_to_sample_len: usize,
shots: usize,
sample_json: &mut *const c_char,
) -> i32,
pub exp_value: fn(
gates_json: *const c_char,
hamiltonian_list_json: *const c_char,
result: *mut f64,
) -> i32,
pub sample_native: fn(
gates_json: *const c_char,
qubits_to_sample: *const usize,
qubits_to_sample_len: usize,
shots: usize,
sample_json: &mut *const c_char,
) -> i32,
pub exp_value_native: fn(
gates_json: *const c_char,
hamiltonian_list_json: *const c_char,
result: *mut f64,
) -> i32,
pub gradient: fn(
gates_json: *const c_char,
hamiltonian_list_json: *const c_char,
exp_result: &mut f64,
grad_json: &mut *const c_char,
) -> i32,
}
impl BatchExecution for CBatchExecution {
fn sample(
&self,
gates: &[GateInstruction],
qubits_to_sample: &[usize],
shots: usize,
) -> Result<SampleData, KetError> {
let mut sample_json = std::ptr::null();
let Ok(gates_json) = serde_json::to_string(gates) else {
return Err(KetError::SerdeError);
};
let Ok(gates_json) = CString::new(gates_json) else {
return Err(KetError::SerdeError);
};
let err = KetError::from_error_code((self.sample)(
gates_json.as_ptr(),
qubits_to_sample.as_ptr(),
qubits_to_sample.len(),
shots,
&mut sample_json,
));
match err {
KetError::Success => {
let Ok(result_json) = { unsafe { CStr::from_ptr(sample_json) } }.to_str() else {
return Err(KetError::SerdeError);
};
match serde_json::from_str::<SampleData>(result_json) {
Ok(sample_data) => Ok(sample_data),
Err(_) => Err(KetError::SerdeError),
}
}
err => Err(err),
}
}
fn exp_value(
&self,
gates: &[GateInstruction],
hamiltonian_list: &[Hamiltonian],
) -> Result<Vec<f64>, KetError> {
let mut result = vec![f64::NAN; hamiltonian_list.len()];
let Ok(gates_json) = serde_json::to_string(gates) else {
return Err(KetError::SerdeError);
};
let Ok(gates_json) = CString::new(gates_json) else {
return Err(KetError::SerdeError);
};
let Ok(ham_json) = serde_json::to_string(&hamiltonian_list) else {
return Err(KetError::SerdeError);
};
let Ok(ham_json) = CString::new(ham_json) else {
return Err(KetError::SerdeError);
};
let err = KetError::from_error_code((self.exp_value)(
gates_json.as_ptr(),
ham_json.as_ptr(),
result.as_mut_ptr(),
));
match err {
KetError::Success => Ok(result),
err => Err(err),
}
}
fn sample_native(
&self,
gates: &[NativeGate],
qubits_to_sample: &[usize],
shots: usize,
) -> Result<SampleData, KetError> {
let mut sample_json = std::ptr::null();
let Ok(gates_json) = serde_json::to_string(gates) else {
return Err(KetError::SerdeError);
};
let Ok(gates_json) = CString::new(gates_json) else {
return Err(KetError::SerdeError);
};
let err = KetError::from_error_code((self.sample_native)(
gates_json.as_ptr(),
qubits_to_sample.as_ptr(),
qubits_to_sample.len(),
shots,
&mut sample_json,
));
match err {
KetError::Success => {
let Ok(result_json) = { unsafe { CStr::from_ptr(sample_json) } }.to_str() else {
return Err(KetError::SerdeError);
};
match serde_json::from_str::<SampleData>(result_json) {
Ok(sample_data) => Ok(sample_data),
Err(_) => Err(KetError::SerdeError),
}
}
err => Err(err),
}
}
fn exp_value_native(
&self,
gates: &[NativeGate],
hamiltonian_list: &[Hamiltonian],
) -> Result<Vec<f64>, KetError> {
let mut result = vec![f64::NAN; hamiltonian_list.len()];
let Ok(gates_json) = serde_json::to_string(gates) else {
return Err(KetError::SerdeError);
};
let Ok(gates_json) = CString::new(gates_json) else {
return Err(KetError::SerdeError);
};
let Ok(ham_json) = serde_json::to_string(&hamiltonian_list) else {
return Err(KetError::SerdeError);
};
let Ok(ham_json) = CString::new(ham_json) else {
return Err(KetError::SerdeError);
};
let err = KetError::from_error_code((self.exp_value_native)(
gates_json.as_ptr(),
ham_json.as_ptr(),
result.as_mut_ptr(),
));
match err {
KetError::Success => Ok(result),
err => Err(err),
}
}
fn gradient(
&self,
gates: &[GateInstruction],
hamiltonian: &Hamiltonian,
) -> Result<(f64, Vec<f64>), KetError> {
let mut exp_result = 0.0;
let Ok(gates_json) = serde_json::to_string(gates) else {
return Err(KetError::SerdeError);
};
let Ok(gates_json) = CString::new(gates_json) else {
return Err(KetError::SerdeError);
};
let Ok(ham_json) = serde_json::to_string(hamiltonian) else {
return Err(KetError::SerdeError);
};
let Ok(ham_json) = CString::new(ham_json) else {
return Err(KetError::SerdeError);
};
let mut grad_json = std::ptr::null();
let err = KetError::from_error_code((self.gradient)(
gates_json.as_ptr(),
ham_json.as_ptr(),
&mut exp_result,
&mut grad_json,
));
match err {
KetError::Success => {
let Ok(grad_json) = { unsafe { CStr::from_ptr(grad_json) } }.to_str() else {
return Err(KetError::SerdeError);
};
match serde_json::from_str::<Vec<f64>>(grad_json) {
Ok(grad_json) => Ok((exp_result, grad_json)),
Err(_) => Err(KetError::SerdeError),
}
}
err => Err(err),
}
}
}
#[repr(C)]
#[derive(Debug, Clone)]
pub struct CNativeGateSet {
pub translate:
fn(gate_json: *const c_char, target: usize, native_gate_json: &mut *const c_char) -> i32,
pub cnot: fn(control: usize, target: usize, native_gate_json: &mut *const c_char) -> i32,
}
impl NativeGateSet for CNativeGateSet {
fn translate(&self, matrix: &Matrix, target: usize) -> Result<Vec<NativeGate>, KetError> {
let matrix_json = [
[
matrix[0][0].re,
matrix[0][0].im,
matrix[0][1].re,
matrix[0][1].im,
],
[
matrix[1][0].re,
matrix[1][0].im,
matrix[1][1].re,
matrix[1][1].im,
],
];
let Ok(gate) = serde_json::to_string(&matrix_json) else {
return Err(KetError::SerdeError);
};
let Ok(gate) = CString::new(gate) else {
return Err(KetError::SerdeError);
};
let mut result_json = null();
let err =
KetError::from_error_code((self.translate)(gate.as_ptr(), target, &mut result_json));
match err {
KetError::Success => {
let Ok(result_json) = { unsafe { CStr::from_ptr(result_json) } }.to_str() else {
return Err(KetError::SerdeError);
};
match serde_json::from_str(result_json) {
Ok(result) => Ok(result),
Err(_) => Err(KetError::SerdeError),
}
}
err => Err(err),
}
}
fn cnot(&self, control: usize, target: usize) -> Result<Vec<NativeGate>, KetError> {
let mut result_json = null();
let err = KetError::from_error_code((self.cnot)(control, target, &mut result_json));
match err {
KetError::Success => {
let Ok(result_json) = { unsafe { CStr::from_ptr(result_json) } }.to_str() else {
return Err(KetError::SerdeError);
};
match serde_json::from_str(result_json) {
Ok(result) => Ok(result),
Err(_) => Err(KetError::SerdeError),
}
}
err => Err(err),
}
}
}
#[no_mangle]
pub extern "C" fn ket_quantum_execution_live(
num_qubits: usize,
live: &CLiveExecution,
decompose: bool,
native_gate_set: *const CNativeGateSet,
qpu_config: &mut *mut QPUConfig,
) -> i32 {
let native_gate_set = if native_gate_set.is_null() {
None
} else {
Some(Box::new(unsafe { &*native_gate_set }.clone()) as Box<dyn NativeGateSet>)
};
let execution = QuantumExecution::Live {
qpu: Box::new(live.clone()),
decompose,
native_gate_set,
};
*qpu_config = Box::into_raw(Box::new(QPUConfig {
num_qubits,
quantum_execution: Some(execution),
}));
c_ok()
}
#[no_mangle]
pub extern "C" fn ket_quantum_execution_batch(
num_qubits: usize,
batch: &CBatchExecution,
native_gate_set: *const CNativeGateSet,
decompose: bool,
gradient: bool,
coupling_graph_json: *const c_char,
exp_value_strategy_json: *const c_char,
qpu_config: &mut *mut QPUConfig,
) -> i32 {
let native_gate_set: Option<Box<dyn NativeGateSet>> = unsafe {
if native_gate_set.is_null() {
None
} else {
Some(Box::new((*native_gate_set).clone()))
}
};
let coupling_graph = from_json!(coupling_graph_json, Option<Vec<(usize, usize)>>);
let exp_value = from_json!(exp_value_strategy_json, ExpValueStrategy);
let execution = QuantumExecution::Batch {
qpu: Box::new(batch.clone()),
native_gate_set,
gradient: if gradient {
GradientStrategy::ParameterShiftRule
} else {
GradientStrategy::None
},
exp_value,
coupling_graph,
decompose,
};
*qpu_config = Box::into_raw(Box::new(QPUConfig {
num_qubits,
quantum_execution: Some(execution),
}));
c_ok()
}