geosets_rs/sets/
interval.rs1#![allow(unused)]
2use crate::linalg_utils::vector_leq;
3
4use super::*;
5use ndarray_rand::RandomExt;
6use ndarray_rand::rand_distr::{Exp1, Uniform};
7use thiserror::Error;
8
9#[derive(Clone, Debug)]
10#[allow(non_snake_case)]
11pub struct Interval {
12 lb: Array1<f64>,
13 ub: Array1<f64>,
14}
15
16#[derive(Error, Debug)]
17pub enum IntervalError {
18 #[error("Dimensions of lb {lb_dim:?} and ub {ub_dim:?} do not match")]
19 DimensionMismatch { lb_dim: usize, ub_dim: usize },
20 #[error("Lower bound {lb} must be less than or equal to upper bound {ub}")]
21 InvalidBounds { lb: Array1<f64>, ub: Array1<f64> },
22}
23
24#[allow(non_snake_case)]
25impl Interval {
26 pub fn new(lb: Array1<f64>, ub: Array1<f64>) -> Result<Interval, IntervalError> {
27 if lb.dim() != ub.dim() {
28 return Err(IntervalError::DimensionMismatch {
29 lb_dim: lb.dim(),
30 ub_dim: ub.dim(),
31 });
32 }
33
34 if lb.iter().zip(ub.iter()).any(|(a, b)| a > b) {
35 return Err(IntervalError::InvalidBounds { lb, ub });
36 }
37
38 Ok(Interval { lb, ub })
39 }
40
41 pub fn from_random(dim: usize) -> Result<Interval, IntervalError> {
42 let lb = Array1::random(dim, Uniform::new(-1.0, 0.0));
43 let ub = Array1::random(dim, Uniform::new(0.0, 1.0));
44
45 Interval::new(lb, ub)
46 }
47}
48
49#[allow(non_snake_case)]
50impl GeoSet for Interval {
51 fn from_unit_box(dim: usize) -> Self {
52 Interval {
53 lb: -Array::ones(dim),
54 ub: Array::ones(dim),
55 }
56 }
57
58 fn dim(&self) -> usize {
59 self.lb.dim()
60 }
61
62 fn empty(&self) -> Result<bool, SetOperationError> {
63 Ok(false)
64 }
65
66 fn to_vertices(&self) -> Result<Array2<f64>, SetOperationError> {
67 let mut vertices = Array2::zeros((1 << self.dim(), self.dim()));
68 for i in 0..(1 << self.dim()) {
69 for j in 0..self.dim() {
70 vertices[[i, j]] = if (i & (1 << j)) != 0 {
71 self.ub[j]
72 } else {
73 self.lb[j]
74 };
75 }
76 }
77 Ok(vertices)
78 }
79
80 fn center(&self) -> Result<Array1<f64>, SetOperationError> {
81 let center = (&self.lb + &self.ub) / 2.;
82 Ok(center)
83 }
84
85 fn support_function(
86 &self,
87 direction: Array1<f64>,
88 ) -> Result<(Array1<f64>, f64), SetOperationError> {
89 let support_vector = self
91 .lb
92 .iter()
93 .zip(self.ub.iter())
94 .zip(direction.iter())
95 .map(|((&lb, &ub), &d)| if d > 0.0 { ub } else { lb })
96 .collect::<Array1<f64>>();
97
98 let support_value = support_vector.dot(&direction);
99 Ok((support_vector, support_value))
100 }
101
102 fn volume(&self) -> Result<f64, SetOperationError> {
103 if self.degenerate() {
104 return Ok(0.0);
105 }
106
107 let volume = self
108 .lb
109 .iter()
110 .zip(self.ub.iter())
111 .map(|(lb, ub)| ub - lb)
112 .product();
113 Ok(volume)
114 }
115
116 fn minkowski_sum_(&mut self, other: &Self) -> Result<(), SetOperationError> {
117 self.lb += &other.lb;
118 self.ub += &other.ub;
119 Ok(())
120 }
121
122 fn matmul_(&mut self, mat: &Array2<f64>) -> Result<(), SetOperationError> {
123 self._check_operand_dim(mat.dim().0);
124
125 let mat_lb = mat.dot(&self.lb);
126 let mat_ub = mat.dot(&self.ub);
127
128 let mat_pos = mat.mapv(|x| x.max(0.0));
130 let mat_neg = mat.mapv(|x| x.min(0.0));
131
132 self.lb = mat_pos.dot(&self.lb) + mat_neg.dot(&self.ub);
135 self.ub = mat_pos.dot(&self.ub) + mat_neg.dot(&self.lb);
136
137 Ok(())
138 }
139
140 fn translate_(&mut self, vector: &Array1<f64>) -> Result<(), SetOperationError> {
141 self._check_operand_dim(vector.dim());
142 self.lb += vector;
143 self.ub += vector;
144 Ok(())
145 }
146
147 fn degenerate(&self) -> bool {
148 self.lb
149 .iter()
150 .zip(self.ub.iter())
151 .any(|(lb, ub)| (ub - lb).abs() < 1e-9)
152 }
153
154 fn contains_point(&self, point: &Array1<f64>) -> Result<bool, SetOperationError> {
155 self._check_operand_dim(point.dim());
156 Ok(vector_leq(&self.lb, point) && vector_leq(point, &self.ub))
157 }
158}