1use crate::graph_types::SimpleGraph;
7use crate::traits::Problem;
8use crate::types::{EnergyMode, ProblemSize, SolutionSize};
9use serde::{Deserialize, Serialize};
10
11#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
16pub struct VarBounds {
17 pub lower: Option<i64>,
19 pub upper: Option<i64>,
21}
22
23impl VarBounds {
24 pub fn binary() -> Self {
26 Self {
27 lower: Some(0),
28 upper: Some(1),
29 }
30 }
31
32 pub fn non_negative() -> Self {
34 Self {
35 lower: Some(0),
36 upper: None,
37 }
38 }
39
40 pub fn unbounded() -> Self {
42 Self {
43 lower: None,
44 upper: None,
45 }
46 }
47
48 pub fn bounded(lo: i64, hi: i64) -> Self {
50 Self {
51 lower: Some(lo),
52 upper: Some(hi),
53 }
54 }
55
56 pub fn contains(&self, value: i64) -> bool {
58 if let Some(lo) = self.lower {
59 if value < lo {
60 return false;
61 }
62 }
63 if let Some(hi) = self.upper {
64 if value > hi {
65 return false;
66 }
67 }
68 true
69 }
70
71 pub fn num_values(&self) -> Option<usize> {
74 match (self.lower, self.upper) {
75 (Some(lo), Some(hi)) => {
76 if hi >= lo {
77 Some((hi - lo + 1) as usize)
78 } else {
79 Some(0)
80 }
81 }
82 _ => None,
83 }
84 }
85}
86
87#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
89pub enum Comparison {
90 Le,
92 Ge,
94 Eq,
96}
97
98impl Comparison {
99 pub fn holds(&self, lhs: f64, rhs: f64) -> bool {
101 match self {
102 Comparison::Le => lhs <= rhs,
103 Comparison::Ge => lhs >= rhs,
104 Comparison::Eq => (lhs - rhs).abs() < 1e-9,
105 }
106 }
107}
108
109#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
113pub struct LinearConstraint {
114 pub terms: Vec<(usize, f64)>,
116 pub cmp: Comparison,
118 pub rhs: f64,
120}
121
122impl LinearConstraint {
123 pub fn new(terms: Vec<(usize, f64)>, cmp: Comparison, rhs: f64) -> Self {
125 Self { terms, cmp, rhs }
126 }
127
128 pub fn le(terms: Vec<(usize, f64)>, rhs: f64) -> Self {
130 Self::new(terms, Comparison::Le, rhs)
131 }
132
133 pub fn ge(terms: Vec<(usize, f64)>, rhs: f64) -> Self {
135 Self::new(terms, Comparison::Ge, rhs)
136 }
137
138 pub fn eq(terms: Vec<(usize, f64)>, rhs: f64) -> Self {
140 Self::new(terms, Comparison::Eq, rhs)
141 }
142
143 pub fn evaluate_lhs(&self, values: &[i64]) -> f64 {
145 self.terms
146 .iter()
147 .map(|&(var, coef)| coef * values.get(var).copied().unwrap_or(0) as f64)
148 .sum()
149 }
150
151 pub fn is_satisfied(&self, values: &[i64]) -> bool {
153 let lhs = self.evaluate_lhs(values);
154 self.cmp.holds(lhs, self.rhs)
155 }
156
157 pub fn variables(&self) -> Vec<usize> {
159 self.terms.iter().map(|&(var, _)| var).collect()
160 }
161}
162
163#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
165pub enum ObjectiveSense {
166 Maximize,
168 Minimize,
170}
171
172impl From<EnergyMode> for ObjectiveSense {
173 fn from(mode: EnergyMode) -> Self {
174 match mode {
175 EnergyMode::LargerSizeIsBetter => ObjectiveSense::Maximize,
176 EnergyMode::SmallerSizeIsBetter => ObjectiveSense::Minimize,
177 }
178 }
179}
180
181impl From<ObjectiveSense> for EnergyMode {
182 fn from(sense: ObjectiveSense) -> Self {
183 match sense {
184 ObjectiveSense::Maximize => EnergyMode::LargerSizeIsBetter,
185 ObjectiveSense::Minimize => EnergyMode::SmallerSizeIsBetter,
186 }
187 }
188}
189
190#[derive(Debug, Clone, Serialize, Deserialize)]
217pub struct ILP {
218 pub num_vars: usize,
220 pub bounds: Vec<VarBounds>,
222 pub constraints: Vec<LinearConstraint>,
224 pub objective: Vec<(usize, f64)>,
226 pub sense: ObjectiveSense,
228}
229
230impl ILP {
231 pub fn new(
243 num_vars: usize,
244 bounds: Vec<VarBounds>,
245 constraints: Vec<LinearConstraint>,
246 objective: Vec<(usize, f64)>,
247 sense: ObjectiveSense,
248 ) -> Self {
249 assert_eq!(
250 bounds.len(),
251 num_vars,
252 "bounds length must match num_vars"
253 );
254 Self {
255 num_vars,
256 bounds,
257 constraints,
258 objective,
259 sense,
260 }
261 }
262
263 pub fn binary(
267 num_vars: usize,
268 constraints: Vec<LinearConstraint>,
269 objective: Vec<(usize, f64)>,
270 sense: ObjectiveSense,
271 ) -> Self {
272 let bounds = vec![VarBounds::binary(); num_vars];
273 Self::new(num_vars, bounds, constraints, objective, sense)
274 }
275
276 pub fn empty() -> Self {
278 Self {
279 num_vars: 0,
280 bounds: vec![],
281 constraints: vec![],
282 objective: vec![],
283 sense: ObjectiveSense::Minimize,
284 }
285 }
286
287 pub fn evaluate_objective(&self, values: &[i64]) -> f64 {
289 self.objective
290 .iter()
291 .map(|&(var, coef)| coef * values.get(var).copied().unwrap_or(0) as f64)
292 .sum()
293 }
294
295 pub fn bounds_satisfied(&self, values: &[i64]) -> bool {
297 if values.len() != self.num_vars {
298 return false;
299 }
300 for (i, &value) in values.iter().enumerate() {
301 if !self.bounds[i].contains(value) {
302 return false;
303 }
304 }
305 true
306 }
307
308 pub fn constraints_satisfied(&self, values: &[i64]) -> bool {
310 self.constraints.iter().all(|c| c.is_satisfied(values))
311 }
312
313 pub fn is_feasible(&self, values: &[i64]) -> bool {
315 self.bounds_satisfied(values) && self.constraints_satisfied(values)
316 }
317
318 fn config_to_values(&self, config: &[usize]) -> Vec<i64> {
321 config
322 .iter()
323 .enumerate()
324 .map(|(i, &c)| {
325 let lo = self.bounds.get(i).and_then(|b| b.lower).unwrap_or(0);
326 lo + c as i64
327 })
328 .collect()
329 }
330}
331
332impl Problem for ILP {
333 const NAME: &'static str = "ILP";
334 type GraphType = SimpleGraph;
335 type Weight = f64;
336 type Size = f64;
337
338 fn num_variables(&self) -> usize {
339 self.num_vars
340 }
341
342 fn num_flavors(&self) -> usize {
343 self.bounds
346 .iter()
347 .map(|b| b.num_values().unwrap_or(usize::MAX))
348 .max()
349 .unwrap_or(2)
350 }
351
352 fn problem_size(&self) -> ProblemSize {
353 ProblemSize::new(vec![
354 ("num_vars", self.num_vars),
355 ("num_constraints", self.constraints.len()),
356 ])
357 }
358
359 fn energy_mode(&self) -> EnergyMode {
360 match self.sense {
361 ObjectiveSense::Maximize => EnergyMode::LargerSizeIsBetter,
362 ObjectiveSense::Minimize => EnergyMode::SmallerSizeIsBetter,
363 }
364 }
365
366 fn solution_size(&self, config: &[usize]) -> SolutionSize<f64> {
367 let values = self.config_to_values(config);
369
370 let bounds_ok = self.bounds_satisfied(&values);
372
373 let constraints_ok = self.constraints_satisfied(&values);
375
376 let is_valid = bounds_ok && constraints_ok;
377
378 let obj = self.evaluate_objective(&values);
380
381 SolutionSize::new(obj, is_valid)
382 }
383}
384
385#[cfg(test)]
386mod tests {
387 use super::*;
388 use crate::solvers::{BruteForce, Solver};
389
390 #[test]
395 fn test_varbounds_binary() {
396 let bounds = VarBounds::binary();
397 assert_eq!(bounds.lower, Some(0));
398 assert_eq!(bounds.upper, Some(1));
399 assert!(bounds.contains(0));
400 assert!(bounds.contains(1));
401 assert!(!bounds.contains(-1));
402 assert!(!bounds.contains(2));
403 assert_eq!(bounds.num_values(), Some(2));
404 }
405
406 #[test]
407 fn test_varbounds_non_negative() {
408 let bounds = VarBounds::non_negative();
409 assert_eq!(bounds.lower, Some(0));
410 assert_eq!(bounds.upper, None);
411 assert!(bounds.contains(0));
412 assert!(bounds.contains(100));
413 assert!(!bounds.contains(-1));
414 assert_eq!(bounds.num_values(), None);
415 }
416
417 #[test]
418 fn test_varbounds_unbounded() {
419 let bounds = VarBounds::unbounded();
420 assert_eq!(bounds.lower, None);
421 assert_eq!(bounds.upper, None);
422 assert!(bounds.contains(-1000));
423 assert!(bounds.contains(0));
424 assert!(bounds.contains(1000));
425 assert_eq!(bounds.num_values(), None);
426 }
427
428 #[test]
429 fn test_varbounds_bounded() {
430 let bounds = VarBounds::bounded(-5, 10);
431 assert_eq!(bounds.lower, Some(-5));
432 assert_eq!(bounds.upper, Some(10));
433 assert!(bounds.contains(-5));
434 assert!(bounds.contains(0));
435 assert!(bounds.contains(10));
436 assert!(!bounds.contains(-6));
437 assert!(!bounds.contains(11));
438 assert_eq!(bounds.num_values(), Some(16)); }
440
441 #[test]
442 fn test_varbounds_default() {
443 let bounds = VarBounds::default();
444 assert_eq!(bounds.lower, None);
445 assert_eq!(bounds.upper, None);
446 }
447
448 #[test]
449 fn test_varbounds_empty_range() {
450 let bounds = VarBounds::bounded(5, 3); assert_eq!(bounds.num_values(), Some(0));
452 }
453
454 #[test]
459 fn test_comparison_le() {
460 let cmp = Comparison::Le;
461 assert!(cmp.holds(5.0, 10.0));
462 assert!(cmp.holds(10.0, 10.0));
463 assert!(!cmp.holds(11.0, 10.0));
464 }
465
466 #[test]
467 fn test_comparison_ge() {
468 let cmp = Comparison::Ge;
469 assert!(cmp.holds(10.0, 5.0));
470 assert!(cmp.holds(10.0, 10.0));
471 assert!(!cmp.holds(4.0, 5.0));
472 }
473
474 #[test]
475 fn test_comparison_eq() {
476 let cmp = Comparison::Eq;
477 assert!(cmp.holds(10.0, 10.0));
478 assert!(!cmp.holds(10.0, 10.1));
479 assert!(!cmp.holds(9.9, 10.0));
480 assert!(cmp.holds(10.0, 10.0 + 1e-10));
482 }
483
484 #[test]
489 fn test_linear_constraint_le() {
490 let constraint = LinearConstraint::le(vec![(0, 1.0), (1, 2.0)], 5.0);
492 assert_eq!(constraint.cmp, Comparison::Le);
493 assert_eq!(constraint.rhs, 5.0);
494
495 assert!(constraint.is_satisfied(&[1, 2]));
497 assert!(!constraint.is_satisfied(&[2, 2]));
499 }
500
501 #[test]
502 fn test_linear_constraint_ge() {
503 let constraint = LinearConstraint::ge(vec![(0, 1.0), (1, 1.0)], 3.0);
505 assert_eq!(constraint.cmp, Comparison::Ge);
506
507 assert!(constraint.is_satisfied(&[2, 2])); assert!(constraint.is_satisfied(&[1, 2])); assert!(!constraint.is_satisfied(&[1, 1])); }
511
512 #[test]
513 fn test_linear_constraint_eq() {
514 let constraint = LinearConstraint::eq(vec![(0, 1.0), (1, 1.0)], 2.0);
516 assert_eq!(constraint.cmp, Comparison::Eq);
517
518 assert!(constraint.is_satisfied(&[1, 1])); assert!(!constraint.is_satisfied(&[1, 2])); assert!(!constraint.is_satisfied(&[0, 1])); }
522
523 #[test]
524 fn test_linear_constraint_evaluate_lhs() {
525 let constraint = LinearConstraint::le(vec![(0, 3.0), (2, -1.0)], 10.0);
526 assert!((constraint.evaluate_lhs(&[2, 5, 7]) - (-1.0)).abs() < 1e-9);
528 }
529
530 #[test]
531 fn test_linear_constraint_variables() {
532 let constraint = LinearConstraint::le(vec![(0, 1.0), (3, 2.0), (5, -1.0)], 10.0);
533 assert_eq!(constraint.variables(), vec![0, 3, 5]);
534 }
535
536 #[test]
537 fn test_linear_constraint_out_of_bounds() {
538 let constraint = LinearConstraint::le(vec![(5, 1.0)], 10.0);
540 assert!(constraint.is_satisfied(&[1, 2, 3]));
542 }
543
544 #[test]
549 fn test_objective_sense_from_energy_mode() {
550 assert_eq!(
551 ObjectiveSense::from(EnergyMode::LargerSizeIsBetter),
552 ObjectiveSense::Maximize
553 );
554 assert_eq!(
555 ObjectiveSense::from(EnergyMode::SmallerSizeIsBetter),
556 ObjectiveSense::Minimize
557 );
558 }
559
560 #[test]
561 fn test_energy_mode_from_objective_sense() {
562 assert_eq!(
563 EnergyMode::from(ObjectiveSense::Maximize),
564 EnergyMode::LargerSizeIsBetter
565 );
566 assert_eq!(
567 EnergyMode::from(ObjectiveSense::Minimize),
568 EnergyMode::SmallerSizeIsBetter
569 );
570 }
571
572 #[test]
577 fn test_ilp_new() {
578 let ilp = ILP::new(
579 2,
580 vec![VarBounds::binary(), VarBounds::binary()],
581 vec![LinearConstraint::le(vec![(0, 1.0), (1, 1.0)], 1.0)],
582 vec![(0, 1.0), (1, 2.0)],
583 ObjectiveSense::Maximize,
584 );
585 assert_eq!(ilp.num_vars, 2);
586 assert_eq!(ilp.bounds.len(), 2);
587 assert_eq!(ilp.constraints.len(), 1);
588 assert_eq!(ilp.objective.len(), 2);
589 assert_eq!(ilp.sense, ObjectiveSense::Maximize);
590 }
591
592 #[test]
593 #[should_panic(expected = "bounds length must match num_vars")]
594 fn test_ilp_new_mismatched_bounds() {
595 ILP::new(
596 3,
597 vec![VarBounds::binary(), VarBounds::binary()], vec![],
599 vec![],
600 ObjectiveSense::Minimize,
601 );
602 }
603
604 #[test]
605 fn test_ilp_binary() {
606 let ilp = ILP::binary(
607 3,
608 vec![],
609 vec![(0, 1.0), (1, 1.0), (2, 1.0)],
610 ObjectiveSense::Minimize,
611 );
612 assert_eq!(ilp.num_vars, 3);
613 assert!(ilp.bounds.iter().all(|b| *b == VarBounds::binary()));
614 }
615
616 #[test]
617 fn test_ilp_empty() {
618 let ilp = ILP::empty();
619 assert_eq!(ilp.num_vars, 0);
620 assert!(ilp.bounds.is_empty());
621 assert!(ilp.constraints.is_empty());
622 assert!(ilp.objective.is_empty());
623 }
624
625 #[test]
626 fn test_ilp_evaluate_objective() {
627 let ilp = ILP::binary(
628 3,
629 vec![],
630 vec![(0, 2.0), (1, 3.0), (2, -1.0)],
631 ObjectiveSense::Maximize,
632 );
633 assert!((ilp.evaluate_objective(&[1, 1, 0]) - 5.0).abs() < 1e-9);
635 assert!((ilp.evaluate_objective(&[0, 0, 1]) - (-1.0)).abs() < 1e-9);
637 }
638
639 #[test]
640 fn test_ilp_bounds_satisfied() {
641 let ilp = ILP::new(
642 2,
643 vec![VarBounds::bounded(0, 5), VarBounds::bounded(-2, 2)],
644 vec![],
645 vec![],
646 ObjectiveSense::Minimize,
647 );
648 assert!(ilp.bounds_satisfied(&[0, 0]));
649 assert!(ilp.bounds_satisfied(&[5, 2]));
650 assert!(ilp.bounds_satisfied(&[3, -2]));
651 assert!(!ilp.bounds_satisfied(&[6, 0])); assert!(!ilp.bounds_satisfied(&[0, 3])); assert!(!ilp.bounds_satisfied(&[0])); }
655
656 #[test]
657 fn test_ilp_constraints_satisfied() {
658 let ilp = ILP::binary(
659 3,
660 vec![
661 LinearConstraint::le(vec![(0, 1.0), (1, 1.0)], 1.0), LinearConstraint::ge(vec![(2, 1.0)], 0.0), ],
664 vec![],
665 ObjectiveSense::Minimize,
666 );
667 assert!(ilp.constraints_satisfied(&[0, 0, 1]));
668 assert!(ilp.constraints_satisfied(&[1, 0, 0]));
669 assert!(ilp.constraints_satisfied(&[0, 1, 1]));
670 assert!(!ilp.constraints_satisfied(&[1, 1, 0])); }
672
673 #[test]
674 fn test_ilp_is_feasible() {
675 let ilp = ILP::binary(
676 2,
677 vec![LinearConstraint::le(vec![(0, 1.0), (1, 1.0)], 1.0)],
678 vec![(0, 1.0), (1, 1.0)],
679 ObjectiveSense::Maximize,
680 );
681 assert!(ilp.is_feasible(&[0, 0]));
682 assert!(ilp.is_feasible(&[1, 0]));
683 assert!(ilp.is_feasible(&[0, 1]));
684 assert!(!ilp.is_feasible(&[1, 1])); assert!(!ilp.is_feasible(&[2, 0])); }
687
688 #[test]
693 fn test_ilp_num_variables() {
694 let ilp = ILP::binary(5, vec![], vec![], ObjectiveSense::Minimize);
695 assert_eq!(ilp.num_variables(), 5);
696 }
697
698 #[test]
699 fn test_ilp_num_flavors_binary() {
700 let ilp = ILP::binary(3, vec![], vec![], ObjectiveSense::Minimize);
701 assert_eq!(ilp.num_flavors(), 2);
702 }
703
704 #[test]
705 fn test_ilp_num_flavors_mixed() {
706 let ilp = ILP::new(
707 3,
708 vec![
709 VarBounds::binary(),
710 VarBounds::bounded(0, 5),
711 VarBounds::bounded(-1, 1),
712 ],
713 vec![],
714 vec![],
715 ObjectiveSense::Minimize,
716 );
717 assert_eq!(ilp.num_flavors(), 6); }
719
720 #[test]
721 fn test_ilp_num_flavors_unbounded() {
722 let ilp = ILP::new(
723 2,
724 vec![VarBounds::binary(), VarBounds::unbounded()],
725 vec![],
726 vec![],
727 ObjectiveSense::Minimize,
728 );
729 assert_eq!(ilp.num_flavors(), usize::MAX);
730 }
731
732 #[test]
733 fn test_ilp_num_flavors_empty() {
734 let ilp = ILP::empty();
735 assert_eq!(ilp.num_flavors(), 2); }
737
738 #[test]
739 fn test_ilp_problem_size() {
740 let ilp = ILP::binary(
741 4,
742 vec![
743 LinearConstraint::le(vec![(0, 1.0)], 1.0),
744 LinearConstraint::le(vec![(1, 1.0)], 1.0),
745 ],
746 vec![],
747 ObjectiveSense::Minimize,
748 );
749 let size = ilp.problem_size();
750 assert_eq!(size.get("num_vars"), Some(4));
751 assert_eq!(size.get("num_constraints"), Some(2));
752 }
753
754 #[test]
755 fn test_ilp_energy_mode() {
756 let max_ilp = ILP::binary(2, vec![], vec![], ObjectiveSense::Maximize);
757 let min_ilp = ILP::binary(2, vec![], vec![], ObjectiveSense::Minimize);
758
759 assert!(max_ilp.energy_mode().is_maximization());
760 assert!(min_ilp.energy_mode().is_minimization());
761 }
762
763 #[test]
764 fn test_ilp_solution_size_valid() {
765 let ilp = ILP::binary(
767 2,
768 vec![LinearConstraint::le(vec![(0, 1.0), (1, 1.0)], 1.0)],
769 vec![(0, 1.0), (1, 2.0)],
770 ObjectiveSense::Maximize,
771 );
772
773 let sol = ilp.solution_size(&[0, 1]);
775 assert!(sol.is_valid);
776 assert!((sol.size - 2.0).abs() < 1e-9);
777
778 let sol = ilp.solution_size(&[1, 0]);
780 assert!(sol.is_valid);
781 assert!((sol.size - 1.0).abs() < 1e-9);
782 }
783
784 #[test]
785 fn test_ilp_solution_size_invalid() {
786 let ilp = ILP::binary(
788 2,
789 vec![LinearConstraint::le(vec![(0, 1.0), (1, 1.0)], 1.0)],
790 vec![(0, 1.0), (1, 2.0)],
791 ObjectiveSense::Maximize,
792 );
793
794 let sol = ilp.solution_size(&[1, 1]);
796 assert!(!sol.is_valid);
797 assert!((sol.size - 3.0).abs() < 1e-9);
798 }
799
800 #[test]
801 fn test_ilp_solution_size_with_offset_bounds() {
802 let ilp = ILP::new(
804 2,
805 vec![VarBounds::bounded(1, 3), VarBounds::bounded(-1, 1)],
806 vec![],
807 vec![(0, 1.0), (1, 1.0)],
808 ObjectiveSense::Maximize,
809 );
810
811 let sol = ilp.solution_size(&[0, 0]);
813 assert!(sol.is_valid);
814 assert!((sol.size - 0.0).abs() < 1e-9);
815
816 let sol = ilp.solution_size(&[2, 2]);
818 assert!(sol.is_valid);
819 assert!((sol.size - 4.0).abs() < 1e-9);
820 }
821
822 #[test]
823 fn test_ilp_brute_force_maximization() {
824 let ilp = ILP::binary(
826 2,
827 vec![LinearConstraint::le(vec![(0, 1.0), (1, 1.0)], 1.0)],
828 vec![(0, 1.0), (1, 2.0)],
829 ObjectiveSense::Maximize,
830 );
831
832 let solver = BruteForce::new();
833 let solutions = solver.find_best(&ilp);
834
835 assert_eq!(solutions.len(), 1);
837 assert_eq!(solutions[0], vec![0, 1]);
838 }
839
840 #[test]
841 fn test_ilp_brute_force_minimization() {
842 let ilp = ILP::binary(
844 2,
845 vec![LinearConstraint::ge(vec![(0, 1.0), (1, 1.0)], 1.0)],
846 vec![(0, 1.0), (1, 1.0)],
847 ObjectiveSense::Minimize,
848 );
849
850 let solver = BruteForce::new();
851 let solutions = solver.find_best(&ilp);
852
853 assert_eq!(solutions.len(), 2);
855 for sol in &solutions {
856 let size = ilp.solution_size(sol);
857 assert!(size.is_valid);
858 assert!((size.size - 1.0).abs() < 1e-9);
859 }
860 }
861
862 #[test]
863 fn test_ilp_brute_force_no_feasible() {
864 let ilp = ILP::binary(
866 1,
867 vec![
868 LinearConstraint::ge(vec![(0, 1.0)], 1.0),
869 LinearConstraint::le(vec![(0, 1.0)], 0.0),
870 ],
871 vec![(0, 1.0)],
872 ObjectiveSense::Minimize,
873 );
874
875 let solver = BruteForce::new();
876 let solutions = solver.find_best(&ilp);
877
878 assert!(solutions.is_empty());
880 }
881
882 #[test]
883 fn test_ilp_unconstrained() {
884 let ilp = ILP::binary(
886 2,
887 vec![],
888 vec![(0, 1.0), (1, 1.0)],
889 ObjectiveSense::Maximize,
890 );
891
892 let solver = BruteForce::new();
893 let solutions = solver.find_best(&ilp);
894
895 assert_eq!(solutions.len(), 1);
897 assert_eq!(solutions[0], vec![1, 1]);
898 }
899
900 #[test]
901 fn test_ilp_equality_constraint() {
902 let ilp = ILP::binary(
904 2,
905 vec![LinearConstraint::eq(vec![(0, 1.0), (1, 1.0)], 1.0)],
906 vec![(0, 1.0)],
907 ObjectiveSense::Minimize,
908 );
909
910 let solver = BruteForce::new();
911 let solutions = solver.find_best(&ilp);
912
913 assert_eq!(solutions.len(), 1);
915 assert_eq!(solutions[0], vec![0, 1]);
916 }
917
918 #[test]
919 fn test_ilp_multiple_constraints() {
920 let ilp = ILP::binary(
925 3,
926 vec![
927 LinearConstraint::le(vec![(0, 1.0), (1, 1.0)], 1.0),
928 LinearConstraint::le(vec![(1, 1.0), (2, 1.0)], 1.0),
929 ],
930 vec![(0, 1.0), (1, 1.0), (2, 1.0)],
931 ObjectiveSense::Maximize,
932 );
933
934 let solver = BruteForce::new();
935 let solutions = solver.find_best(&ilp);
936
937 assert_eq!(solutions.len(), 1);
939 assert_eq!(solutions[0], vec![1, 0, 1]);
940 }
941
942 #[test]
943 fn test_ilp_config_to_values() {
944 let ilp = ILP::new(
945 3,
946 vec![
947 VarBounds::bounded(0, 2), VarBounds::bounded(-1, 1), VarBounds::bounded(5, 7), ],
951 vec![],
952 vec![],
953 ObjectiveSense::Minimize,
954 );
955
956 assert_eq!(ilp.config_to_values(&[0, 0, 0]), vec![0, -1, 5]);
958 assert_eq!(ilp.config_to_values(&[2, 2, 2]), vec![2, 1, 7]);
960 assert_eq!(ilp.config_to_values(&[1, 1, 1]), vec![1, 0, 6]);
962 }
963}