1use std::any::Any;
90use std::collections::{HashMap, HashSet};
91
92use apex_manifolds::{LieGroup, Tangent};
93use faer::Mat;
94use nalgebra::DVector;
95
96#[derive(Clone, Debug)]
119pub struct Variable<M: LieGroup> {
120 pub value: M,
122 pub fixed_indices: HashSet<usize>,
124 pub bounds: HashMap<usize, (f64, f64)>,
126 pub covariance: Option<faer::Mat<f64>>,
135}
136
137impl<M> Variable<M>
138where
139 M: LieGroup + Clone + 'static,
140 M::TangentVector: Tangent<M>,
141{
142 pub fn new(value: M) -> Self {
156 Variable {
157 value,
158 fixed_indices: HashSet::new(),
159 bounds: HashMap::new(),
160 covariance: None,
161 }
162 }
163
164 pub fn set_value(&mut self, value: M) {
169 self.value = value;
170 }
171
172 pub fn get_size(&self) -> usize {
180 self.value.tangent_dim()
181 }
182
183 pub fn plus(&self, tangent: &M::TangentVector) -> M {
209 self.value.plus(tangent, None, None)
210 }
211
212 pub fn minus(&self, other: &Self) -> M::TangentVector {
237 self.value.minus(&other.value, None, None)
238 }
239
240 pub fn get_covariance(&self) -> Option<&Mat<f64>> {
247 self.covariance.as_ref()
248 }
249
250 pub fn set_covariance(&mut self, cov: Mat<f64>) {
258 self.covariance = Some(cov);
259 }
260
261 pub fn clear_covariance(&mut self) {
263 self.covariance = None;
264 }
265}
266
267use apex_manifolds::rn::Rn;
269
270impl Variable<Rn> {
271 pub fn to_vector(&self) -> DVector<f64> {
273 self.value.data().clone()
274 }
275
276 pub fn from_vector(values: DVector<f64>) -> Self {
278 Self::new(Rn::new(values))
279 }
280
281 pub fn update_variable(&mut self, mut tangent_delta: DVector<f64>) {
283 for (&idx, &(lower, upper)) in &self.bounds {
285 tangent_delta[idx] = tangent_delta[idx].max(lower).min(upper);
286 }
287
288 for &index_to_fix in &self.fixed_indices {
290 tangent_delta[index_to_fix] = self.value.data()[index_to_fix];
291 }
292
293 self.value = Rn::new(tangent_delta);
294 }
295}
296
297pub trait ManifoldVariable: Send + Sync + 'static {
309 fn as_param_slice(&self) -> &[f64];
312 fn dof(&self) -> usize;
314 fn apply_tangent_step(&mut self, step: &[f64]);
317
318 fn set_fixed_indices(&mut self, indices: HashSet<usize>);
320 fn get_fixed_indices(&self) -> &HashSet<usize>;
321 fn set_bounds(&mut self, bounds: HashMap<usize, (f64, f64)>);
322 fn get_bounds(&self) -> &HashMap<usize, (f64, f64)>;
323 fn manifold_type_name(&self) -> &'static str;
325
326 fn free_dof(&self) -> usize {
334 self.dof() - self.get_fixed_indices().len()
335 }
336
337 fn local_free_offset(&self, local: usize) -> Option<usize> {
340 let fixed = self.get_fixed_indices();
341 if fixed.is_empty() {
342 return Some(local);
344 }
345 if fixed.contains(&local) {
346 return None;
347 }
348 Some(local - fixed.iter().filter(|&&i| i < local).count())
349 }
350
351 fn expand_free_step(&self, free_step: &[f64], out: &mut [f64]) {
354 let fixed = self.get_fixed_indices();
355 let dof = self.dof();
356 if fixed.is_empty() {
357 out[..dof].copy_from_slice(&free_step[..dof]);
358 return;
359 }
360 let mut free_idx = 0usize;
361 for (local, slot) in out.iter_mut().enumerate().take(dof) {
362 *slot = if fixed.contains(&local) {
363 0.0
364 } else {
365 let v = free_step.get(free_idx).copied().unwrap_or(0.0);
366 free_idx += 1;
367 v
368 };
369 }
370 }
371
372 fn set_covariance(&mut self, cov: faer::Mat<f64>);
374 fn covariance(&self) -> Option<&faer::Mat<f64>>;
375 fn clear_covariance(&mut self);
376
377 fn to_dvector(&self) -> DVector<f64>;
379
380 fn as_any(&self) -> &dyn Any;
382 fn as_any_mut(&mut self) -> &mut dyn Any;
383 fn clone_box(&self) -> Box<dyn ManifoldVariable>;
384}
385
386impl Clone for Box<dyn ManifoldVariable> {
387 fn clone(&self) -> Self {
388 self.clone_box()
389 }
390}
391
392impl<M> ManifoldVariable for Variable<M>
393where
394 M: LieGroup + Clone + Send + Sync + 'static,
395 M::TangentVector: Tangent<M> + Send + Sync + 'static,
396{
397 fn as_param_slice(&self) -> &[f64] {
398 self.value.as_param_slice()
399 }
400
401 fn dof(&self) -> usize {
402 self.value.tangent_dim()
403 }
404
405 fn apply_tangent_step(&mut self, step: &[f64]) {
406 let dof = self.dof();
407 if dof <= 16 {
410 let mut buf = [0f64; 16];
411 buf[..dof].copy_from_slice(&step[..dof]);
412 for &idx in &self.fixed_indices {
413 if idx < dof {
414 buf[idx] = 0.0;
415 }
416 }
417 let tangent = M::TangentVector::from_slice(&buf[..dof]);
418 self.value = self.value.plus(&tangent, None, None);
419 } else {
420 let mut v = step[..dof].to_vec();
421 for &idx in &self.fixed_indices {
422 if idx < dof {
423 v[idx] = 0.0;
424 }
425 }
426 let tangent = M::TangentVector::from_slice(&v);
427 self.value = self.value.plus(&tangent, None, None);
428 }
429 }
430
431 fn manifold_type_name(&self) -> &'static str {
432 M::NAME
433 }
434
435 fn set_fixed_indices(&mut self, indices: HashSet<usize>) {
436 self.fixed_indices = indices;
437 }
438
439 fn get_fixed_indices(&self) -> &HashSet<usize> {
440 &self.fixed_indices
441 }
442
443 fn set_bounds(&mut self, bounds: HashMap<usize, (f64, f64)>) {
444 self.bounds = bounds;
445 }
446
447 fn get_bounds(&self) -> &HashMap<usize, (f64, f64)> {
448 &self.bounds
449 }
450
451 fn set_covariance(&mut self, cov: faer::Mat<f64>) {
452 self.covariance = Some(cov);
453 }
454
455 fn covariance(&self) -> Option<&faer::Mat<f64>> {
456 self.covariance.as_ref()
457 }
458
459 fn clear_covariance(&mut self) {
460 self.covariance = None;
461 }
462
463 fn to_dvector(&self) -> DVector<f64> {
464 DVector::from_column_slice(self.value.as_param_slice())
465 }
466
467 fn as_any(&self) -> &dyn Any {
468 self
469 }
470
471 fn as_any_mut(&mut self) -> &mut dyn Any {
472 self
473 }
474
475 fn clone_box(&self) -> Box<dyn ManifoldVariable> {
476 Box::new(self.clone())
477 }
478}
479
480#[cfg(test)]
481mod tests {
482 use super::*;
483 use apex_manifolds::{rn::Rn, se2::SE2, se3::SE3, so2::SO2, so3::SO3};
484 use nalgebra::{DVector, Quaternion, Vector3};
485 use std;
486
487 #[test]
488 fn test_variable_creation_rn() {
489 let vec_data = DVector::from_vec(vec![1.0, 2.0, 3.0, 4.0, 5.0]);
490 let rn_value = Rn::new(vec_data);
491 let variable = Variable::new(rn_value);
492
493 assert_eq!(variable.get_size(), 5);
495 assert!(variable.fixed_indices.is_empty());
496 assert!(variable.bounds.is_empty());
497 }
498
499 #[test]
500 fn test_variable_creation_se2() {
501 let se2 = SE2::from_xy_angle(1.0, 2.0, 0.5);
502 let variable = Variable::new(se2);
503
504 assert_eq!(variable.get_size(), SE2::DOF);
505 assert!(variable.fixed_indices.is_empty());
506 assert!(variable.bounds.is_empty());
507 }
508
509 #[test]
510 fn test_variable_creation_se3() {
511 let se3 = SE3::from_translation_quaternion(
512 Vector3::new(1.0, 2.0, 3.0),
513 Quaternion::new(1.0, 0.0, 0.0, 0.0),
514 );
515 let variable = Variable::new(se3);
516
517 assert_eq!(variable.get_size(), SE3::DOF);
518 assert!(variable.fixed_indices.is_empty());
519 assert!(variable.bounds.is_empty());
520 }
521
522 #[test]
523 fn test_variable_creation_so2() {
524 let so2 = SO2::from_angle(0.5);
525 let variable = Variable::new(so2);
526
527 assert_eq!(variable.get_size(), SO2::DOF);
528 assert!(variable.fixed_indices.is_empty());
529 assert!(variable.bounds.is_empty());
530 }
531
532 #[test]
533 fn test_variable_creation_so3() {
534 let so3 = SO3::from_euler_angles(0.1, 0.2, 0.3);
535 let variable = Variable::new(so3);
536
537 assert_eq!(variable.get_size(), SO3::DOF);
538 assert!(variable.fixed_indices.is_empty());
539 assert!(variable.bounds.is_empty());
540 }
541
542 #[test]
543 fn test_variable_set_value() {
544 let initial_vec = DVector::from_vec(vec![1.0, 2.0, 3.0]);
545 let mut variable = Variable::new(Rn::new(initial_vec));
546
547 let new_vec = DVector::from_vec(vec![4.0, 5.0, 6.0, 7.0]);
548 variable.set_value(Rn::new(new_vec));
549 assert_eq!(variable.get_size(), 4);
550
551 let se2_initial = SE2::from_xy_angle(0.0, 0.0, 0.0);
552 let mut se2_variable = Variable::new(se2_initial);
553
554 let se2_new = SE2::from_xy_angle(1.0, 2.0, std::f64::consts::PI / 4.0);
555 se2_variable.set_value(se2_new);
556 assert_eq!(se2_variable.get_size(), SE2::DOF);
557 }
558
559 #[test]
560 fn test_variable_plus_minus_operations() {
561 let se2_1 = SE2::from_xy_angle(2.0, 3.0, std::f64::consts::PI / 2.0);
563 let se2_2 = SE2::from_xy_angle(1.0, 1.0, std::f64::consts::PI / 4.0);
564 let var1 = Variable::new(se2_1);
565 let var2 = Variable::new(se2_2);
566
567 let diff_tangent = var1.minus(&var2);
568 let var2_updated = var2.plus(&diff_tangent);
569 let final_diff = var1.minus(&Variable::new(var2_updated));
570
571 assert!(DVector::from_column_slice(final_diff.as_slice()).norm() < 1e-10);
572 }
573
574 #[test]
575 fn test_variable_rn_plus_minus_operations() {
576 let rn_1 = Rn::new(DVector::from_vec(vec![1.0, 2.0, 3.0]));
578 let rn_2 = Rn::new(DVector::from_vec(vec![4.0, 5.0, 6.0]));
579 let var1 = Variable::new(rn_1);
580 let var2 = Variable::new(rn_2);
581
582 let diff_tangent = var1.minus(&var2);
584 assert_eq!(
585 diff_tangent.to_vector(),
586 DVector::from_vec(vec![-3.0, -3.0, -3.0])
587 );
588
589 let var2_updated = var2.plus(&diff_tangent);
591 assert_eq!(var2_updated.data(), &DVector::from_vec(vec![1.0, 2.0, 3.0]));
592
593 let final_diff = var1.minus(&Variable::new(var2_updated));
595 assert!(final_diff.to_vector().norm() < 1e-10);
596 }
597
598 #[test]
599 fn test_variable_update_with_bounds() {
600 let vec_data = DVector::from_vec(vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]);
601 let mut variable = Variable::new(Rn::new(vec_data));
602
603 variable.bounds.insert(0, (-1.0, 1.0));
604 variable.bounds.insert(2, (0.0, 5.0));
605
606 let new_values = DVector::from_vec(vec![-5.0, 10.0, -3.0, 20.0, 30.0, 40.0]);
607 variable.update_variable(new_values);
608
609 let result_vec = variable.to_vector();
610 assert!(result_vec.len() == 6);
611 }
612
613 #[test]
614 fn test_variable_update_with_fixed_indices() {
615 let vec_data = DVector::from_vec(vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0]);
616 let mut variable = Variable::new(Rn::new(vec_data.clone()));
617
618 variable.fixed_indices.insert(1);
619 variable.fixed_indices.insert(4);
620
621 let delta_values = DVector::from_vec(vec![9.0, 18.0, 27.0, 36.0, 45.0, 54.0, 63.0, 72.0]);
622 variable.update_variable(delta_values);
623
624 let result_vec = variable.to_vector();
625 assert_eq!(result_vec[1], 2.0);
626 assert_eq!(result_vec[4], 5.0);
627 assert!(result_vec.len() == 8);
628 }
629
630 #[test]
631 fn test_variable_combined_bounds_and_fixed() {
632 let vec_data = DVector::from_vec(vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0]);
633 let mut variable = Variable::new(Rn::new(vec_data.clone()));
634
635 variable.bounds.insert(0, (-2.0, 2.0));
636 variable.bounds.insert(3, (-1.0, 1.0));
637 variable.fixed_indices.insert(1);
638 variable.fixed_indices.insert(5);
639
640 let delta_values = DVector::from_vec(vec![-5.0, 100.0, 30.0, 10.0, 50.0, 600.0, 70.0]);
641 variable.update_variable(delta_values);
642
643 let result = variable.to_vector();
644 assert_eq!(result[1], 2.0);
645 assert_eq!(result[5], 6.0);
646 assert!(result.len() == 7);
647 }
648
649 #[test]
650 fn test_variable_type_safety() {
651 let se2_var = Variable::new(SE2::from_xy_angle(1.0, 2.0, 0.5));
652 let se3_var = Variable::new(SE3::from_translation_quaternion(
653 Vector3::new(1.0, 2.0, 3.0),
654 Quaternion::new(1.0, 0.0, 0.0, 0.0),
655 ));
656 let so2_var = Variable::new(SO2::from_angle(0.5));
657 let so3_var = Variable::new(SO3::from_euler_angles(0.1, 0.2, 0.3));
658 let rn_var = Variable::new(Rn::new(DVector::from_vec(vec![1.0, 2.0, 3.0])));
659
660 assert_eq!(se2_var.get_size(), SE2::DOF);
661 assert_eq!(se3_var.get_size(), SE3::DOF);
662 assert_eq!(so2_var.get_size(), SO2::DOF);
663 assert_eq!(so3_var.get_size(), SO3::DOF);
664 assert_eq!(rn_var.get_size(), 3);
665 }
666
667 #[test]
668 fn test_variable_vector_conversion_roundtrip() {
669 let original_data = DVector::from_vec(vec![1.0, 2.0, 3.0, 4.0, 5.0]);
670 let rn_var = Variable::new(Rn::new(original_data.clone()));
671 let vec_repr = rn_var.to_vector();
672 assert_eq!(vec_repr, original_data);
673
674 let reconstructed_var = Variable::<Rn>::from_vector(vec_repr);
675 assert_eq!(reconstructed_var.to_vector(), original_data);
676 }
677
678 #[test]
679 fn test_variable_manifold_operations_consistency() {
680 let rn_initial = Rn::new(DVector::from_vec(vec![1.0, 2.0, 3.0]));
682 let mut rn_var = Variable::new(rn_initial);
683 let rn_new_values = DVector::from_vec(vec![2.0, 3.0, 4.0]);
684 rn_var.update_variable(rn_new_values);
685
686 let rn_result = rn_var.to_vector();
687 assert_eq!(rn_result, DVector::from_vec(vec![2.0, 3.0, 4.0]));
688
689 let se2_1 = SE2::from_xy_angle(2.0, 3.0, std::f64::consts::PI / 2.0);
691 let se2_2 = SE2::from_xy_angle(1.0, 1.0, std::f64::consts::PI / 4.0);
692 let var1 = Variable::new(se2_1);
693 let var2 = Variable::new(se2_2);
694
695 let diff_tangent = var1.minus(&var2);
696 let var2_updated = var2.plus(&diff_tangent);
697 let final_diff = var1.minus(&Variable::new(var2_updated));
698
699 assert!(DVector::from_column_slice(final_diff.as_slice()).norm() < 1e-10);
701 }
702
703 #[test]
704 fn test_variable_constraints_interaction() {
705 let rn_data = DVector::from_vec(vec![0.0, 0.0, 0.0, 0.0, 0.0]);
706 let mut rn_var = Variable::new(Rn::new(rn_data));
707
708 rn_var.bounds.insert(0, (-1.0, 1.0));
709 rn_var.bounds.insert(2, (-10.0, 10.0));
710 rn_var.fixed_indices.insert(1);
711 rn_var.fixed_indices.insert(4);
712
713 let large_delta = DVector::from_vec(vec![5.0, 100.0, 15.0, 20.0, 200.0]);
714 rn_var.update_variable(large_delta);
715
716 let result = rn_var.to_vector();
717
718 assert_eq!(result[0], 1.0);
719 assert_eq!(result[1], 0.0);
720 assert_eq!(result[2], 10.0);
721 assert_eq!(result[3], 20.0);
722 assert_eq!(result[4], 0.0);
723 }
724}