1use log::debug;
2use ndarray::{Array1, Array2, ArrayView1};
3use std::fmt::Display;
4
5#[derive(Debug)]
6pub enum SimplexError {
7 UnableToCalculateError,
8 UnlimitedError,
9 NoSolutionsError,
10 InvalidDataError,
11}
12
13pub struct Table {
14 pub table: Array2<f64>,
15 pub base_var: Vec<String>,
16 pub supp_var: Vec<String>,
17}
18
19impl Display for Table {
20 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
21 for i in self.base_var.iter() {
22 write!(f, "\t| {}\t", i)?;
23 }
24 write!(f, "\n")?;
25 for i in 0..self.table.nrows() {
26 write!(f, "{}", self.supp_var[i])?;
27 for j in self.table.row(i).iter() {
28 write!(f, "\t| {:.7}", j)?;
29 }
30 write!(f, "\n")?;
31 }
32 writeln!(f, "")?; self.print_function(f)?;
34 Ok(())
35 }
36}
37
38impl Clone for Table {
39 fn clone(&self) -> Table {
40 Table {
41 table: self.table.clone(),
42 base_var: self.base_var.clone(),
43 supp_var: self.supp_var.clone(),
44 }
45 }
46}
47
48impl Table {
49 pub fn new(
50 mut constr_coeff: Array2<f64>,
51 constr_val: Array1<f64>,
52 mut func_coeff: Array1<f64>,
53 minimisation_task: bool,
54 ) -> Table {
55 if minimisation_task {
56 for i in func_coeff.iter_mut() {
57 *i *= -1f64;
58 }
59 }
60 constr_coeff.push_row(ArrayView1::from(&func_coeff));
61 let mut constr_val_vec = constr_val.to_vec();
62 while constr_val_vec.len() < constr_coeff.nrows() {
63 constr_val_vec.push(0f64);
64 }
65 let constr_val = Array1::from_vec(constr_val_vec);
66 constr_coeff
67 .push_column(ArrayView1::from(&constr_val))
68 .unwrap();
69 let mut base_var = Vec::<String>::with_capacity(constr_coeff.ncols() - 1);
70 for i in 1..constr_coeff.ncols() {
71 base_var.push(i.to_string());
72 }
73 base_var.push("S".to_string());
74 let mut supp_var = Vec::<String>::with_capacity(constr_coeff.nrows());
75 for i in 0..constr_coeff.nrows() - 1 {
76 supp_var.push((i + constr_coeff.ncols()).to_string());
77 }
78 supp_var.push("F".to_string());
79 Table {
80 table: constr_coeff,
81 base_var,
82 supp_var,
83 }
84 }
85 pub fn optimise(&mut self) -> Result<(), crate::SimplexError> {
86 debug!("Beginning table:\n{}", self);
87 loop {
88 let negative_row = self.find_in_free_column();
89 if negative_row.is_none() {
90 break;
91 }
92 let negative_row = negative_row.unwrap();
93 debug!("Found negative free coeff in row {}", negative_row);
94 self.make_acceptable(negative_row)?;
95 debug!("Making acceptable:\n{}", self);
96 }
97 while !self.check_optimised() {
99 self.iterate()?;
100 debug!("Iteration:\n{}\n", self);
101 }
102 Ok(())
103 }
104
105 fn check_optimised(&self) -> bool {
106 for i in self.table.row(self.table.nrows() - 1).iter().enumerate() {
107 if i.0 >= self.table.ncols() - 1 {
109 break;
110 }
111 if i.1.is_sign_positive() {
112 return false;
113 }
114 }
115 return true;
116 }
117
118 fn find_in_free_column(&self) -> Option<usize> {
119 for i in self.table.column(self.table.ncols() - 1).iter().enumerate() {
120 if i.0 >= self.table.nrows() - 1 {
122 break;
123 }
124 if i.1.is_sign_negative() {
125 return Some(i.0);
126 }
127 }
128 return None;
129 }
130
131 fn make_acceptable(&mut self, negative: usize) -> Result<(), SimplexError> {
132 let j = {
133 let mut index = None;
134 for i in self.table.row(negative).iter().enumerate() {
135 if i.0 >= self.table.ncols() - 1 {
137 break;
138 }
139 if i.1.is_sign_negative() {
140 index = Some(i.0);
141 break;
142 }
143 }
144 if index.is_none() {
145 return Err(SimplexError::NoSolutionsError);
146 }
147 index.unwrap()
148 };
149 if let Some(i) = self.find_pivot_row(j) {
150 debug!("Transforming on pivot i: {}\tj: {}", i, j);
151 self.transform((i, j));
152 std::mem::swap(&mut self.base_var[j], &mut self.supp_var[i]);
153 return Ok(());
154 } else {
155 return Err(SimplexError::UnableToCalculateError);
156 }
157 }
158
159 fn iterate(&mut self) -> Result<(), SimplexError> {
160 let (i, j) = self.find_pivot()?;
161 debug!("Current pivot: i: {}\tj:{}", i, j);
162 self.transform((i, j));
163 std::mem::swap(&mut self.base_var[j], &mut self.supp_var[i]);
164 Ok(())
165 }
166
167 fn find_pivot(&self) -> Result<(usize, usize), SimplexError> {
168 let j = {
169 let mut index = None;
170 for j in self.table.row(self.table.nrows() - 1).iter().enumerate() {
171 if j.1.is_sign_positive() {
172 index = Some(j.0);
173 break;
174 }
175 }
176 if index.is_none() {
177 return Err(SimplexError::UnableToCalculateError);
178 }
179 index.unwrap()
180 };
181 let mut has_positive = false;
182 for i in self.table.column(j) {
183 if i.is_sign_positive() {
184 has_positive = true;
185 break;
186 }
187 }
188 if !has_positive {
189 return Err(SimplexError::UnlimitedError);
190 }
191 if let Some(i) = self.find_pivot_row(j) {
192 return Ok((i, j));
193 } else {
194 return Err(SimplexError::UnlimitedError);
195 }
196 }
197
198 fn find_pivot_row(&self, column: usize) -> Option<usize> {
199 let mut min = None;
200 let mut min_index = None;
201 for i in self.table.column(self.table.ncols() - 1).iter().enumerate() {
202 if i.0 >= self.table.nrows() - 1 {
204 break;
205 }
206 let relation = i.1 / self.table[[i.0, column]];
207 if (min.is_none() || relation < min.unwrap()) && relation.is_sign_positive() && relation.is_finite() {
208 min = Some(relation);
209 min_index = Some(i.0);
210 }
211 }
212 min_index
213 }
214
215 fn transform(&mut self, pivot: (usize, usize)) {
216 let pivot_cpy = self.table[[pivot.0, pivot.1]];
217 for i in 0..self.table.nrows() {
218 for j in 0..self.table.ncols() {
219 if i == pivot.0 || j == pivot.1 {
220 continue;
221 }
222 self.table[[i, j]] -=
223 self.table[[pivot.0, j]] * self.table[[i, pivot.1]] / pivot_cpy;
224 }
225 }
226 for i in self.table.row_mut(pivot.0) {
227 *i /= pivot_cpy;
228 }
229 for i in self.table.column_mut(pivot.1) {
230 *i /= -pivot_cpy;
231 }
232 self.table[[pivot.0, pivot.1]] = 1f64 / pivot_cpy;
233 }
234 pub fn print_function(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
235 for i in self.table.column(self.table.ncols() - 1).iter().enumerate() {
236 if i.0 >= self.table.nrows() - 1 {
237 break;
238 }
239 writeln!(f, "X_{} = {}", self.supp_var[i.0], i.1)?;
240 }
241 write!(f, "F = {}", self.table.column(self.table.ncols() - 1).last().unwrap())?;
242 Ok(())
243 }
244}