#![cfg(feature = "oxiz")]
use num_rational::Rational64;
use oxiz_core::ast::TermManager;
use oxiz_solver::{Solver, SolverResult};
#[derive(Debug, Clone, PartialEq)]
pub enum VerificationResult {
Verified,
Counterexample {
x: f64,
y: f64,
},
Unknown,
}
pub fn verify_helmert_scale_bounds(scale_ppm: f64) -> VerificationResult {
let mut solver = Solver::new();
let mut tm = TermManager::new();
solver.set_logic("QF_LRA");
let c100 = tm.mk_real(Rational64::new(100, 1));
let cn100 = tm.mk_real(Rational64::new(-100, 1));
let scale_num = (scale_ppm * 1_000_000.0).round() as i64;
let s_val = tm.mk_real(Rational64::new(scale_num, 1_000_000));
let lt = tm.mk_lt(s_val, cn100);
let gt = tm.mk_gt(s_val, c100);
let violation = tm.mk_or([lt, gt]);
solver.assert(violation, &mut tm);
match solver.check(&mut tm) {
SolverResult::Unsat => VerificationResult::Verified,
SolverResult::Sat => VerificationResult::Counterexample {
x: scale_ppm,
y: 0.0,
},
SolverResult::Unknown => VerificationResult::Unknown,
}
}
pub fn verify_invertible_at(det: f64, threshold: f64) -> VerificationResult {
let mut solver = Solver::new();
let mut tm = TermManager::new();
solver.set_logic("QF_LRA");
let det_num = (det * 1_000_000.0).round() as i64;
let det_val = tm.mk_real(Rational64::new(det_num, 1_000_000));
let thr_num_raw = (threshold * 1_000_000_000_000.0).round() as i64;
let thr_num = if thr_num_raw == 0 { 1 } else { thr_num_raw };
let thr_val = tm.mk_real(Rational64::new(thr_num, 1_000_000_000_000));
let neg_thr = tm.mk_neg(thr_val);
let ge = tm.mk_ge(det_val, neg_thr);
let le = tm.mk_le(det_val, thr_val);
let singular = tm.mk_and([ge, le]);
solver.assert(singular, &mut tm);
match solver.check(&mut tm) {
SolverResult::Unsat => VerificationResult::Verified,
SolverResult::Sat => VerificationResult::Counterexample {
x: det,
y: threshold,
},
SolverResult::Unknown => VerificationResult::Unknown,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn helmert_scale_within_bounds() {
assert_eq!(
verify_helmert_scale_bounds(0.0),
VerificationResult::Verified
);
assert_eq!(
verify_helmert_scale_bounds(50.0),
VerificationResult::Verified
);
assert_eq!(
verify_helmert_scale_bounds(-99.9),
VerificationResult::Verified
);
}
#[test]
fn helmert_scale_out_of_bounds() {
let result = verify_helmert_scale_bounds(200.0);
assert!(matches!(result, VerificationResult::Counterexample { .. }));
let result = verify_helmert_scale_bounds(-150.0);
assert!(matches!(result, VerificationResult::Counterexample { .. }));
}
#[test]
fn invertible_jacobian() {
assert_eq!(
verify_invertible_at(1.0, 1e-10),
VerificationResult::Verified
);
assert_eq!(
verify_invertible_at(-0.5, 1e-10),
VerificationResult::Verified
);
}
#[test]
fn singular_jacobian() {
let result = verify_invertible_at(0.0, 1e-10);
assert!(matches!(result, VerificationResult::Counterexample { .. }));
}
}