use nalgebra::{Matrix2, Matrix3};
use serde::{Deserialize, Serialize};
use crate::math::{is_minkowski_reduced_2d, lift_2d_to_3d, minkowski_reduce_2d};
use super::super::error::MoyoError;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Lattice2D {
pub basis: Matrix2<f64>,
}
impl Lattice2D {
pub fn from_basis(basis: Matrix2<f64>) -> Self {
Self { basis }
}
pub fn from_inplane_of(basis_3d: &Matrix3<f64>) -> Self {
debug_assert!(
basis_3d[(2, 0)].abs() < 1e-6 && basis_3d[(2, 1)].abs() < 1e-6,
"in-plane axes must be in the xy-plane (a_z = b_z = 0); got a_z={}, b_z={}",
basis_3d[(2, 0)],
basis_3d[(2, 1)],
);
Self::from_basis(Matrix2::new(
basis_3d[(0, 0)],
basis_3d[(0, 1)],
basis_3d[(1, 0)],
basis_3d[(1, 1)],
))
}
pub fn minkowski_reduce(&self) -> Result<(Self, Matrix2<i32>), MoyoError> {
let (reduced_basis, trans_mat) = minkowski_reduce_2d(&self.basis);
let reduced = Self {
basis: reduced_basis,
};
if !reduced.is_minkowski_reduced() {
return Err(MoyoError::MinkowskiReductionError);
}
Ok((reduced, trans_mat))
}
pub fn is_minkowski_reduced(&self) -> bool {
is_minkowski_reduced_2d(&self.basis)
}
pub(crate) fn lift_inplane_minkowski_reduce(
basis_3d: &Matrix3<f64>,
) -> Result<Matrix3<i32>, MoyoError> {
let (_, trans_mat_2d) = Self::from_inplane_of(basis_3d).minkowski_reduce()?;
Ok(lift_2d_to_3d(&trans_mat_2d))
}
}
#[cfg(test)]
mod tests {
use nalgebra::{Matrix2, Matrix3, matrix};
use super::Lattice2D;
#[test]
fn test_from_inplane_of_extracts_upper_left_block() {
let basis_3d: Matrix3<f64> = matrix![
1.0, 2.0, 99.0;
3.0, 4.0, 99.0;
0.0, 0.0, 99.0;
];
let l = Lattice2D::from_inplane_of(&basis_3d);
assert_eq!(l.basis, Matrix2::new(1.0, 2.0, 3.0, 4.0));
}
#[test]
fn test_minkowski_reduce_already_reduced_is_identity() {
let l = Lattice2D::from_basis(Matrix2::new(1.0, 0.0, 0.0, 1.0));
let (reduced, t) = l.minkowski_reduce().unwrap();
assert_eq!(reduced.basis, l.basis);
assert_eq!(t, Matrix2::new(1, 0, 0, 1));
assert!(reduced.is_minkowski_reduced());
}
#[test]
fn test_minkowski_reduce_skewed_round_trip() {
let l = Lattice2D::from_basis(Matrix2::new(1.0, 4.0, 0.0, 1.0));
let (reduced, t) = l.minkowski_reduce().unwrap();
assert!(reduced.is_minkowski_reduced());
let t_f = t.map(|e| e as f64);
let reconstructed = l.basis * t_f;
assert!((reconstructed - reduced.basis).norm() < 1e-12);
}
}