linny-r 1.3.2 → 1.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/console.js +23 -7
- package/package.json +1 -1
- package/server.js +23 -3
- package/static/scripts/linny-r-milp.js +99 -6
- package/static/scripts/linny-r-vm.js +33 -85
package/console.js
CHANGED
@@ -87,7 +87,7 @@ console.log('Module directory:', MODULE_DIRECTORY);
|
|
87
87
|
console.log('Working directory:', WORKING_DIRECTORY);
|
88
88
|
|
89
89
|
// Currently, these external solvers are supported:
|
90
|
-
const SUPPORTED_SOLVERS = ['gurobi', 'scip', 'lp_solve'];
|
90
|
+
const SUPPORTED_SOLVERS = ['gurobi', 'cplex', 'scip', 'lp_solve'];
|
91
91
|
|
92
92
|
const
|
93
93
|
// Load the MILP solver (dependent on Node.js: `fs`, `os` and `path`)
|
@@ -126,7 +126,7 @@ Possible options are:
|
|
126
126
|
[name]-stats.txt in (workspace)/reports
|
127
127
|
run will run the loaded model
|
128
128
|
solver=[name] will select solver [name], or warn if not found
|
129
|
-
(name choices: Gurobi, SCIP or LP_solve)
|
129
|
+
(name choices: Gurobi, CPLEX, SCIP or LP_solve)
|
130
130
|
user=[identifier] user ID will be used to log onto remote servers
|
131
131
|
verbose will output solver messages to the console
|
132
132
|
workspace=[path] will create workspace in [path] instead of (main)/user
|
@@ -964,8 +964,25 @@ function commandLineSettings() {
|
|
964
964
|
'WARNING: Failed to access the Gurobi command line application');
|
965
965
|
}
|
966
966
|
}
|
967
|
+
// Check if cplex(.exe) exists in its directory
|
968
|
+
let sp = path.join(cplex_path, 'cplex' + (PLATFORM.startsWith('win') ? '.exe' : ''));
|
969
|
+
const need_cplex = !settings.solver || settings.preferred_solver === 'cplex';
|
970
|
+
try {
|
971
|
+
fs.accessSync(sp, fs.constants.X_OK);
|
972
|
+
console.log('Path to CPLEX:', sp);
|
973
|
+
if(need_cplex) {
|
974
|
+
settings.solver = 'cplex';
|
975
|
+
settings.solver_path = sp;
|
976
|
+
}
|
977
|
+
} catch(err) {
|
978
|
+
// Only report error if CPLEX is needed
|
979
|
+
if(need_cplex) {
|
980
|
+
console.log(err.message);
|
981
|
+
console.log('WARNING: CPLEX application not found in', sp);
|
982
|
+
}
|
983
|
+
}
|
967
984
|
// Check if scip(.exe) exists in its directory
|
968
|
-
|
985
|
+
sp = path.join(scip_path, 'scip' + (PLATFORM.startsWith('win') ? '.exe' : ''));
|
969
986
|
const need_scip = !settings.solver || settings.preferred_solver === 'scip';
|
970
987
|
try {
|
971
988
|
fs.accessSync(sp, fs.constants.X_OK);
|
@@ -982,10 +999,9 @@ function commandLineSettings() {
|
|
982
999
|
}
|
983
1000
|
}
|
984
1001
|
// Check if lp_solve(.exe) exists in main directory
|
985
|
-
|
986
|
-
|
987
|
-
|
988
|
-
need_lps = !settings.solver || settings.preferred_solver === 'lp_solve';
|
1002
|
+
sp = path.join(WORKING_DIRECTORY,
|
1003
|
+
'lp_solve' + (PLATFORM.startsWith('win') ? '.exe' : ''));
|
1004
|
+
const need_lps = !settings.solver || settings.preferred_solver === 'lp_solve';
|
989
1005
|
try {
|
990
1006
|
fs.accessSync(sp, fs.constants.X_OK);
|
991
1007
|
console.log('Path to LP_solve:', sp);
|
package/package.json
CHANGED
package/server.js
CHANGED
@@ -123,7 +123,7 @@ function checkNodeModule(name) {
|
|
123
123
|
}
|
124
124
|
|
125
125
|
// Currently, these external solvers are supported:
|
126
|
-
const SUPPORTED_SOLVERS = ['gurobi', 'scip', 'lp_solve'];
|
126
|
+
const SUPPORTED_SOLVERS = ['gurobi', 'cplex', 'scip', 'lp_solve'];
|
127
127
|
|
128
128
|
// Load class MILPSolver
|
129
129
|
const MILPSolver = require('./static/scripts/linny-r-milp.js');
|
@@ -1458,6 +1458,7 @@ function commandLineSettings() {
|
|
1458
1458
|
const path_list = process.env.PATH.split(path.delimiter);
|
1459
1459
|
let gurobi_path = '',
|
1460
1460
|
scip_path = '',
|
1461
|
+
cplex_path = '',
|
1461
1462
|
match,
|
1462
1463
|
max_v = -1;
|
1463
1464
|
for(let i = 0; i < path_list.length; i++) {
|
@@ -1468,6 +1469,8 @@ function commandLineSettings() {
|
|
1468
1469
|
}
|
1469
1470
|
match = path_list[i].match(/[\/\\]scip[^\/\\]+[\/\\]bin/i);
|
1470
1471
|
if(match) scip_path = path_list[i];
|
1472
|
+
match = path_list[i].match(/[\/\\]cplex[\/\\]bin/i);
|
1473
|
+
if(match) cplex_path = path_list[i];
|
1471
1474
|
match = path_list[i].match(/inkscape/i);
|
1472
1475
|
if(match) settings.inkscape = path_list[i];
|
1473
1476
|
}
|
@@ -1498,8 +1501,25 @@ function commandLineSettings() {
|
|
1498
1501
|
'WARNING: Failed to access the Gurobi command line application');
|
1499
1502
|
}
|
1500
1503
|
}
|
1504
|
+
// Check if cplex(.exe) exists in its directory
|
1505
|
+
let sp = path.join(cplex_path, 'cplex' + (PLATFORM.startsWith('win') ? '.exe' : ''));
|
1506
|
+
const need_cplex = !settings.solver || settings.preferred_solver === 'cplex';
|
1507
|
+
try {
|
1508
|
+
fs.accessSync(sp, fs.constants.X_OK);
|
1509
|
+
console.log('Path to CPLEX:', sp);
|
1510
|
+
if(need_cplex) {
|
1511
|
+
settings.solver = 'cplex';
|
1512
|
+
settings.solver_path = sp;
|
1513
|
+
}
|
1514
|
+
} catch(err) {
|
1515
|
+
// Only report error if CPLEX is needed
|
1516
|
+
if(need_cplex) {
|
1517
|
+
console.log(err.message);
|
1518
|
+
console.log('WARNING: CPLEX application not found in', sp);
|
1519
|
+
}
|
1520
|
+
}
|
1501
1521
|
// Check if scip(.exe) exists in its directory
|
1502
|
-
|
1522
|
+
sp = path.join(scip_path, 'scip' + (PLATFORM.startsWith('win') ? '.exe' : ''));
|
1503
1523
|
const need_scip = !settings.solver || settings.preferred_solver === 'scip';
|
1504
1524
|
try {
|
1505
1525
|
fs.accessSync(sp, fs.constants.X_OK);
|
@@ -1595,7 +1615,7 @@ function createWorkspace() {
|
|
1595
1615
|
data: path.join(SETTINGS.user_dir, 'data'),
|
1596
1616
|
diagrams: path.join(SETTINGS.user_dir, 'diagrams'),
|
1597
1617
|
modules: path.join(SETTINGS.user_dir, 'modules'),
|
1598
|
-
solver_output: path.join(SETTINGS.user_dir, 'solver')
|
1618
|
+
solver_output: path.join(SETTINGS.user_dir, 'solver')
|
1599
1619
|
};
|
1600
1620
|
// Create these sub-directories if not aready there
|
1601
1621
|
try {
|
@@ -82,6 +82,27 @@ module.exports = class MILPSolver {
|
|
82
82
|
14: 'Optimization still in progress',
|
83
83
|
15: 'User-specified objective limit has been reached'
|
84
84
|
};
|
85
|
+
} else if(this.id === 'cplex') {
|
86
|
+
this.ext = '.lp';
|
87
|
+
this.user_model = path.join(workspace.solver_output, 'usr_model.lp');
|
88
|
+
this.solver_model = path.join(workspace.solver_output, 'solver_model.lp');
|
89
|
+
this.solution = path.join(workspace.solver_output, 'model.sol');
|
90
|
+
// NOTE: CPLEX log file is located in the Linny-R working directory
|
91
|
+
this.log = path.join(workspace.solver_output, 'cplex.log');
|
92
|
+
// NOTE: CPLEX command line accepts space separated commands ...
|
93
|
+
this.args = [
|
94
|
+
'read ' + this.user_model,
|
95
|
+
'write ' + this.solver_model + ' lp',
|
96
|
+
'set timelimit 300',
|
97
|
+
'optimize',
|
98
|
+
'write ' + this.solution + ' 0',
|
99
|
+
'quit'
|
100
|
+
];
|
101
|
+
// ... when CPLEX is called with -c option; then each command must be
|
102
|
+
// enclosed in double quotes; SCIP outputs its messages to a log file
|
103
|
+
// terminal, so these must be caputured in a log file
|
104
|
+
this.solve_cmd = 'cplex -c "' + this.args.join('" "') + '"';
|
105
|
+
this.errors = {};
|
85
106
|
} else if(this.id === 'scip') {
|
86
107
|
this.ext = '.lp';
|
87
108
|
this.user_model = path.join(workspace.solver_output, 'usr_model.lp');
|
@@ -217,6 +238,18 @@ module.exports = class MILPSolver {
|
|
217
238
|
this.args[0] = 'TimeLimit=' + timeout;
|
218
239
|
const options = {windowsHide: true};
|
219
240
|
spawn = child_process.spawnSync(this.solver_path, this.args, options);
|
241
|
+
} else if(this.id === 'cplex') {
|
242
|
+
// Delete previous solver model file (if any)
|
243
|
+
try {
|
244
|
+
if(this.solver_model) fs.unlinkSync(this.solver_model);
|
245
|
+
} catch(err) {
|
246
|
+
// Ignore error
|
247
|
+
}
|
248
|
+
// Spawn using the LP_solve approach
|
249
|
+
const
|
250
|
+
cmd = this.solve_cmd.replace(/timelimit \d+/, `timelimit ${timeout}`),
|
251
|
+
options = {shell: true, cwd: 'user/solver', stdio: 'ignore', windowsHide: true};
|
252
|
+
spawn = child_process.spawnSync(cmd, options);
|
220
253
|
} else if(this.id === 'scip') {
|
221
254
|
// When using SCIP, take the LP_solve approach
|
222
255
|
const
|
@@ -311,6 +344,64 @@ module.exports = class MILPSolver {
|
|
311
344
|
result.error = 'No solution found';
|
312
345
|
}
|
313
346
|
}
|
347
|
+
} else if(this.id === 'cplex') {
|
348
|
+
result.seconds = 0;
|
349
|
+
const
|
350
|
+
msg = fs.readFileSync(this.log, 'utf8'),
|
351
|
+
no_license = (msg.indexOf('No license found') >= 0);
|
352
|
+
// `messages` must be an array of strings
|
353
|
+
result.messages = msg.split(os.EOL);
|
354
|
+
let solved = false,
|
355
|
+
output = [];
|
356
|
+
if(no_license) {
|
357
|
+
result.error = 'Too many variables for unlicensed CPLEX solver';
|
358
|
+
result.status = -13;
|
359
|
+
} else if(result.status !== 0) {
|
360
|
+
// Non-zero solver exit code indicates serious trouble
|
361
|
+
result.error = 'CPLEX solver terminated with error';
|
362
|
+
result.status = -13;
|
363
|
+
} else {
|
364
|
+
try {
|
365
|
+
output = fs.readFileSync(this.solution, 'utf8').trim();
|
366
|
+
if(output.indexOf('CPLEXSolution') >= 0) {
|
367
|
+
solved = true;
|
368
|
+
output = output.split(os.EOL);
|
369
|
+
}
|
370
|
+
} catch(err) {
|
371
|
+
console.log('No CPLEX solution file');
|
372
|
+
}
|
373
|
+
}
|
374
|
+
if(solved) {
|
375
|
+
// CPLEX saves solution as XML, but for now just extract the
|
376
|
+
// status and then the variables
|
377
|
+
let i = 0;
|
378
|
+
while(i < output.length) {
|
379
|
+
const s = output[i].split('"');
|
380
|
+
if(s[0].indexOf('objectiveValue') >= 0) {
|
381
|
+
result.obj = s[1];
|
382
|
+
} else if(s[0].indexOf('solutionStatusValue') >= 0) {
|
383
|
+
result.status = s[1];
|
384
|
+
} else if(s[0].indexOf('solutionStatusString') >= 0) {
|
385
|
+
result.error = s[1];
|
386
|
+
break;
|
387
|
+
}
|
388
|
+
i++;
|
389
|
+
}
|
390
|
+
if(['1', '101', '102'].indexOf(result.status) >= 0) {
|
391
|
+
result.status = 0;
|
392
|
+
result.error = '';
|
393
|
+
}
|
394
|
+
// Fill dictionary with variable name: value entries
|
395
|
+
while(i < output.length) {
|
396
|
+
const m = output[i].match(/^.*name="(X[^"]+)".*value="([^"]+)"/);
|
397
|
+
if(m !== null) x_dict[m[1]] = m[2];
|
398
|
+
i++;
|
399
|
+
}
|
400
|
+
// Fill the solution vector, adding 0 for missing columns
|
401
|
+
getValuesFromDict();
|
402
|
+
} else {
|
403
|
+
console.log('No solution found');
|
404
|
+
}
|
314
405
|
} else if(this.id === 'scip') {
|
315
406
|
result.seconds = 0;
|
316
407
|
// `messages` must be an array of strings
|
@@ -332,19 +423,21 @@ module.exports = class MILPSolver {
|
|
332
423
|
const m = result.messages[i];
|
333
424
|
if(m.startsWith('SCIP Status')) {
|
334
425
|
if(m.indexOf('problem is solved') >= 0) {
|
335
|
-
|
426
|
+
if(m.indexOf('infeasible') >= 0) {
|
427
|
+
result.status = (m.indexOf('unbounded') >= 0 ? 14 : 12);
|
428
|
+
} else if(m.indexOf('unbounded') >= 0) {
|
429
|
+
result.status = 13;
|
430
|
+
} else {
|
431
|
+
solved = true;
|
432
|
+
}
|
336
433
|
} else if(m.indexOf('interrupted') >= 0) {
|
337
434
|
if(m.indexOf('time limit') >= 0) {
|
338
435
|
result.status = 5;
|
339
436
|
} else if(m.indexOf('memory limit') >= 0) {
|
340
437
|
result.status = 6;
|
341
|
-
} else if(m.indexOf('infeasible') >= 0) {
|
342
|
-
result.status = (m.indexOf('unbounded') >= 0 ? 14 : 12);
|
343
|
-
} else if(m.indexOf('unbounded') >= 0) {
|
344
|
-
result.status = 13;
|
345
438
|
}
|
346
|
-
result.error = this.errors[result.status];
|
347
439
|
}
|
440
|
+
if(result.status) result.error = this.errors[result.status];
|
348
441
|
} else if (m.startsWith('Solving Time')) {
|
349
442
|
result.seconds = parseFloat(m.split(':')[1]);
|
350
443
|
}
|
@@ -2249,7 +2249,7 @@ class VirtualMachine {
|
|
2249
2249
|
}
|
2250
2250
|
// NOTES:
|
2251
2251
|
// (1) Processes have NO slack variables, because sufficient slack is
|
2252
|
-
// provided by
|
2252
|
+
// provided by adding slack variables to products; these slack
|
2253
2253
|
// variables will have high cost penalty values in the objective
|
2254
2254
|
// function, to serve as "last resort" to still obtain a solution when
|
2255
2255
|
// the "real" product levels are overconstrained
|
@@ -2352,7 +2352,7 @@ class VirtualMachine {
|
|
2352
2352
|
}
|
2353
2353
|
}
|
2354
2354
|
// Likewise get the upper bound
|
2355
|
-
if(p.equal_bounds) {
|
2355
|
+
if(p.equal_bounds && p.lower_bound.defined) {
|
2356
2356
|
u = l;
|
2357
2357
|
} else if(p.upper_bound.defined) {
|
2358
2358
|
if(p.upper_bound.isStatic) {
|
@@ -2575,9 +2575,9 @@ class VirtualMachine {
|
|
2575
2575
|
// efficient, while cash flows are inferred properties and will not
|
2576
2576
|
// result in an "unbounded problem" error message from the solver
|
2577
2577
|
this.code.push(
|
2578
|
-
[
|
2578
|
+
[VMI_set_bounds, [a.cash_in_var_index,
|
2579
2579
|
VM.MINUS_INFINITY, VM.PLUS_INFINITY, true]],
|
2580
|
-
[
|
2580
|
+
[VMI_set_bounds, [a.cash_out_var_index,
|
2581
2581
|
VM.MINUS_INFINITY, VM.PLUS_INFINITY, true]]
|
2582
2582
|
);
|
2583
2583
|
}
|
@@ -2590,18 +2590,17 @@ class VirtualMachine {
|
|
2590
2590
|
if(!MODEL.ignored_entities[k]) {
|
2591
2591
|
p = MODEL.processes[k];
|
2592
2592
|
lbx = p.lower_bound;
|
2593
|
-
|
2593
|
+
// NOTE: if UB = LB, set UB to LB only if LB is defined,
|
2594
|
+
// because LB expressions default to -INF while UB expressions
|
2595
|
+
// default to + INF
|
2596
|
+
ubx = (p.equal_bounds && lbx.defined ? lbx : p.upper_bound);
|
2597
|
+
if(lbx.isStatic) lbx = lbx.result(0);
|
2598
|
+
if(ubx.isStatic) ubx = ubx.result(0);
|
2594
2599
|
// NOTE: pass TRUE as fourth parameter to indicate that +INF
|
2595
2600
|
// and -INF can be coded as the infinity values used by the
|
2596
2601
|
// solver, rather than the Linny-R values used to detect
|
2597
2602
|
// unbounded problems
|
2598
|
-
|
2599
|
-
this.code.push([VMI_set_const_bounds,
|
2600
|
-
[p.level_var_index, lbx.result(0), ubx.result(0), true]]);
|
2601
|
-
} else {
|
2602
|
-
this.code.push([VMI_set_var_bounds,
|
2603
|
-
[p.level_var_index, lbx, ubx, true]]);
|
2604
|
-
}
|
2603
|
+
this.code.push([VMI_set_bounds, [p.level_var_index, lbx, ubx, true]]);
|
2605
2604
|
// Add level variable index to "fixed" list for specified rounds
|
2606
2605
|
const rf = p.actor.round_flags;
|
2607
2606
|
if(rf != 0) {
|
@@ -2629,17 +2628,17 @@ class VirtualMachine {
|
|
2629
2628
|
// If no slack, the bound constraints can be set on the
|
2630
2629
|
// variables themselves
|
2631
2630
|
lbx = p.lower_bound;
|
2632
|
-
|
2633
|
-
|
2634
|
-
|
2635
|
-
|
2636
|
-
|
2637
|
-
|
2638
|
-
|
2631
|
+
// NOTE: if UB = LB, set UB to LB only if LB is defined,
|
2632
|
+
// because LB expressions default to -INF while UB expressions
|
2633
|
+
// default to + INF
|
2634
|
+
ubx = (p.equal_bounds && lbx.defined ? lbx : p.upper_bound);
|
2635
|
+
if(lbx.isStatic) lbx = lbx.result(0);
|
2636
|
+
if(ubx.isStatic) ubx = ubx.result(0);
|
2637
|
+
this.code.push([VMI_set_bounds, [vi, lbx, ubx]]);
|
2639
2638
|
} else {
|
2640
2639
|
// Otherwise, set bounds of stock variable to -INF and +INF,
|
2641
2640
|
// as product constraints will be added later on
|
2642
|
-
this.code.push([
|
2641
|
+
this.code.push([VMI_set_bounds,
|
2643
2642
|
[vi, VM.MINUS_INFINITY, VM.PLUS_INFINITY]]);
|
2644
2643
|
}
|
2645
2644
|
}
|
@@ -3224,7 +3223,7 @@ class VirtualMachine {
|
|
3224
3223
|
// To deal with this, the default equations will NOT be set when UB <= 0,
|
3225
3224
|
// while the "exceptional" equations (q.v.) will NOT be set when UB > 0.
|
3226
3225
|
// This can be realized using a special VM instruction:
|
3227
|
-
ubx = (p.equal_bounds && p.lower_bound.
|
3226
|
+
ubx = (p.equal_bounds && p.lower_bound.defined ? p.lower_bound : p.upper_bound);
|
3228
3227
|
this.code.push([VMI_set_add_constraints_flag, [ubx, '>', 0]]);
|
3229
3228
|
|
3230
3229
|
// NOTE: if UB <= 0 the following constraints will be prepared but NOT added
|
@@ -4731,8 +4730,6 @@ class VirtualMachine {
|
|
4731
4730
|
return [-2, 2, 6];
|
4732
4731
|
} else if(this.solver_name === 'gurobi') {
|
4733
4732
|
return [1, 3, 4, 6, 11, 12, 14];
|
4734
|
-
} else if(this.solver_name === 'scip') {
|
4735
|
-
return [];
|
4736
4733
|
} else {
|
4737
4734
|
return [];
|
4738
4735
|
}
|
@@ -6446,8 +6443,8 @@ from) the k'th coefficient.
|
|
6446
6443
|
|
6447
6444
|
*/
|
6448
6445
|
|
6449
|
-
function
|
6450
|
-
// `args`: [var_index, number, number]
|
6446
|
+
function VMI_set_bounds(args) {
|
6447
|
+
// `args`: [var_index, number or expression, number or expression]
|
6451
6448
|
const
|
6452
6449
|
vi = args[0],
|
6453
6450
|
vbl = VM.variables[vi - 1][1],
|
@@ -6465,7 +6462,8 @@ function VMI_set_const_bounds(args) {
|
|
6465
6462
|
// if this is the first round
|
6466
6463
|
if(VM.current_round) {
|
6467
6464
|
l = vbl.actualLevel(VM.t);
|
6468
|
-
//PATCH
|
6465
|
+
// QUICK PATCH! should resolve that small non-zero process levels
|
6466
|
+
// computed in prior round make problem infeasible
|
6469
6467
|
if(l < 0.0005) l = 0;
|
6470
6468
|
} else {
|
6471
6469
|
l = 0;
|
@@ -6474,17 +6472,22 @@ function VMI_set_const_bounds(args) {
|
|
6474
6472
|
fixed = ' (FIXED ' + vbl.displayName + ')';
|
6475
6473
|
} else {
|
6476
6474
|
// Set bounds as specified by the two arguments
|
6477
|
-
l =
|
6478
|
-
|
6475
|
+
l = args[1];
|
6476
|
+
if(l instanceof Expression) l = l.result(VM.t);
|
6477
|
+
if(l === VM.UNDEFINED) l = 0;
|
6478
|
+
u = args[2];
|
6479
|
+
if(u instanceof Expression) u = u.result(VM.t);
|
6480
|
+
u = Math.min(u, VM.PLUS_INFINITY);
|
6479
6481
|
if(solver_inf) {
|
6480
6482
|
if(l === VM.MINUS_INFINITY) l = -inf_val;
|
6481
6483
|
if(u === VM.PLUS_INFINITY) u = inf_val;
|
6482
6484
|
}
|
6483
6485
|
fixed = '';
|
6484
6486
|
}
|
6485
|
-
// NOTE: to
|
6487
|
+
// NOTE: to see in the console whether fixing across rounds works, insert
|
6488
|
+
// "fixed !== '' || " before DEBUGGING below
|
6486
6489
|
if(DEBUGGING) {
|
6487
|
-
console.log(['
|
6490
|
+
console.log(['set_bounds [', k, '] ', vbl.displayName, ' t = ', VM.t,
|
6488
6491
|
' LB = ', VM.sig4Dig(l), ', UB = ', VM.sig4Dig(u), fixed].join(''));
|
6489
6492
|
}
|
6490
6493
|
// NOTE: since the VM vectors for lower bounds and upper bounds are
|
@@ -6511,61 +6514,6 @@ function VMI_set_const_bounds(args) {
|
|
6511
6514
|
}
|
6512
6515
|
}
|
6513
6516
|
|
6514
|
-
function VMI_set_var_bounds(args) {
|
6515
|
-
// `args`: [var_index, expression, expression]
|
6516
|
-
const
|
6517
|
-
vi = args[0],
|
6518
|
-
vbl = VM.variables[vi - 1][1],
|
6519
|
-
k = VM.offset + vi,
|
6520
|
-
r = VM.round_letters.indexOf(VM.round_sequence[VM.current_round]),
|
6521
|
-
// Optional fourth parameter indicates whether the solver's
|
6522
|
-
// infinity values should be used
|
6523
|
-
solver_inf = args.length > 3 && args[3],
|
6524
|
-
inf_val = (solver_inf ? VM.SOLVER_PLUS_INFINITY : VM.PLUS_INFINITY);
|
6525
|
-
let l,
|
6526
|
-
u,
|
6527
|
-
fixed = (vi in VM.fixed_var_indices[r - 1]);
|
6528
|
-
if(fixed) {
|
6529
|
-
// Set both bounds equal to the level set in the previous round, or to 0
|
6530
|
-
// if this is the first round
|
6531
|
-
if(VM.current_round) {
|
6532
|
-
l = vbl.actualLevel(VM.t);
|
6533
|
-
} else {
|
6534
|
-
l = 0;
|
6535
|
-
}
|
6536
|
-
u = l;
|
6537
|
-
fixed = ' (FIXED ' + vbl.displayName + ')';
|
6538
|
-
} else {
|
6539
|
-
l = args[1].result(VM.t);
|
6540
|
-
if(l === VM.UNDEFINED) l = 0;
|
6541
|
-
u = Math.min(args[2].result(VM.t), inf_val);
|
6542
|
-
if(solver_inf) {
|
6543
|
-
if(l === VM.MINUS_INFINITY) l = -inf_val;
|
6544
|
-
if(u === VM.PLUS_INFINITY) u = inf_val;
|
6545
|
-
}
|
6546
|
-
fixed = '';
|
6547
|
-
}
|
6548
|
-
// Here, too, no need to set default values
|
6549
|
-
if(Math.abs(l) > VM.NEAR_ZERO || u !== inf_val) {
|
6550
|
-
VM.lower_bounds[k] = l;
|
6551
|
-
VM.upper_bounds[k] = u;
|
6552
|
-
// Check for peak increase -- see comments in VMI_set_const_bound
|
6553
|
-
if(vbl.peak_inc_var_index >= 0) {
|
6554
|
-
u = Math.max(0, u - vbl.b_peak[VM.block_count - 1]);
|
6555
|
-
const
|
6556
|
-
cvi = VM.chunk_offset + vbl.peak_inc_var_index,
|
6557
|
-
piub = VM.upper_bounds[cvi];
|
6558
|
-
if(piub) u = Math.max(piub, u);
|
6559
|
-
VM.upper_bounds[cvi] = u;
|
6560
|
-
VM.upper_bounds[cvi + 1] = u;
|
6561
|
-
}
|
6562
|
-
}
|
6563
|
-
if(DEBUGGING) {
|
6564
|
-
console.log(['set_var_bounds [', k, '] ', vbl.displayName, ' t = ', VM.t,
|
6565
|
-
' LB = ', l, ', UB = ', u, fixed].join(''));
|
6566
|
-
}
|
6567
|
-
}
|
6568
|
-
|
6569
6517
|
function VMI_clear_coefficients(empty) {
|
6570
6518
|
if(DEBUGGING) console.log('clear_coefficients');
|
6571
6519
|
VM.coefficients = {};
|