use super::*;
use crate::common::gates::UnitaryGateType;
use std::convert::TryFrom;
fn expected_qubit_count(num_qubits: isize) -> Option<usize> {
if num_qubits < 0 {
None
} else {
Some(num_qubits as usize)
}
}
#[no_mangle]
pub extern "C" fn dqcs_gm_new(
strip_qubit_refs: bool,
strip_data: bool,
key_cmp: Option<extern "C" fn(*const c_void, *const c_void) -> bool>,
key_hash: Option<extern "C" fn(*const c_void) -> u64>,
) -> dqcs_handle_t {
let map = if strip_qubit_refs {
if strip_data {
ConverterMap::new(Some(Box::new(|gate: &Gate| gate.without_qubit_refs())))
} else {
ConverterMap::new(Some(Box::new(|gate: &Gate| {
let mut gate = gate.without_qubit_refs();
gate.data.clear();
gate
})))
}
} else if strip_data {
ConverterMap::new(Some(Box::new(|gate: &Gate| {
let mut gate = gate.clone();
gate.data.clear();
gate
})))
} else {
ConverterMap::new(None)
};
insert(GateMap {
map,
key_cmp,
key_hash,
})
}
#[no_mangle]
pub extern "C" fn dqcs_gm_add_predef_unitary(
gm: dqcs_handle_t,
key_free: Option<extern "C" fn(user_data: *mut c_void)>,
key_data: *mut c_void,
gate: dqcs_predefined_gate_t,
num_controls: isize,
epsilon: c_double,
ignore_gphase: bool,
) -> dqcs_return_t {
api_return_none(|| {
let key = UserKeyData::new(key_free, key_data);
resolve!(gm as &mut GateMap);
let key = gm.make_key(key);
let gate = UnitaryGateType::try_from(gate)?;
let num_controls = expected_qubit_count(num_controls);
gm.map.push(
key,
gate.into_gate_converter(num_controls, epsilon, ignore_gphase),
);
Ok(())
})
}
#[no_mangle]
pub extern "C" fn dqcs_gm_add_fixed_unitary(
gm: dqcs_handle_t,
key_free: Option<extern "C" fn(key_data: *mut c_void)>,
key_data: *mut c_void,
matrix: dqcs_handle_t,
num_controls: isize,
epsilon: c_double,
ignore_gphase: bool,
) -> dqcs_return_t {
api_return_none(|| {
let key = UserKeyData::new(key_free, key_data);
resolve!(gm as &mut GateMap);
let key = gm.make_key(key);
take!(matrix as Matrix);
let num_controls = expected_qubit_count(num_controls);
gm.map.push(
key,
Box::new(UnitaryGateConverter::from(UnitaryConverter::new(
FixedMatrixConverter::from(matrix),
num_controls,
epsilon,
ignore_gphase,
))),
);
Ok(())
})
}
#[no_mangle]
pub extern "C" fn dqcs_gm_add_custom_unitary(
gm: dqcs_handle_t,
key_free: Option<extern "C" fn(key_data: *mut c_void)>,
key_data: *mut c_void,
detector: Option<
extern "C" fn(
user_data: *const c_void,
matrix: dqcs_handle_t,
num_controls: size_t,
param_data: *mut dqcs_handle_t,
) -> dqcs_bool_return_t,
>,
detector_user_free: Option<extern "C" fn(user_data: *mut c_void)>,
detector_user_data: *mut c_void,
constructor: Option<
extern "C" fn(
user_data: *const c_void,
param_data: *mut dqcs_handle_t,
num_controls: *mut isize,
) -> dqcs_handle_t,
>,
constructor_user_free: Option<extern "C" fn(user_data: *mut c_void)>,
constructor_user_data: *mut c_void,
) -> dqcs_return_t {
api_return_none(|| {
let key = UserKeyData::new(key_free, key_data);
let detector_user_data = UserData::new(detector_user_free, detector_user_data);
let constructor_user_data = UserData::new(constructor_user_free, constructor_user_data);
resolve!(gm as &mut GateMap);
let key = gm.make_key(key);
gm.map.push(
key,
Box::new(CustomGateConverter::new(
move |gate| {
if let Some(detector) = detector {
if gate.get_name().is_some() || !gate.get_measures().is_empty() {
Ok(None)
} else if let Some(matrix) = gate.get_matrix() {
let matrix = insert(matrix.clone());
let num_controls = gate.get_controls().len();
let mut param_data = insert(gate.data.clone());
let result = cb_return_bool(detector(
detector_user_data.data(),
matrix,
num_controls,
&mut param_data as *mut dqcs_handle_t,
));
delete!(matrix);
if let Ok(true) = result {
take!(param_data as ArbData);
let mut qubits = vec![];
qubits.extend(gate.get_controls().iter());
qubits.extend(gate.get_targets().iter());
Ok(Some((qubits, param_data)))
} else {
delete!(param_data);
result.map(|_| None)
}
} else {
unreachable!();
}
} else {
Ok(None)
}
},
move |qubits, param_data| {
if let Some(constructor) = constructor {
let mut param_data = insert(param_data.clone());
let mut expected_num_controls = -1isize;
let result = constructor(
constructor_user_data.data(),
&mut param_data as *mut dqcs_handle_t,
&mut expected_num_controls as *mut isize,
);
let matrix = match cb_return(0, result) {
Ok(matrix) => matrix,
Err(err) => {
delete!(param_data);
return Err(err);
}
};
take!(matrix as Matrix);
take!(param_data as ArbData);
let expected_num_controls = expected_qubit_count(expected_num_controls);
let num_targets = matrix.num_qubits().unwrap();
let num_controls =
qubits
.len()
.checked_sub(num_targets)
.ok_or_else(oe_inv_arg(format!(
"need at least {} qubits",
num_targets
)))?;
if let Some(expected) = expected_num_controls {
if num_controls != expected {
inv_arg(format!(
"expected {} control and {} target qubits",
expected, num_targets
))?;
}
}
let controls = &qubits[..num_controls];
let targets = &qubits[num_controls..];
let mut gate = Gate::new_unitary(
targets.iter().cloned(),
controls.iter().cloned(),
matrix,
)?;
gate.data.copy_from(¶m_data);
Ok(gate)
} else {
inv_arg("no constructor function defined")
}
},
)),
);
Ok(())
})
}
#[no_mangle]
pub extern "C" fn dqcs_gm_add_measure(
gm: dqcs_handle_t,
key_free: Option<extern "C" fn(user_data: *mut c_void)>,
key_data: *mut c_void,
num_measures: isize,
basis: dqcs_handle_t,
epsilon: f64,
) -> dqcs_return_t {
api_return_none(|| {
let key = UserKeyData::new(key_free, key_data);
resolve!(gm as &mut GateMap);
let key = gm.make_key(key);
let num_measures = expected_qubit_count(num_measures);
resolve!(optional basis as pending Matrix);
let defaulted_basis = {
if let Some(basis) = basis.as_ref() {
let basis: &Matrix = basis.as_ref().unwrap();
if basis.dimension() != 2 {
inv_arg("measurement basis matrix must be 2x2")?;
}
basis.clone()
} else {
Matrix::new_identity(2)
}
};
gm.map.push(
key,
Box::new(MeasurementGateConverter::new(
num_measures,
defaulted_basis,
epsilon,
)),
);
if let Some(mut basis) = basis {
delete!(resolved basis);
}
Ok(())
})
}
#[no_mangle]
pub extern "C" fn dqcs_gm_add_prep(
gm: dqcs_handle_t,
key_free: Option<extern "C" fn(user_data: *mut c_void)>,
key_data: *mut c_void,
num_targets: isize,
basis: dqcs_handle_t,
epsilon: f64,
) -> dqcs_return_t {
api_return_none(|| {
let key = UserKeyData::new(key_free, key_data);
resolve!(gm as &mut GateMap);
let key = gm.make_key(key);
let num_targets = expected_qubit_count(num_targets);
resolve!(optional basis as pending Matrix);
let defaulted_basis = {
if let Some(basis) = basis.as_ref() {
let basis: &Matrix = basis.as_ref().unwrap();
if basis.dimension() != 2 {
inv_arg("prep basis matrix must be 2x2")?;
}
basis.clone()
} else {
Matrix::new_identity(2)
}
};
gm.map.push(
key,
Box::new(PrepGateConverter::new(
num_targets,
defaulted_basis,
epsilon,
)),
);
if let Some(mut basis) = basis {
delete!(resolved basis);
}
Ok(())
})
}
#[no_mangle]
pub extern "C" fn dqcs_gm_add_custom(
gm: dqcs_handle_t,
key_free: Option<extern "C" fn(key_data: *mut c_void)>,
key_data: *mut c_void,
detector: Option<
extern "C" fn(
user_data: *const c_void,
gate: dqcs_handle_t,
qubits: *mut dqcs_handle_t,
param_data: *mut dqcs_handle_t,
) -> dqcs_bool_return_t,
>,
detector_user_free: Option<extern "C" fn(user_data: *mut c_void)>,
detector_user_data: *mut c_void,
constructor: Option<
extern "C" fn(
user_data: *const c_void,
qubits: dqcs_handle_t,
param_data: dqcs_handle_t,
) -> dqcs_handle_t,
>,
constructor_user_free: Option<extern "C" fn(user_data: *mut c_void)>,
constructor_user_data: *mut c_void,
) -> dqcs_return_t {
api_return_none(|| {
let key = UserKeyData::new(key_free, key_data);
let detector_user_data = UserData::new(detector_user_free, detector_user_data);
let constructor_user_data = UserData::new(constructor_user_free, constructor_user_data);
resolve!(gm as &mut GateMap);
let key = gm.make_key(key);
gm.map.push(
key,
Box::new(CustomGateConverter::new(
move |gate| {
if let Some(detector) = detector {
let gate = insert(gate.clone());
let mut qubits: dqcs_handle_t = 0;
let mut param_data: dqcs_handle_t = 0;
let result = cb_return_bool(detector(
detector_user_data.data(),
gate,
&mut qubits as *mut dqcs_handle_t,
&mut param_data as *mut dqcs_handle_t,
));
if let Ok(true) = result {
let param_data = if param_data == 0 {
take!(gate as Gate);
gate.data
} else {
delete!(gate);
take!(param_data as ArbData);
param_data
};
let qubits = if qubits == 0 {
vec![]
} else {
take!(qubits as QubitReferenceSet);
qubits.into_iter().collect()
};
Ok(Some((qubits, param_data)))
} else {
delete!(gate);
if param_data != 0 {
delete!(param_data);
}
if qubits != 0 {
delete!(qubits);
}
result.map(|_| None)
}
} else {
Ok(None)
}
},
move |qubits, param_data| {
if let Some(constructor) = constructor {
let qubits: QubitReferenceSet = qubits.iter().cloned().collect();
let qubits = insert(qubits);
let param_data = insert(param_data.clone());
let result = constructor(constructor_user_data.data(), qubits, param_data);
delete!(param_data);
delete!(qubits);
let gate = cb_return(0, result)?;
take!(gate as Gate);
Ok(gate)
} else {
inv_arg("no constructor function defined")
}
},
)),
);
Ok(())
})
}
#[no_mangle]
pub extern "C" fn dqcs_gm_detect(
gm: dqcs_handle_t,
gate: dqcs_handle_t,
key_data: *mut *const c_void,
qubits: *mut dqcs_handle_t,
param_data: *mut dqcs_handle_t,
) -> dqcs_bool_return_t {
api_return_bool(|| {
if !qubits.is_null() {
unsafe { *qubits = 0 };
}
if !param_data.is_null() {
unsafe { *param_data = 0 };
}
resolve!(gm as &GateMap);
resolve!(gate as &Gate);
if let Some((key, (args, data))) = gm.map.detect(&gate)? {
if !key_data.is_null() {
unsafe { *key_data = key.raw() };
}
if !qubits.is_null() {
let args: QubitReferenceSet = args.iter().cloned().collect();
let handle = insert(args);
unsafe { *qubits = handle };
}
if !param_data.is_null() {
let handle = insert(data);
unsafe { *param_data = handle };
}
Ok(true)
} else {
Ok(false)
}
})
}
fn construct_helper(
gm: dqcs_handle_t,
key_data: *const c_void,
qubits: Vec<QubitRef>,
param_data: dqcs_handle_t,
) -> Result<dqcs_handle_t> {
let key = UserKeyData::new_borrowed(key_data);
resolve!(gm as &GateMap);
let key = gm.make_key(key);
resolve!(optional param_data as pending ArbData);
let data: ArbData = {
if let Some(data) = param_data.as_ref() {
let x: &ArbData = data.as_ref().unwrap();
x.clone()
} else {
ArbData::default()
}
};
let gate = insert(gm.map.construct(&(key, (qubits, data)))?);
if let Some(mut param_data) = param_data {
delete!(resolved param_data);
}
Ok(gate)
}
#[no_mangle]
pub extern "C" fn dqcs_gm_construct(
gm: dqcs_handle_t,
key_data: *const c_void,
qubits: dqcs_handle_t,
param_data: dqcs_handle_t,
) -> dqcs_handle_t {
api_return(0, || {
resolve!(optional qubits as pending QubitReferenceSet);
let qubits_vec: Vec<QubitRef> = {
if let Some(qubits) = qubits.as_ref() {
let x: &QubitReferenceSet = qubits.as_ref().unwrap();
x.iter().cloned().collect()
} else {
vec![]
}
};
let gate = construct_helper(gm, key_data, qubits_vec, param_data)?;
if let Some(mut qubits) = qubits {
delete!(resolved qubits);
}
Ok(gate)
})
}
#[no_mangle]
pub extern "C" fn dqcs_gm_construct_one(
gm: dqcs_handle_t,
key_data: *const c_void,
qa: dqcs_qubit_t,
param_data: dqcs_handle_t,
) -> dqcs_handle_t {
api_return(0, || {
let qubits_vec = vec![QubitRef::from_foreign(qa)
.ok_or_else(oe_inv_arg("0 is not a valid qubit reference"))?];
construct_helper(gm, key_data, qubits_vec, param_data)
})
}
#[no_mangle]
pub extern "C" fn dqcs_gm_construct_two(
gm: dqcs_handle_t,
key_data: *const c_void,
qa: dqcs_qubit_t,
qb: dqcs_qubit_t,
param_data: dqcs_handle_t,
) -> dqcs_handle_t {
api_return(0, || {
let qubits_vec = vec![
QubitRef::from_foreign(qa)
.ok_or_else(oe_inv_arg("0 is not a valid qubit reference"))?,
QubitRef::from_foreign(qb)
.ok_or_else(oe_inv_arg("0 is not a valid qubit reference"))?,
];
if qa == qb {
inv_arg(format!("cannot use qubit {} twice", qa))?;
}
construct_helper(gm, key_data, qubits_vec, param_data)
})
}
#[no_mangle]
pub extern "C" fn dqcs_gm_construct_three(
gm: dqcs_handle_t,
key_data: *const c_void,
qa: dqcs_qubit_t,
qb: dqcs_qubit_t,
qc: dqcs_qubit_t,
param_data: dqcs_handle_t,
) -> dqcs_handle_t {
api_return(0, || {
let qubits_vec = vec![
QubitRef::from_foreign(qa)
.ok_or_else(oe_inv_arg("0 is not a valid qubit reference"))?,
QubitRef::from_foreign(qb)
.ok_or_else(oe_inv_arg("0 is not a valid qubit reference"))?,
QubitRef::from_foreign(qc)
.ok_or_else(oe_inv_arg("0 is not a valid qubit reference"))?,
];
if qa == qb || qa == qc {
inv_arg(format!("cannot use qubit {} twice", qa))?;
}
if qb == qc {
inv_arg(format!("cannot use qubit {} twice", qb))?;
}
construct_helper(gm, key_data, qubits_vec, param_data)
})
}