-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathMIP.php
336 lines (296 loc) · 8.72 KB
/
MIP.php
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
<?php
namespace vendor\i4h\phpmip;
/**
* This class represents a mixed-integer program
*
* @author Ingmar Vierhaus <[email protected]>
* @since 2016/09/21
*
*/
function echon($msg) {
echo $msg."\n";
}
//@todo: Remove YII dependency
class MIP extends Object
{
/**
* @var array list of virtual properties
*/
public $magic = ['sets', 'variables', 'constraints', 'objectiveCoefficients'];
/**
* @var array sets of the MIP
*/
private $_sets = [];
/**
* @var array variables of the MIP
*/
private $_variables = [];
/**
* @var array constraints of the MIP
*/
private $_constraints = [];
/**
* @var array objective function coefficients
*/
private $_objectiveCoefficients = [];
/**
* @var constant value used for big m constraints
*/
const bigM=100000;
/**
* @var bool should bounds be shown in __toString() output
*/
public $boundsInToString = true;
/**
* Add a set to the mip
* @param $name name of the set
* @param $values array of values of the set
* @throws \Exception if set already exists
*/
public function addSet($name, Array $values) {
if (in_array($name,$this->_sets)) {
//@todo: more specialized exception for this
throw new \Exception("set ".$name." already exists in mip");
}
$this->_sets[$name] = $values;
}
/**
* Add a variable to the MIP
*
* @param $name Variable name
* @param $type Variable type (see MIPvariable for valid types)
* @param null $set If given, the new variable will have one
* value for each set element
* @throws \Exception If variable already exists or type is not recognized
*/
public function addVariable($name, $type, $set = null) {
if (in_array($name,$this->_variables))
throw new \Exception("variable ".$name." already exists in mip");
if ($set === null) {
$this->_variables[$name] = new MIPvariable($type);
} else {
if (!isset($this->_sets[$set]))
throw new \Exception("Set ".$set." not known to mip");
foreach ($this->_sets[$set] as $setIdx=>$setValue) {
$this->_variables[$name][$setValue] = new MIPvariable($type);
}
}
}
/**
*
* Add a variable to the MIP if a variable
* with given name does not yet exist
*
* @param $name Variable name
* @param $type Variable type (see MIPvariable for valid types)
* @param null $set If given, the new variable will have one
* value for each set element
*/
public function addVariableIfNew($name, $type, $set = null) {
if (in_array($name,$this->_variables)) {
return;
}
$this->addVariable($name, $type, $set);
}
/**
* Set the lower bound of a variable
*
* @param $name Variable name
* @param $bound value of the variable bound
* @param null $setIdx If the variable is defined on a set, the
* corresponding set index must be given
*/
public function setVariableLB($name, $bound, $setIdx = null) {
$this->checkVars([['name'=>$name, 'setIdx'=>$setIdx]]);
if ($setIdx === null)
$var = $this->_variables[$name];
else
$var = $this->_variables[$name][$setIdx];
$var->setLB($bound);
}
/**
* Set the upper bound of a variable
*
* @param $name Variable name
* @param $bound value of the variable bound
* @param null $setIdx If the variable is defined on a set, the
* corresponding set index must be given
*/
public function setVariableUB($name, $bound, $setIdx = null) {
$this->checkVars([['name'=>$name, 'setIdx'=>$setIdx]]);
if ($setIdx === null)
$var = $this->_variables[$name];
else
$var = $this->_variables[$name][$setIdx];
$var->setUB($bound);
}
/**
* adds constraint of type $lhs <= $vars <= $rhs
* @param $vars the variables of the constraint with their coefficients
* @param $lhs value
* @param $rhs value
*/
public function addConstraint(Array $vars, $lhs, $rhs) {
$this->checkVars($vars);
$this->_constraints[] = new MIPconstraint(MIPconstraint::TYPE_DEFAULT, $vars, $lhs, $rhs);
}
/**
* adds constraint of type $bound <= $vars <= inf
* @param $vars the variables of the constraint with their coefficients
* @param $bound number
*/
public function addConstraintGE(Array $vars, $bound) {
$this->checkVars($vars);
$this->_constraints[] = new MIPconstraint(MIPconstraint::TYPE_GE, $vars, $bound);
}
/**
* adds constraint of type -inf <= $vars <= rhs
* @param $vars the variables of the constraint with their coefficients
* @param $bound number
*/
public function addConstraintLE($vars, $bound) {
$this->checkVars($vars);
$this->_constraints[] = new MIPconstraint(MIPconstraint::TYPE_LE, $vars, $bound);
}
/**
* adds constraint of type $value <= $vars <= $value
* @param $vars the variables of the constraint with their coefficients
* @param $value number
*/
public function addConstraintEQ($vars, $value) {
$this->checkVars($vars);
$this->_constraints[] = new MIPconstraint(MIPconstraint::TYPE_EQ, $vars, $value);
}
/**
* Set the objective Coefficient for a variable
* @param $var variable name
* @param null $setValue set index
* @param int $coefficient objective function coefficient value
*/
public function addObjectiveCoefficient($var, $setValue = null, $coefficient = 1)
{
$this->checkVars([['name'=>$var, 'setIdx'=>$setValue]]);
if ($setValue === null)
$this->_objectiveCoefficients[$var] = $coefficient;
else
$this->_objectiveCoefficients[$var][$setValue] = $coefficient;
}
/**
* Returns a human readable string representation of this MIP
*
* @return string
*/
public function __toString() {
ob_start();
echon("BEGIN MIP:");
echon("Sets:");
foreach($this->_sets as $name=>$values) {
echon($name.": ".implode(", ",$values));
}
echon("");
echon("Variables:");
$vars = [];
foreach($this->_variables as $name=>$arr) {
if (!is_array($arr)) {
$var = $arr;
$vars[] = $var->getTypeString().": ".$name."".($this->boundsInToString ? ":".$var->boundsString : "");
} else {
$varList = [];
foreach($arr as $setValue=>$var)
$varList[] = $name."(".$setValue.")".($this->boundsInToString ? ":".$var->boundsString : "");
$vars[] = $var->getTypeString().": ".implode(", ",$varList);
}
}
echon(implode("\n",$vars));
echon("");
echon("Constraints:");
foreach($this->_constraints as $cons)
echon($cons);
echon("");
echon("Objective:");
$objectiveParts = [];
foreach($this->_objectiveCoefficients as $var=>$arr) {
if (is_array($arr)) {
foreach($arr as $setValue=>$coefficient)
$objectiveParts[] = $coefficient." ".$var."(".$setValue.")";
} else
$objectiveParts[] = $arr." ".$var;
}
echon("minimize ".implode(" + ",$objectiveParts));
echon("");
echon("END MIP");
$str = ob_get_contents();
ob_clean();
return $str;
}
/**
* Returns the sets of the MIP
*
* @return array
*/
public function getSets() {
return $this->_sets;
}
/**
* Returns the variables of the MIP
*
* @return array
*/
public function getVariables() {
return $this->_variables;
}
/**
* Returns the constraints of the MIP
*
* @return array
*/
public function getConstraints() {
return $this->_constraints;
}
/**
* Returns the non-zero objective coefficients of the MIP
*
* @return array
*/
public function getObjectiveCoefficients() {
return $this->_objectiveCoefficients;
}
/**
* Checks the existence of sets with the respective names
*
* @param $setArray array with set names to be checked
* @throws \Exception if a set does not exist in the MIP
* @return true
*/
public function checkSets($setArray) {
foreach ($setArray as $name){
if (!isset($this->_sets[$name])){
throw new \Exception("Set ".$name." is not set.");
}
}
return true;
}
/** Check that vars exist
*
* Checks that the variables described in the vars vector
* were previously added to the problem
*
* @param array $vars
* @throws \Exception
*/
public function checkVars(array $vars) {
foreach($vars as $var) {
/* Var array should exist */
if (!isset($this->_variables[$var['name']]))
throw new \Exception("Variable ".$var['name']." does not exist in MIP.");
/* Check if var is on set */
if (isset($var['setIdx'])) {
if (!is_array($this->_variables[$var['name']]) || !isset($this->_variables[$var['name']][$var['setIdx']]))
throw new \Exception("Variable ".$var['name']." does not exist in MIP.");
} else { /* Check if var is not on set */
if (is_array($this->_variables[$var['name']]))
throw new \Exception("Set index ".$var['setIdx']." is not defined in variable set ".$var['name']);
}
}
}
}