use crate::error::{QuantumError, Result};
use moonlab_sys::{
chern_kpm_bulk_map, chern_kpm_bulk_sum, chern_kpm_cn_modulation,
chern_kpm_create, chern_kpm_free, chern_kpm_local_marker,
chern_kpm_set_modulation, moonlab_qwz_chern, qgt_berry_grid_free,
qgt_berry_grid_nband, qgt_berry_grid_proj, qgt_berry_grid_pt,
qgt_berry_grid_t, qgt_free, qgt_free_1d, qgt_free_nband,
qgt_model_bhz, qgt_model_hofstadter, qgt_model_kane_mele,
qgt_model_kitaev_chain, qgt_model_qwz, qgt_model_ssh,
qgt_winding_1d, qgt_z2_invariant, qgt_z2_invariant_1d_bdg,
};
use std::os::raw::{c_double, c_int};
use std::ptr;
pub fn qwz_chern(m: f64, n: usize) -> i32 {
unsafe { moonlab_qwz_chern(m, n, ptr::null_mut()) }
}
pub fn ssh_winding(t1: f64, t2: f64, n: usize) -> i32 {
unsafe {
let sys = qgt_model_ssh(t1, t2);
if sys.is_null() {
return 0;
}
let mut raw: c_double = 0.0;
let w = qgt_winding_1d(sys, n, &mut raw);
qgt_free_1d(sys);
w
}
}
fn berry_chern_2d(
sys: *mut moonlab_sys::qgt_system_t,
n: usize,
integrator: unsafe extern "C" fn(
*const moonlab_sys::qgt_system_t,
usize,
*mut qgt_berry_grid_t,
) -> c_int,
label: &'static str,
) -> Result<i32> {
if sys.is_null() {
return Err(QuantumError::AllocationFailed(0));
}
let mut grid = qgt_berry_grid_t {
N: 0,
berry: ptr::null_mut(),
chern: 0.0,
};
let rc = unsafe { integrator(sys, n, &mut grid) };
if rc != 0 {
unsafe { qgt_free(sys) };
return Err(QuantumError::Ffi(format!("{label} failed (rc={rc})")));
}
let chern = grid.chern.round() as i32;
unsafe {
qgt_berry_grid_free(&mut grid);
qgt_free(sys);
}
Ok(chern)
}
pub fn chern_qwz_proj(m: f64, n: usize) -> Result<i32> {
if n < 4 {
return Err(QuantumError::InvalidQubit { index: n, max: 4 });
}
let sys = unsafe { qgt_model_qwz(m) };
berry_chern_2d(sys, n, qgt_berry_grid_proj, "qgt_berry_grid_proj")
}
pub fn chern_qwz_parallel_transport(m: f64, n: usize) -> Result<i32> {
if n < 4 {
return Err(QuantumError::InvalidQubit { index: n, max: 4 });
}
let sys = unsafe { qgt_model_qwz(m) };
berry_chern_2d(sys, n, qgt_berry_grid_pt, "qgt_berry_grid_pt")
}
pub fn kane_mele_z2(
t: f64,
lambda_so: f64,
lambda_r: f64,
lambda_v: f64,
n: usize,
) -> Result<i32> {
if n < 8 || n % 2 != 0 {
return Err(QuantumError::InvalidQubit { index: n, max: 8 });
}
let sys = unsafe { qgt_model_kane_mele(t, lambda_so, lambda_r, lambda_v) };
if sys.is_null() {
return Err(QuantumError::AllocationFailed(0));
}
let mut z2: c_int = -1;
let rc = unsafe { qgt_z2_invariant(sys, n, &mut z2) };
unsafe { qgt_free_nband(sys) };
if rc != 0 {
return Err(QuantumError::Ffi(format!("qgt_z2_invariant failed (rc={rc})")));
}
Ok(z2 as i32)
}
pub fn bhz_z2(a: f64, b: f64, m: f64, n: usize) -> Result<i32> {
if n < 8 || n % 2 != 0 {
return Err(QuantumError::InvalidQubit { index: n, max: 8 });
}
let sys = unsafe { qgt_model_bhz(a, b, m) };
if sys.is_null() {
return Err(QuantumError::AllocationFailed(0));
}
let mut z2: c_int = -1;
let rc = unsafe { qgt_z2_invariant(sys, n, &mut z2) };
unsafe { qgt_free_nband(sys) };
if rc != 0 {
return Err(QuantumError::Ffi(format!("qgt_z2_invariant failed (rc={rc})")));
}
Ok(z2 as i32)
}
pub fn kitaev_chain_z2(t: f64, mu: f64, delta: f64) -> Result<i32> {
let sys = unsafe { qgt_model_kitaev_chain(t, mu, delta) };
if sys.is_null() {
return Err(QuantumError::AllocationFailed(0));
}
let mut z2: c_int = -1;
let rc = unsafe { qgt_z2_invariant_1d_bdg(sys, &mut z2) };
unsafe { qgt_free_1d(sys) };
if rc != 0 {
return Err(QuantumError::Ffi(format!(
"qgt_z2_invariant_1d_bdg failed (rc={rc})"
)));
}
Ok(z2 as i32)
}
pub fn hofstadter_chern(
p: usize,
q: usize,
n_occupied: usize,
t: f64,
n: usize,
) -> Result<i32> {
if q < 2 {
return Err(QuantumError::InvalidQubit { index: q, max: 2 });
}
if !(1..=q.saturating_sub(1)).contains(&n_occupied) {
return Err(QuantumError::InvalidQubit {
index: n_occupied,
max: q.saturating_sub(1),
});
}
if n < 8 {
return Err(QuantumError::InvalidQubit { index: n, max: 8 });
}
let sys = unsafe { qgt_model_hofstadter(t, p, q, n_occupied) };
if sys.is_null() {
return Err(QuantumError::AllocationFailed(0));
}
let mut grid = qgt_berry_grid_t {
N: 0,
berry: ptr::null_mut(),
chern: 0.0,
};
let rc = unsafe { qgt_berry_grid_nband(sys, n, &mut grid) };
if rc != 0 {
unsafe { qgt_free_nband(sys) };
return Err(QuantumError::Ffi(format!(
"qgt_berry_grid_nband failed (rc={rc})"
)));
}
let chern = grid.chern.round() as i32;
unsafe {
qgt_berry_grid_free(&mut grid);
qgt_free_nband(sys);
}
Ok(chern)
}
pub struct ChernKpm {
handle: *mut moonlab_sys::chern_kpm_system_t,
l: usize,
modulation: Option<Vec<f64>>,
}
unsafe impl Send for ChernKpm {}
impl ChernKpm {
pub fn new(l: usize, m: f64, n_cheby: usize) -> Result<Self> {
if l < 3 {
return Err(QuantumError::InvalidQubit { index: l, max: 3 });
}
if n_cheby < 8 {
return Err(QuantumError::InvalidQubit { index: n_cheby, max: 8 });
}
let handle = unsafe { chern_kpm_create(l, m, n_cheby) };
if handle.is_null() {
return Err(QuantumError::AllocationFailed(l));
}
Ok(Self { handle, l, modulation: None })
}
pub fn lattice_size(&self) -> usize {
self.l
}
pub fn local_marker(&self, x: usize, y: usize) -> Result<f64> {
if x >= self.l || y >= self.l {
return Err(QuantumError::InvalidQubit { index: x.max(y), max: self.l });
}
Ok(unsafe { chern_kpm_local_marker(self.handle, x, y) })
}
pub fn bulk_sum(&self, rmin: usize, rmax: usize) -> Result<f64> {
if rmin >= rmax || rmax > self.l {
return Err(QuantumError::InvalidQubit { index: rmax, max: self.l });
}
Ok(unsafe { chern_kpm_bulk_sum(self.handle, rmin, rmax) })
}
pub fn bulk_map(&self, rmin: usize, rmax: usize) -> Result<Vec<f64>> {
if rmin >= rmax || rmax > self.l {
return Err(QuantumError::InvalidQubit { index: rmax, max: self.l });
}
let side = rmax - rmin;
let mut out = vec![0.0f64; side * side];
let rc = unsafe {
chern_kpm_bulk_map(self.handle, rmin, rmax, out.as_mut_ptr())
};
if rc != 0 {
return Err(QuantumError::Ffi(format!(
"chern_kpm_bulk_map failed (rc={rc})"
)));
}
Ok(out)
}
pub fn set_cn_modulation(&mut self, n: i32, q: f64, v0: f64) -> Result<()> {
if n < 2 {
return Err(QuantumError::InvalidQubit { index: n.max(0) as usize, max: 2 });
}
let raw = unsafe { chern_kpm_cn_modulation(self.l, n, q, v0) };
if raw.is_null() {
return Err(QuantumError::AllocationFailed(self.l * self.l));
}
let len = self.l * self.l;
let slice = unsafe { std::slice::from_raw_parts(raw, len) };
let mut owned = Vec::with_capacity(len);
owned.extend_from_slice(slice);
unsafe { libc::free(raw as *mut libc::c_void) };
let v_max = (n as f64).abs() * v0.abs();
let rc = unsafe {
chern_kpm_set_modulation(self.handle, owned.as_ptr(), v_max)
};
if rc != 0 {
return Err(QuantumError::Ffi(format!(
"chern_kpm_set_modulation failed (rc={rc})"
)));
}
self.modulation = Some(owned);
Ok(())
}
pub fn clear_modulation(&mut self) {
unsafe { chern_kpm_set_modulation(self.handle, ptr::null(), 0.0) };
self.modulation = None;
}
}
impl Drop for ChernKpm {
fn drop(&mut self) {
if !self.handle.is_null() {
unsafe { chern_kpm_free(self.handle) };
self.handle = ptr::null_mut();
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn qwz_chern_phases() {
assert_eq!(qwz_chern(1.0, 32), -1);
assert_eq!(qwz_chern(-1.0, 32), 1);
assert_eq!(qwz_chern(3.0, 32), 0);
}
#[test]
fn ssh_winding_phases() {
assert_eq!(ssh_winding(1.0, 2.0, 64), 1);
assert_eq!(ssh_winding(2.0, 1.0, 64), 0);
}
#[test]
fn three_qwz_integrators_agree() {
for &m in &[-2.5_f64, -1.5, -0.5, 0.5, 1.5, 2.5] {
let fhs = qwz_chern(m, 32);
let proj = chern_qwz_proj(m, 32).unwrap();
let pt = chern_qwz_parallel_transport(m, 32).unwrap();
assert_eq!(
fhs, proj,
"FHS != proj at m={m}: {fhs} vs {proj}"
);
assert_eq!(
fhs, pt,
"FHS != parallel-transport at m={m}: {fhs} vs {pt}"
);
}
}
#[test]
fn kane_mele_z2_phase_window() {
assert_eq!(
kane_mele_z2(1.0, 0.06, 0.0, 0.10, 24).unwrap(),
1
);
assert_eq!(
kane_mele_z2(1.0, 0.06, 0.0, 0.40, 24).unwrap(),
0
);
}
#[test]
fn kane_mele_accepts_nonzero_rashba() {
let r = kane_mele_z2(1.0, 0.06, 0.05, 0.10, 24);
assert!(
r.is_ok(),
"v0.10.0 should accept lambda_r != 0, got {:?}", r,
);
}
#[test]
fn bhz_z2_lattice_window() {
assert_eq!(bhz_z2(1.0, 1.0, 3.0, 24).unwrap(), 1);
assert_eq!(bhz_z2(1.0, 1.0, -1.0, 24).unwrap(), 0);
assert_eq!(bhz_z2(1.0, 1.0, 9.0, 24).unwrap(), 0);
}
#[test]
fn kitaev_chain_majorana_window() {
assert_eq!(kitaev_chain_z2(1.0, 0.5, 1.0).unwrap(), 1);
assert_eq!(kitaev_chain_z2(1.0, -1.5, 1.0).unwrap(), 1);
assert_eq!(kitaev_chain_z2(1.0, 2.5, 1.0).unwrap(), 0);
}
#[test]
fn hofstadter_lowest_band_chern_plus_one() {
for &q in &[3_usize, 4, 5] {
let c = hofstadter_chern(1, q, 1, 1.0, 24).unwrap();
assert_eq!(c, 1, "q={q}: expected +1, got {c}");
}
}
#[test]
fn invalid_inputs_are_rejected() {
assert!(chern_qwz_proj(0.0, 2).is_err());
assert!(kane_mele_z2(1.0, 0.06, 0.0, 0.1, 7).is_err()); assert!(hofstadter_chern(1, 1, 1, 1.0, 24).is_err()); assert!(hofstadter_chern(1, 3, 3, 1.0, 24).is_err()); }
#[test]
#[cfg(not(target_arch = "aarch64"))]
fn kpm_bulk_topological() {
let sys = ChernKpm::new(12, -1.0, 100).unwrap();
let map = sys.bulk_map(4, 8).unwrap();
let mean: f64 = map.iter().sum::<f64>() / map.len() as f64;
assert!((mean - 1.0).abs() < 0.25, "bulk mean = {mean}");
}
}