-
Notifications
You must be signed in to change notification settings - Fork 14
/
Copy pathmodel.rs
2099 lines (1860 loc) · 70.2 KB
/
model.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
use crate::builder::CanBeAddedToModel;
use crate::constraint::Constraint;
use crate::eventhdlr::Eventhdlr;
use crate::node::Node;
use crate::param::ScipParameter;
use crate::retcode::Retcode;
use crate::scip::ScipPtr;
use crate::solution::{SolError, Solution};
use crate::status::Status;
use crate::variable::{VarId, VarType, Variable};
use crate::Conshdlr;
use crate::{ffi, Row, Separator};
use crate::{BranchRule, HeurTiming, Heuristic, Pricer};
use scip_sys::SCIP;
use std::rc::Rc;
/// Represents an optimization model.
#[non_exhaustive]
#[derive(Debug)]
pub struct Model<State> {
pub(crate) scip: Rc<ScipPtr>,
#[allow(dead_code)]
pub(crate) state: State,
}
/// Represents the state of an optimization model that has not yet been solved.
#[derive(Debug)]
pub struct Unsolved;
/// Represents the state of an optimization model where all plugins have been included.
#[derive(Debug)]
pub struct PluginsIncluded;
/// Represents the state of an optimization model where the problem has been created.
#[derive(Debug, Clone)]
pub struct ProblemCreated;
/// Represents the state of an optimization model during the solving process (to be used in plugins).
#[derive(Debug)]
pub struct Solving;
/// Represents the state of an optimization model that has been solved.
#[derive(Debug)]
pub struct Solved;
impl Model<Unsolved> {
/// Creates a new `Model` instance with an `Unsolved` state.
pub fn new() -> Self {
Self::try_new().expect("Failed to create SCIP instance")
}
/// Tries to create a new `Model` instance with an `Unsolved` state.
///
/// Returns a `Result` with the new `Model` instance on success, or a `Retcode` error on failure.
pub fn try_new() -> Result<Self, Retcode> {
let scip_ptr = ScipPtr::new();
Ok(Model {
scip: Rc::new(scip_ptr),
state: Unsolved {},
})
}
}
impl Model<PluginsIncluded> {
/// Creates a new problem in the SCIP instance with the given name and returns a new `Model` instance with a `ProblemCreated` state.
///
/// # Arguments
///
/// * `name` - The name of the problem to create.
///
/// # Panics
///
/// This method panics if the problem cannot be created in the current state.
#[allow(unused_mut)]
pub fn create_prob(mut self, name: &str) -> Model<ProblemCreated> {
let mut scip = self.scip.clone();
scip.create_prob(name)
.expect("Failed to create problem in state PluginsIncluded");
Model {
scip,
state: ProblemCreated {},
}
}
/// Reads a problem from the given file and returns a new `Model` instance with a `ProblemCreated` state.
///
/// # Arguments
///
/// * `filename` - The name of the file to read the problem from.
///
/// # Errors
///
/// This method returns a `Retcode` error if the problem cannot be read from the file.
#[allow(unused_mut)]
pub fn read_prob(mut self, filename: &str) -> Result<Model<ProblemCreated>, Retcode> {
let scip = self.scip.clone();
scip.read_prob(filename)?;
let new_model = Model {
scip: self.scip,
state: ProblemCreated {},
};
Ok(new_model)
}
}
impl Model<ProblemCreated> {
/// Sets the objective sense of the model to the given value and returns the same `Model` instance.
///
/// # Arguments
///
/// * `sense` - The objective sense to set.
///
/// # Panics
///
/// This method panics if the objective sense cannot be set in the current state.
pub fn set_obj_sense(mut self, sense: ObjSense) -> Self {
let scip = self.scip.clone();
scip.set_obj_sense(sense)
.expect("Failed to set objective sense in state ProblemCreated");
self.scip = scip;
self
}
/// Sets the objective sense of the model to maximize
#[allow(unused_mut)]
pub fn maximize(mut self) -> Self {
self.set_obj_sense(ObjSense::Maximize)
}
/// Sets the objective sense of the model to minimize
#[allow(unused_mut)]
pub fn minimize(mut self) -> Self {
self.set_obj_sense(ObjSense::Minimize)
}
/// Sets the constraint as modifiable or not.
pub fn set_cons_modifiable(&mut self, cons: &Constraint, modifiable: bool) {
self.scip
.set_cons_modifiable(cons, modifiable)
.expect("Failed to set constraint modifiable");
}
/// Informs the SCIP instance that the objective value is always integral and returns the same `Model` instance.
#[allow(unused_mut)]
pub fn set_obj_integral(mut self) -> Self {
self.scip
.set_obj_integral()
.expect("Failed to set the objective value as integral");
self
}
/// Adds a new variable to the model with the given lower bound, upper bound, objective coefficient, name, and type.
///
/// # Arguments
///
/// * `lb` - The lower bound of the variable.
/// * `ub` - The upper bound of the variable.
/// * `obj` - The objective coefficient of the variable.
/// * `name` - The name of the variable.
/// * `var_type` - The type of the variable.
///
/// # Returns
///
/// A reference-counted pointer to the new variable.
///
/// # Panics
///
/// This method panics if the variable cannot be created in the current state.
pub fn add_var(
&mut self,
lb: f64,
ub: f64,
obj: f64,
name: &str,
var_type: VarType,
) -> Variable {
let var = self
.scip
.create_var(lb, ub, obj, name, var_type)
.expect("Failed to create variable in state ProblemCreated");
Variable {
raw: var,
scip: self.scip.clone(),
}
}
/// Includes a new branch rule in the model with the given name, description, priority, maximum depth, maximum bound distance, and implementation.
///
/// # Arguments
///
/// * `name` - The name of the branching rule. This should be a unique identifier.
/// * `desc` - A brief description of the branching rule. This is used for informational purposes.
/// * `priority` - The priority of the branching rule. When SCIP decides which branching rule to call, it considers their priorities. A higher value indicates a higher priority.
/// * `maxdepth` - The maximum depth level up to which this branching rule should be used. If this is -1, the branching rule can be used at any depth.
/// * `maxbounddist` - The maximum relative distance from the current node's dual bound to primal bound compared to the best node's dual bound for applying the branching rule. A value of 0.0 means the rule can only be applied on the current best node, while 1.0 means it can be applied on all nodes.
/// * `rule` - The branching rule to be included. This should be a mutable reference to an object that implements the `BranchRule` trait, and represents the branching rule data.
///
/// # Panics
///
/// This method will panic if the inclusion of the branching rule fails. This can happen if another branching rule with the same name already exists.
pub fn include_branch_rule(
&mut self,
name: &str,
desc: &str,
priority: i32,
maxdepth: i32,
maxbounddist: f64,
rule: Box<dyn BranchRule>,
) {
self.scip
.include_branch_rule(name, desc, priority, maxdepth, maxbounddist, rule)
.expect("Failed to include branch rule at state ProblemCreated");
}
/// Include a new primal heuristic in the model.
///
/// # Arguments
///
/// * `name` - The name of the heuristic. This should be a unique identifier.
/// * `desc` - A brief description of the heuristic. This is used for informational purposes.
/// * `priority` - The priority of the heuristic. When SCIP decides which heuristic to call, it considers their priorities. A higher value indicates a higher priority.
/// * `dispchar` - The display character of the heuristic (used in logs).
/// * `freq` - The frequency for calling the heuristic in the tree; 1 means at every node, 2 means at every other node and so on, -1 turns off the heuristic.
/// * `freqofs` - The frequency offset for calling the heuristic in the tree; it defines the depth of the branching tree at which the primal heuristic is executed for the first time.
/// * `maxdepth` - The maximum depth level up to which this heuristic should be used. If this is -1, the heuristic can be used at any depth.
/// * `timing` - The timing mask of the heuristic.
/// * `usessubscip` - Should the heuristic use a secondary SCIP instance?
/// * `heur` - The heuristic to be included. This should be a Box of an object that implements the `Heur` trait, and represents the heuristic data.
pub fn include_heur(
&mut self,
name: &str,
desc: &str,
priority: i32,
dispchar: char,
freq: i32,
freqofs: i32,
maxdepth: i32,
timing: HeurTiming,
usessubscip: bool,
heur: Box<dyn Heuristic>,
) {
self.scip
.include_heur(
name,
desc,
priority,
dispchar,
freq,
freqofs,
maxdepth,
timing,
usessubscip,
heur,
)
.expect("Failed to include heuristic at state ProblemCreated");
}
/// Includes a new separator in the model.
///
/// # Arguments
///
/// * `name` - The name of the separator. This should be a unique identifier.
/// * `desc` - A brief description of the separator. This is used for informational purposes.
/// * `priority` - The priority of the separator. When SCIP decides which separator to call, it considers their priorities. A higher value indicates a higher priority.
/// * `freq` - The frequency for calling the separator in the tree; 1 means at every node, 2 means at every other node and so on, -1 turns off the separator.
/// * `maxbounddist` - The maximum relative distance from the current node's dual bound to primal bound compared to the best node's dual bound for applying the separator. A value of 0.0 means the separator can only be applied on the current best node, while 1.0 means it can be applied on all nodes.
/// * `usesubscip` - Does the separator use a secondary SCIP instance?
/// * `delay` - A boolean indicating whether the separator should be delayed.
/// * `separator`- The separator to be included. This should be a mutable reference to an object that implements the `Separator` trait, and represents the separator data.
pub fn include_separator(
&mut self,
name: &str,
desc: &str,
priority: i32,
freq: i32,
maxbounddist: f64,
usesubscip: bool,
delay: bool,
separator: Box<dyn Separator>,
) {
self.scip
.include_separator(
name,
desc,
priority,
freq,
maxbounddist,
usesubscip,
delay,
separator,
)
.expect("Failed to include separator at state ProblemCreated");
}
/// Includes a new event handler in the model.
///
/// # Arguments
///
/// * `name` - The name of the event handler. This should be a unique identifier.
/// * `desc` - A brief description of the event handler. This is used for informational purposes.
/// * `eventhdlr` - The event handler to be included. This should be a mutable reference to an object that implements the `EventHdlr` trait, and represents the event handling logic.
pub fn include_eventhdlr(&mut self, name: &str, desc: &str, eventhdlr: Box<dyn Eventhdlr>) {
self.scip
.include_eventhdlr(name, desc, eventhdlr)
.expect("Failed to include event handler at state ProblemCreated");
}
/// Includes a new pricer in the SCIP data structure.
///
/// # Arguments
///
/// * `name` - The name of the variable pricer. This should be a unique identifier.
/// * `desc` - A brief description of the variable pricer.
/// * `priority` - The priority of the variable pricer. When SCIP decides which pricer to call, it considers their priorities. A higher value indicates a higher priority.
/// * `delay` - A boolean indicating whether the pricer should be delayed. If true, the pricer is only called when no other pricers or already existing problem variables with negative reduced costs are found. If this is set to false, the pricer may produce columns that already exist in the problem.
/// * `pricer` - The pricer to be included. This should be a mutable reference to an object that implements the `Pricer` trait.
///
/// # Panics
///
/// This method will panic if the inclusion of the pricer fails. This can happen if another pricer with the same name already exists.
pub fn include_pricer(
&mut self,
name: &str,
desc: &str,
priority: i32,
delay: bool,
pricer: Box<dyn Pricer>,
) {
self.scip
.include_pricer(name, desc, priority, delay, pricer)
.expect("Failed to include pricer at state ProblemCreated");
}
/// Includes a custom constraint handler in the SCIP data structure.
///
/// # Arguments
/// * `name` - The name of the constraint handler. This should be a unique identifier.
/// * `desc` - A brief description of the constraint handler.
/// * `enfopriority` - Like the separation priority, the enforcement priorities define the order
/// in which the different constraint handlers are called in the constraint enforcement step
/// of the sub-problem processing. The constraint enforcement is called after the price-and-cut
/// loop is executed (in the case that the LP is solved at the current subproblem).
/// The integrality constraint handler has an enforcement priority of 0. That means, if a
/// constraint handler has negative enforcement priority, it only has to deal with integral
/// solutions in its enforcement methods, because for fractional solutions, the integrality
/// constraint handler would have created a branching, thereby aborting the enforcement step.
/// If you want to implement a constraint-depending branching rule (for example, SOS branching
/// on special ordered set constraints), you have to assign a positive enforcement priority to
/// your constraint handler. In this case, you have to be able to deal with fractional solutions.
/// * `checkpriority` - The checking priorities define the order in which the different constraint
/// handlers are called to check the feasibility of a given primal solution candidate.
/// The integrality constraint handler has a checking priority of 0. That means, constraint
/// handlers with negative checking priorities only have to deal with integral solutions.
/// * `conshdlr` - The constraint handler to be included.
pub fn include_conshdlr(
&mut self,
name: &str,
desc: &str,
enfopriority: i32,
checkpriority: i32,
conshdlr: Box<dyn Conshdlr>,
) {
self.scip
.include_conshdlr(name, desc, enfopriority, checkpriority, conshdlr)
.expect("Failed to include constraint handler at state ProblemCreated");
}
/// Solves the model and returns a new `Model` instance with a `Solved` state.
///
/// # Returns
///
/// A new `Model` instance with a `Solved` state.
///
/// # Panics
///
/// This method panics if the problem cannot be solved in the current state.
#[allow(unused_mut)]
pub fn solve(mut self) -> Model<Solved> {
self.scip
.solve()
.expect("Failed to solve problem in state ProblemCreated");
Model {
scip: self.scip,
state: Solved {},
}
}
}
impl Model<Solving> {
/// Adds a new variable to the model with the given lower bound, upper bound, objective coefficient, name, and type.
///
/// # Arguments
///
/// * `lb` - The lower bound of the variable.
/// * `ub` - The upper bound of the variable.
/// * `obj` - The objective coefficient of the variable.
/// * `name` - The name of the variable.
/// * `var_type` - The type of the variable.
///
/// # Returns
///
/// A reference-counted pointer to the new variable.
///
/// # Panics
///
/// This method panics if the variable cannot be created in the current state.
pub fn add_var(
&mut self,
lb: f64,
ub: f64,
obj: f64,
name: &str,
var_type: VarType,
) -> Variable {
let var = self
.scip
.create_var_solving(lb, ub, obj, name, var_type)
.expect("Failed to create variable in state ProblemCreated");
Variable {
raw: var,
scip: self.scip.clone(),
}
}
/// Returns the current node of the model.
///
/// # Panics
///
/// This method panics if not called in the `Solving` state, it should only be used from plugins implementations.
pub fn focus_node(&self) -> Node {
let scip_node = self.scip.focus_node().expect("Failed to get focus node");
Node {
raw: scip_node,
scip: self.scip.clone(),
}
}
/// Creates a new child node of the current node and returns it.
///
/// # Panics
///
/// This method panics if not called from plugins implementations.
pub fn create_child(&mut self) -> Node {
let node_ptr = self
.scip
.create_child()
.expect("Failed to create child node in state ProblemCreated");
Node {
raw: node_ptr,
scip: self.scip.clone(),
}
}
/// Adds a new priced variable to the SCIP data structure.
///
/// # Arguments
///
/// * `lb` - The lower bound of the variable.
/// * `ub` - The upper bound of the variable.
/// * `obj` - The objective function coefficient for the variable.
/// * `name` - The name of the variable. This should be a unique identifier.
/// * `var_type` - The type of the variable, specified as an instance of the `VarType` enum.
///
/// # Returns
///
/// This function returns a reference-counted smart pointer (`Rc`) to the created `Variable` instance.
pub fn add_priced_var(
&mut self,
lb: f64,
ub: f64,
obj: f64,
name: &str,
var_type: VarType,
) -> Variable {
let var = self
.scip
.create_priced_var(lb, ub, obj, name, var_type)
.expect("Failed to create variable in state ProblemCreated");
Variable {
raw: var,
scip: self.scip.clone(),
}
}
/// Gets the variable in current problem given its index (in the problem).
///
/// # Arguments
/// * `var_prob_id` - The index of the variable in the problem.
///
/// # Returns
/// A reference-counted pointer to the variable.
pub fn var_in_prob(&self, var_prob_id: usize) -> Option<Variable> {
unsafe {
ScipPtr::var_from_id(self.scip.raw, var_prob_id).map(|v| Variable {
raw: v,
scip: self.scip.clone(),
})
}
}
/// Adds a new cut (row) to the model.
///
/// # Arguments
/// * `row` - The row to add.
/// * `force_cut` - If true, the cut (row) is forced to be selected.
///
/// # Returns
/// A boolean indicating whether the row is infeasible from the local bounds.
pub fn add_cut(&mut self, cut: Row, force_cut: bool) -> bool {
self.scip
.add_row(cut, force_cut)
.expect("Failed to add row in state ProblemCreated")
}
/// Returns the value of a variable in the current LP/pseudo solution.
///
/// #Arguments
/// * `var` - Variable to obtain value for.
///
/// #Returns
/// Value of the variable.
pub fn current_val(&self, var: &Variable) -> f64 {
unsafe { ffi::SCIPgetSolVal(self.scip_ptr(), std::ptr::null_mut(), var.inner()) }
}
}
impl Model<Solved> {
/// Returns the objective value of the best solution found by the optimization model.
pub fn obj_val(&self) -> f64 {
self.scip.obj_val()
}
/// Returns the number of nodes explored by the optimization model.
pub fn n_nodes(&self) -> usize {
self.scip.n_nodes()
}
/// Returns the total solving time of the optimization model.
pub fn solving_time(&self) -> f64 {
self.scip.solving_time()
}
/// Returns the number of LP iterations performed by the optimization model.
pub fn n_lp_iterations(&self) -> usize {
self.scip.n_lp_iterations()
}
/// Frees the transformed problem and returns the model the ProblemCreated state where you
/// can add variables and constraints, useful for iterated solving
pub fn free_transform(self) -> Model<ProblemCreated> {
self.scip
.free_transform()
.unwrap_or_else(|retcode| panic!("SCIP returned unexpected retcode {:?}", retcode));
Model {
scip: self.scip,
state: ProblemCreated {},
}
}
}
/// A trait for optimization models with a problem created.
pub trait ModelWithProblem {
/// Returns a vector of all variables in the optimization model.
fn vars(&self) -> Vec<Variable>;
/// Returns a vector of all original variables in the optimization model.
fn orig_vars(&self) -> Vec<Variable>;
/// Returns the variable with the given ID, if it exists.
fn var(&self, var_id: VarId) -> Option<Variable>;
/// Returns the number of variables in the optimization model.
fn n_vars(&self) -> usize;
/// Returns the number of constraints in the optimization model.
fn n_conss(&self) -> usize;
/// Finds a constraint by name
fn find_cons(&self, name: &str) -> Option<Constraint>;
/// Returns a vector of all constraints in the optimization model.
fn conss(&self) -> Vec<Constraint>;
/// Writes the optimization model to a file with the given path and extension.
fn write(&self, path: &str, ext: &str) -> Result<(), Retcode>;
}
/// A trait for model stages that have a problem.
pub trait ModelStageWithProblem {}
impl ModelStageWithProblem for ProblemCreated {}
impl ModelStageWithProblem for Solved {}
impl ModelStageWithProblem for Solving {}
impl<S: ModelStageWithProblem> ModelWithProblem for Model<S> {
/// Returns a vector of all variables in the optimization model.
fn vars(&self) -> Vec<Variable> {
let scip_vars = self.scip.vars(false, false);
scip_vars
.into_values()
.map(|v| Variable {
raw: v,
scip: self.scip.clone(),
})
.collect()
}
/// Returns a vector of all original variables in the optimization model.
fn orig_vars(&self) -> Vec<Variable> {
let scip_vars = self.scip.vars(true, false);
scip_vars
.into_values()
.map(|v| Variable {
raw: v,
scip: self.scip.clone(),
})
.collect()
}
/// Returns the variable with the given ID, if it exists.
fn var(&self, var_id: VarId) -> Option<Variable> {
let vars = self.scip.vars(false, false);
for (i, v) in vars {
if i == var_id {
return Some(Variable {
raw: v,
scip: self.scip.clone(),
});
}
}
None
}
/// Returns the number of variables in the optimization model.
fn n_vars(&self) -> usize {
self.scip.n_vars()
}
/// Returns the number of constraints in the optimization model.
fn n_conss(&self) -> usize {
self.scip.n_conss()
}
fn find_cons(&self, name: &str) -> Option<Constraint> {
self.scip.find_cons(name).map(|cons| Constraint {
raw: cons,
scip: self.scip.clone(),
})
}
/// Returns a vector of all constraints in the optimization model.
fn conss(&self) -> Vec<Constraint> {
let scip_conss = self.scip.conss(false);
scip_conss
.into_iter()
.map(|c| Constraint {
raw: c,
scip: self.scip.clone(),
})
.collect()
}
/// Writes the optimization model to a file with the given path and extension.
fn write(&self, path: &str, ext: &str) -> Result<(), Retcode> {
self.scip.write(path, ext)?;
Ok(())
}
}
/// A trait for optimization models with a problem created or solved.
pub trait ProblemOrSolving {
/// Creates a new solution initialized to zero.
fn create_sol(&self) -> Solution;
/// Create a solution in the original space
fn create_orig_sol(&self) -> Solution;
/// Adds a solution to the model
///
/// # Returns
/// A `Result` indicating whether the solution was added successfully.
fn add_sol(&self, sol: Solution) -> Result<(), SolError>;
/// Adds a binary variable to the given set partitioning constraint.
///
/// # Arguments
///
/// * `cons` - The constraint to add the variable to.
/// * `var` - The binary variable to add.
///
/// # Panics
///
/// This method panics if the variable cannot be added in the current state, or if the variable is not binary.
fn add_cons_coef_setppc(&mut self, cons: &Constraint, var: &Variable);
/// Adds a coefficient to the given constraint for the given variable and coefficient value.
///
/// # Arguments
///
/// * `cons` - The constraint to add the coefficient to.
/// * `var` - The variable to add the coefficient for.
/// * `coef` - The coefficient value to add.
///
/// # Panics
///
/// This method panics if the coefficient cannot be added in the current state.
fn add_cons_coef(&mut self, cons: &Constraint, var: &Variable, coef: f64);
/// Adds a new quadratic constraint to the model with the given variables, coefficients, left-hand side, right-hand side, and name.
///
/// # Arguments
///
/// * `lin_vars` - The linear variables in the constraint.
/// * `lin_coefs` - The coefficients of the linear variables in the constraint.
/// * `quad_vars_1` - The first variable in the quadratic constraints.
/// * `quad_vars_2` - The second variable in the quadratic constraints.
/// * `quad_coefs` - The coefficients of the quadratic terms in the constraint.
/// * `lhs` - The left-hand side of the constraint.
/// * `rhs` - The right-hand side of the constraint.
/// * `name` - The name of the constraint.
///
/// # Returns
///
/// A reference-counted pointer to the new constraint.
///
/// # Panics
///
/// This method panics if the constraint cannot be created in the current state.
fn add_cons_quadratic(
&mut self,
lin_vars: Vec<&Variable>,
lin_coefs: &mut [f64],
quad_vars_1: Vec<&Variable>,
quad_vars_2: Vec<&Variable>,
quad_coefs: &mut [f64],
lhs: f64,
rhs: f64,
name: &str,
) -> Constraint;
/// Adds a new constraint to the model with the given variables, coefficients, left-hand side, right-hand side, and name.
///
/// # Arguments
///
/// * `vars` - The variables in the constraint.
/// * `coefs` - The coefficients of the variables in the constraint.
/// * `lhs` - The left-hand side of the constraint.
/// * `rhs` - The right-hand side of the constraint.
/// * `name` - The name of the constraint.
///
/// # Returns
///
/// A reference-counted pointer to the new constraint.
///
/// # Panics
///
/// This method panics if the constraint cannot be created in the current state.
fn add_cons(
&mut self,
vars: Vec<&Variable>,
coefs: &[f64],
lhs: f64,
rhs: f64,
name: &str,
) -> Constraint;
/// Adds a new set partitioning constraint to the model with the given variables and name.
///
/// # Arguments
///
/// * `vars` - The binary variables in the constraint.
/// * `name` - The name of the constraint.
///
/// # Returns
///
/// A reference-counted pointer to the new constraint.
///
/// # Panics
///
/// This method panics if the constraint cannot be created in the current state, or if any of the variables are not binary.
fn add_cons_set_part(&mut self, vars: Vec<&Variable>, name: &str) -> Constraint;
/// Adds a new set cover constraint to the model with the given variables and name.
///
/// # Arguments
///
/// * `vars` - The binary variables in the constraint.
/// * `name` - The name of the constraint.
///
/// # Returns
///
/// A reference-counted pointer to the new constraint.
///
/// # Panics
///
/// This method panics if the constraint cannot be created in the current state, or if any of the variables are not binary.
fn add_cons_set_cover(&mut self, vars: Vec<&Variable>, name: &str) -> Constraint;
/// Adds a new set packing constraint to the model with the given variables and name.
///
/// # Arguments
///
/// * `vars` - The binary variables in the constraint.
/// * `name` - The name of the constraint.
///
/// # Returns
///
/// A reference-counted pointer to the new constraint.
///
/// # Panics
///
/// This method panics if the constraint cannot be created in the current state, or if any of the variables are not binary.
fn add_cons_set_pack(&mut self, vars: Vec<&Variable>, name: &str) -> Constraint;
/// Adds a new cardinality constraint to the model with the given variables, cardinality limit, and name.
///
/// # Arguments
///
/// * `vars` - The binary variables in the constraint.
/// * `cardinality` - The maximum number of non-zero variables this constraint allows
/// * `name` - The name of the constraint.
///
/// # Returns
///
/// A reference-counted pointer to the new constraint.
///
/// # Panics
///
/// This method panics if the constraint cannot be created in the current state.
fn add_cons_cardinality(
&mut self,
vars: Vec<&Variable>,
cardinality: usize,
name: &str,
) -> Constraint;
/// Adds a new indicator constraint to the model with the given variables, coefficients, right-hand side, and name.
///
/// # Arguments
///
/// * `bin_var` - The binary variable in the constraint.
/// * `vars` - The variables of the constraints.
/// * `coefs` - The coefficients of the variables in the constraint.
/// * `rhs` - The right-hand side of the constraint.
/// * `name` - The name of the constraint.
///
/// # Returns
///
/// A reference-counted pointer to the new constraint.
///
/// # Panics
///
/// This method panics if the constraint cannot be created in the current state.
fn add_cons_indicator(
&mut self,
bin_var: &Variable,
vars: Vec<&Variable>,
coefs: &mut [f64],
rhs: f64,
name: &str,
) -> Constraint;
}
/// A trait for model stages that have a problem or are during solving.
pub trait ModelStageProblemOrSolving {}
impl ModelStageProblemOrSolving for ProblemCreated {}
impl ModelStageProblemOrSolving for Solving {}
impl<S: ModelStageProblemOrSolving> ProblemOrSolving for Model<S> {
/// Creates a new solution initialized to zero.
fn create_sol(&self) -> Solution {
let sol_ptr = self
.scip
.create_sol(false)
.expect("Failed to create solution in state ProblemCreated");
Solution {
scip_ptr: self.scip.clone(),
raw: sol_ptr,
}
}
/// Create a new solution in the original space
fn create_orig_sol(&self) -> Solution {
let sol_ptr = self
.scip
.create_sol(true)
.expect("Failed to create solution in state ProblemCreated");
Solution {
scip_ptr: self.scip.clone(),
raw: sol_ptr,
}
}
/// Adds a solution to the model
///
/// # Returns
/// A `Result` indicating whether the solution was added successfully.
fn add_sol(&self, sol: Solution) -> Result<(), SolError> {
let succesfully_stored = self.scip.add_sol(sol).expect("Failed to add solution");
if succesfully_stored {
Ok(())
} else {
Err(SolError::Infeasible)
}
}
/// Adds a binary variable to the given set partitioning constraint.
///
/// # Arguments
///
/// * `cons` - The constraint to add the variable to.
/// * `var` - The binary variable to add.
///
/// # Panics
///
/// This method panics if the variable cannot be added in the current state, or if the variable is not binary.
fn add_cons_coef_setppc(&mut self, cons: &Constraint, var: &Variable) {
assert_eq!(var.var_type(), VarType::Binary);
self.scip
.add_cons_coef_setppc(cons, var)
.expect("Failed to add constraint coefficient in state ProblemCreated");
}
/// Adds a coefficient to the given constraint for the given variable and coefficient value.
///
/// # Arguments
///
/// * `cons` - The constraint to add the coefficient to.
/// * `var` - The variable to add the coefficient for.
/// * `coef` - The coefficient value to add.
///
/// # Panics
///
/// This method panics if the coefficient cannot be added in the current state.
fn add_cons_coef(&mut self, cons: &Constraint, var: &Variable, coef: f64) {
self.scip
.add_cons_coef(cons, var, coef)
.expect("Failed to add constraint coefficient in state ProblemCreated");
}
/// Adds a new quadratic constraint to the model with the given variables, coefficients, left-hand side, right-hand side, and name.
///
/// # Arguments
///
/// * `lin_vars` - The linear variables in the constraint.
/// * `lin_coefs` - The coefficients of the linear variables in the constraint.
/// * `quad_vars_1` - The first variable in the quadratic constraints.
/// * `quad_vars_2` - The second variable in the quadratic constraints.
/// * `quad_coefs` - The coefficients of the quadratic terms in the constraint.
/// * `lhs` - The left-hand side of the constraint.
/// * `rhs` - The right-hand side of the constraint.
/// * `name` - The name of the constraint.
///
/// # Returns
///
/// A reference-counted pointer to the new constraint.
///
/// # Panics
///
/// This method panics if the constraint cannot be created in the current state.
fn add_cons_quadratic(
&mut self,
lin_vars: Vec<&Variable>,
lin_coefs: &mut [f64],
quad_vars_1: Vec<&Variable>,
quad_vars_2: Vec<&Variable>,
quad_coefs: &mut [f64],
lhs: f64,
rhs: f64,
name: &str,
) -> Constraint {
assert_eq!(lin_vars.len(), lin_coefs.len());
assert_eq!(quad_vars_1.len(), quad_vars_2.len());
assert_eq!(quad_vars_1.len(), quad_coefs.len());
let cons = self
.scip
.create_cons_quadratic(
lin_vars,
lin_coefs,
quad_vars_1,
quad_vars_2,
quad_coefs,
lhs,
rhs,
name,
)
.expect("Failed to create constraint in state ProblemCreated");
Constraint {
raw: cons,
scip: self.scip.clone(),
}
}
/// Adds a new constraint to the model with the given variables, coefficients, left-hand side, right-hand side, and name.
///
/// # Arguments