1use std::io;
8use std::io::{BufReader, BufRead};
9use std::convert::{TryFrom, TryInto};
10use std::fmt::{Display, Debug};
11use std::fs::File;
12use std::cmp::max;
13
14use rand::distributions::range::SampleRange;
15use std::ops::Sub;
16
17use crate::errors::Error;
18use crate::permutation::PermuPopulation;
19
20pub enum ProblemType {
23 Qap,
24 Pfsp,
25 Lop,
26}
27
28impl TryFrom<&str> for ProblemType {
29 type Error = Error;
30
31 fn try_from(path: &str) -> Result<Self, Self::Error> {
32 let splitted: Vec<&str> = path.split(".").collect();
33
34 if splitted.len() < 2 {
36 return Err(Error::Io(
37 io::Error::new(io::ErrorKind::InvalidInput,
38 "Instance extension not found")));
39 }
40
41 match splitted[1] {
42 "dat" => Ok(ProblemType::Qap),
43 "fsp" => Ok(ProblemType::Pfsp),
44 "lop" => Ok(ProblemType::Lop),
45 _ => Err(Error::Io(
46 io::Error::new(io::ErrorKind::InvalidInput,
47 format!("Wrong instance extension {}", splitted[1])))),
48 }
49 }
50}
51
52pub enum ProblemInstance {
54 Qap(usize, Vec<Vec<usize>>, Vec<Vec<usize>>),
56 Pfsp(usize, usize, Vec<Vec<usize>>),
58 Lop(usize, Vec<Vec<usize>>),
60}
61
62impl ProblemInstance {
63
64 pub fn size(&self) -> usize {
67 match self {
68 ProblemInstance::Qap(n, _, _) => *n,
69 ProblemInstance::Pfsp(n, _, _) => *n,
70 ProblemInstance::Lop(n, _) => *n,
71 }
72 }
73
74 pub fn load(path: &str) -> Result<Self, Error> {
80 match ProblemType::try_from(path) {
81 Ok(ProblemType::Qap) => Ok(Qap::load(&path)?),
82 Ok(ProblemType::Pfsp) => Ok(Pfsp::load(&path)?),
83 Ok(ProblemType::Lop) => Ok(Lop::load(&path)?),
84 Err(err) => panic!(err),
85 }
86 }
87
88 pub fn evaluate<T>(&self,
110 solutions: &PermuPopulation<T>,
111 fitness_vec: &mut Vec<usize>) -> Result<(), Error>
112 where T :
113 Copy +
114 From<u8> +
115 TryFrom<usize> +
116 TryInto<usize> +
117 Eq +
118 SampleRange +
119 PartialOrd +
120 Sub +
121 Display +
122 Debug
123 {
124 match self {
125 ProblemInstance::Qap(_,_,_) => Qap::evaluate(self, solutions, fitness_vec),
126 ProblemInstance::Pfsp(_, _,_) => Pfsp::evaluate(self, solutions, fitness_vec),
127 ProblemInstance::Lop(_,_) => Lop::evaluate(self, solutions, fitness_vec),
128 }
129 }
130}
131
132#[doc(hidden)]
134trait Problem {
135 fn load(path: &str) -> Result<ProblemInstance, Error>;
137
138 fn evaluate<T>(instace: &ProblemInstance,
140 solutions: &PermuPopulation<T>,
141 fitness_vec: &mut Vec<usize>) -> Result<(), Error>
142 where T :
143 Copy +
144 From<u8> +
145 TryFrom<usize> +
146 TryInto<usize> +
147 Eq +
148 SampleRange +
149 PartialOrd +
150 Sub +
151 Display +
152 Debug;
153
154 fn lines2matrix(buffer: &mut BufReader<File>,
156 n_lines: usize,
157 n_elems: usize) -> Result<Vec<Vec<usize>>, Error> {
158
159 let mut matrix = vec![Vec::with_capacity(n_elems); n_lines];
161
162 for i_line in 0..n_lines {
163 let mut line = String::new();
165 buffer.read_line(&mut line)?;
166 let line = line.split_whitespace();
167
168 let mut count = 0;
170 for str_num in line {
171 matrix[i_line].push(match str_num.trim().parse() {
172 Ok(n) => n,
173 Err(_) => return Err(Error::ParseError),
174 });
175 count += 1;
176 }
177
178 if count != n_elems {
180 return Err(Error::Io(
181 io::Error::new(io::ErrorKind::InvalidData,
182 "All rows must have the same length as the instance size")));
183 }
184 }
185 Ok(matrix)
186 }
187}
188
189#[doc(hidden)]
191struct Qap {}
192
193impl Problem for Qap {
194
195 fn load(path: &str) -> Result<ProblemInstance, Error> {
196 let file = File::open(path)?;
198 let mut reader = BufReader::new(file);
199
200 let mut size_str = String::new();
202 let _n = reader.read_line(&mut size_str); let size: usize = size_str.trim()
205 .parse()
206 .unwrap();
207
208 let distance = Self::lines2matrix(&mut reader, size, size)?;
209 let flow = Self::lines2matrix(&mut reader, size, size)?;
210
211 Ok(ProblemInstance::Qap(size, distance, flow))
212 }
213
214 fn evaluate<T>(instace: &ProblemInstance,
215 solutions: &PermuPopulation<T>,
216 fitness_vec: &mut Vec<usize>) -> Result<(), Error>
217 where T :
218 Copy +
219 From<u8> +
220 TryFrom<usize> +
221 TryInto<usize> +
222 Eq +
223 SampleRange +
224 PartialOrd +
225 Sub +
226 Display +
227 Debug {
228
229 let (size, distance, flow) = match instace {
231 ProblemInstance::Qap(size, dist, flow) => (size, dist, flow),
232 _ => return Err(Error::IncorrectProblemInstance),
233 };
234
235 if solutions.population[0].len() != *size {
237 return Err(Error::LengthError);
238 }
239
240 for (index, solution) in solutions.population.iter().enumerate() {
241 let mut fitness = 0;
242 for i in 0..*size {
243 for j in 0..*size {
244
245 let fact_a: usize = match solution.permu[i].try_into() {
246 Ok(n) => n,
247 Err(_) => return Err(Error::ParseError),
248 };
249 let fact_b: usize = match solution.permu[j].try_into() {
250 Ok(n) => n,
251 Err(_) => return Err(Error::ParseError),
252 };
253
254 let dist_ab = distance[i][j];
255 let flow_ab = flow[fact_a][fact_b];
256
257 fitness += dist_ab*flow_ab;
258 }
259 }
260 fitness_vec[index] = fitness;
261 }
262 Ok(())
263 }
264}
265
266#[doc(hidden)]
268struct Pfsp {}
269
270impl Problem for Pfsp {
271
272 fn load(path: &str) -> Result<ProblemInstance, Error> {
273 let file = File::open(path)?;
275 let mut reader = BufReader::new(file);
276
277 let mut size_str = String::new();
279 let _n = reader.read_line(&mut size_str); size_str.clear();
281 let _n = reader.read_line(&mut size_str); let mut splitted = size_str.split_whitespace();
285 let mut count = 0;
286 let mut sizes = vec![]; while count < 2 {
288 if let Some(item) = splitted.next() {
289 let num: usize = match item.trim().parse() {
290 Ok(n) => n,
291 Err(_) => continue,
292 };
293
294 sizes.push(num);
295 count += 1;
296
297 } else {
298 return Err(Error::Io(io::Error::new(
299 io::ErrorKind::InvalidInput,
300 "Cannot find size inside instance file")));
301 }
302 }
303 let _n = reader.read_line(&mut size_str);
305
306 let matrix = Self::lines2matrix(&mut reader, sizes[1], sizes[0])?;
308 Ok(ProblemInstance::Pfsp(sizes[0], sizes[1], matrix))
309 }
310
311 fn evaluate<T>(instace: &ProblemInstance,
312 solutions: &PermuPopulation<T>,
313 fitness_vec: &mut Vec<usize>) -> Result<(), Error>
314 where T :
315 Copy +
316 From<u8> +
317 TryFrom<usize> +
318 TryInto<usize> +
319 Eq +
320 SampleRange +
321 PartialOrd +
322 Sub +
323 Display +
324 Debug {
325
326 let (size, n_machines, matrix) = match instace {
328 ProblemInstance::Pfsp(n, m, mat) => (n, m, mat),
329 _ => return Err(Error::IncorrectProblemInstance),
330 };
331
332 if solutions.population[0].len() != *size {
334 return Err(Error::LengthError);
335 }
336
337 for (index, solution) in solutions.population.iter().enumerate() {
338 let mut tft = 0;
339 let mut b = vec![0;*n_machines];
340 for (job_i, job_n) in solution.permu.iter().enumerate() {
341 let mut pt = 0;
342 for machine in 0..*n_machines {
343
344 let job: usize = match T::try_into(*job_n) {
345 Ok(n) => n,
346 Err(_) => return Err(Error::ParseError),
347 };
348
349 if job_i == 0 && machine == 0 {
350 pt = matrix[machine][job];
351 }
352 else if job_i > 0 && machine == 0 {
353 pt = b[machine] + matrix[machine][job];
354 }
355 else if job_i == 0 && machine > 0 {
356 pt = b[machine-1] + matrix[machine][job];
357 }
358 else if job_i > 0 && machine > 0 {
359 pt = max(b[machine-1], b[machine]) + matrix[machine][job];
360 }
361
362 b[machine] = pt;
363 }
364 tft += pt;
365 }
366 fitness_vec[index] = tft;
367 }
368 Ok(())
369 }
370}
371
372#[doc(hidden)]
374struct Lop {}
375
376impl Problem for Lop {
377
378 fn load(path: &str) -> Result<ProblemInstance, Error> {
379 let file = File::open(path)?;
381 let mut reader = BufReader::new(file);
382
383 let mut size_str = String::new();
385 let _n = reader.read_line(&mut size_str); let size: usize = size_str.trim()
388 .parse()
389 .unwrap();
390
391 let matrix = Self::lines2matrix(&mut reader, size, size)?;
392
393 Ok(ProblemInstance::Lop(size, matrix))
394 }
395
396 fn evaluate<T>(instace: &ProblemInstance,
397 solutions: &PermuPopulation<T>,
398 fitness_vec: &mut Vec<usize>) -> Result<(), Error>
399 where T :
400 Copy +
401 From<u8> +
402 TryFrom<usize> +
403 TryInto<usize> +
404 Eq +
405 SampleRange +
406 PartialOrd +
407 Sub +
408 Display +
409 Debug
410 {
411 let (size, matrix) = match instace {
413 ProblemInstance::Lop(n, mat) => (n, mat),
414 _ => return Err(Error::IncorrectProblemInstance),
415 };
416
417 if solutions.population[0].len() != *size {
419 return Err(Error::LengthError);
420 }
421
422 for (index, solution) in solutions.population.iter().enumerate() {
423 let mut fitness = 0;
424 (0..*size-1).for_each(|i| {
425 (i+1..*size).for_each(|j| {
426
427 let elem1 = match solution.permu[i].try_into() {
428 Ok(a) => a,
429 Err(_) => unreachable!(),
430 };
431 let elem2 = match solution.permu[j].try_into() {
432 Ok(a) => a,
433 Err(_) => unreachable!(),
434 };
435
436 fitness += matrix[elem1][elem2];
437 });
438 });
439 fitness_vec[index] = fitness;
440 }
441 Ok(())
442 }
443}
444
445#[cfg(test)]
446mod test {
447
448 use crate::problems::*;
449 use std::convert::TryInto;
450 use crate::permutation::{PermuPopulation, Permutation};
451
452 #[test]
453 fn read_lop() {
454 let instance_path = "instances/LOP/N-be75eec_150.lop";
455 let ptype: ProblemType = instance_path.try_into().unwrap();
456
457 if let ProblemType::Lop = ptype {
458 } else {
459 panic!("The instace type is not LOP");
460 }
461
462 let pop = PermuPopulation::<u8>::random(10, 150);
463 let instance = Lop::load(instance_path).unwrap();
464 let mut fitness = vec![0;10];
465
466 instance.evaluate(&pop, &mut fitness).unwrap();
467
468 fitness.iter()
469 .for_each(|x| println!("permu fitness: {}", x));
470 }
471
472 #[test]
473 fn read_qap() {
474 let instance_path = "instances/QAP/tai20b.dat";
475 let ptype: ProblemType = instance_path.try_into().unwrap();
476
477 if let ProblemType::Qap = ptype {
478 } else {
479 panic!("The instace type is not LOP");
480 }
481
482 let instance = Qap::load(instance_path).unwrap();
483
484 let mut permu = Permutation::<u8>::from_vec_unsec(
485 vec![12,6,18,16,7,2,5,3,14,0,13,9,15,1,8,10,4,19,17,11]);
486 let pop = PermuPopulation::<u8>::from_vec(vec![permu]);
487
488 let mut fitness = vec![0];
489 instance.evaluate(&pop, &mut fitness).unwrap();
490 assert_eq!(125551590, fitness[0]);
491 }
492
493 #[test]
494 fn read_pfsp() {
495 let instance_path = "instances/PFSP/tai20_5_0.fsp";
496
497 let ptype: ProblemType = instance_path.try_into().unwrap();
498
499 if let ProblemType::Pfsp = ptype {
500 } else {
501 panic!("The instace type is not LOP");
502 }
503
504 let instance = Pfsp::load(instance_path).unwrap();
505
506 let mut permu = Permutation::<u8>::from_vec_unsec(
507 vec![11,12,0,13,14,9,10,5,2,19,18,17,7,4,3,8,1,15,6,16]);
508 permu.clone().invert(&mut permu).unwrap();
509 let pop = PermuPopulation::<u8>::from_vec(vec![permu]);
510
511 let mut fitness = vec![0];
512 instance.evaluate(&pop, &mut fitness).unwrap();
513 assert_eq!(14033, fitness[0]);
514 }
515
516 #[test]
517 fn test_load() {
518 use crate::permutation::PermuPopulation;
519
520 let paths = ["PFSP/tai100_20_0.fsp",
521 "QAP/tai100a.dat",
522 "/LOP/N-be75eec_150.lop"];
523 for name in paths.iter() {
524 let path = format!("instances/{}", name);
525
526 let instance = match ProblemType::try_from(path.as_str()) {
527 Ok(ProblemType::Qap) => Qap::load(&path).unwrap(),
528 Ok(ProblemType::Pfsp) => Pfsp::load(&path).unwrap(),
529 Ok(ProblemType::Lop) => Lop::load(&path).unwrap(),
530 Err(err) => panic!(err),
531 };
532
533 let pop = PermuPopulation::<u16>::random(100, instance.size());
534 let mut fitness = vec![0; 100];
535
536 instance.evaluate(&pop, &mut fitness).unwrap();
537 }
538 }
539}