linny-r 1.3.3 → 1.3.4
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 +17 -0
- package/package.json +1 -1
- package/server.js +17 -2
- package/static/scripts/linny-r-gui.js +50 -8
- package/static/scripts/linny-r-model.js +17 -11
- package/static/scripts/linny-r-vm.js +9 -6
package/console.js
CHANGED
@@ -932,6 +932,23 @@ function commandLineSettings() {
|
|
932
932
|
gurobi_path = path_list[i];
|
933
933
|
max_v = parseInt(match[1]);
|
934
934
|
}
|
935
|
+
match = path_list[i].match(/[\/\\]cplex[\/\\]bin/i);
|
936
|
+
if(match) {
|
937
|
+
cplex_path = path_list[i];
|
938
|
+
} else {
|
939
|
+
// NOTE: CPLEX may create its own environment variable for its paths
|
940
|
+
match = path_list[i].match(/%(.*cplex.*)%/i);
|
941
|
+
if(match) {
|
942
|
+
const cpl = process.env[match[1]].split(path.delimiter);
|
943
|
+
for(let i = 0; i < cpl.length; i++) {
|
944
|
+
match = cpl[i].match(/[\/\\]cplex[\/\\]bin/i);
|
945
|
+
if(match) {
|
946
|
+
cplex_path = cpl[i];
|
947
|
+
break;
|
948
|
+
}
|
949
|
+
}
|
950
|
+
}
|
951
|
+
}
|
935
952
|
match = path_list[i].match(/[\/\\]scip[^\/\\]+[\/\\]bin/i);
|
936
953
|
if(match) scip_path = path_list[i];
|
937
954
|
match = path_list[i].match(/inkscape/i);
|
package/package.json
CHANGED
package/server.js
CHANGED
@@ -1467,10 +1467,25 @@ function commandLineSettings() {
|
|
1467
1467
|
gurobi_path = path_list[i];
|
1468
1468
|
max_v = parseInt(match[1]);
|
1469
1469
|
}
|
1470
|
+
match = path_list[i].match(/[\/\\]cplex[\/\\]bin/i);
|
1471
|
+
if(match) {
|
1472
|
+
cplex_path = path_list[i];
|
1473
|
+
} else {
|
1474
|
+
// NOTE: CPLEX may create its own environment variable for its paths
|
1475
|
+
match = path_list[i].match(/%(.*cplex.*)%/i);
|
1476
|
+
if(match) {
|
1477
|
+
const cpl = process.env[match[1]].split(path.delimiter);
|
1478
|
+
for(let i = 0; i < cpl.length; i++) {
|
1479
|
+
match = cpl[i].match(/[\/\\]cplex[\/\\]bin/i);
|
1480
|
+
if(match) {
|
1481
|
+
cplex_path = cpl[i];
|
1482
|
+
break;
|
1483
|
+
}
|
1484
|
+
}
|
1485
|
+
}
|
1486
|
+
}
|
1470
1487
|
match = path_list[i].match(/[\/\\]scip[^\/\\]+[\/\\]bin/i);
|
1471
1488
|
if(match) scip_path = path_list[i];
|
1472
|
-
match = path_list[i].match(/[\/\\]cplex[\/\\]bin/i);
|
1473
|
-
if(match) cplex_path = path_list[i];
|
1474
1489
|
match = path_list[i].match(/inkscape/i);
|
1475
1490
|
if(match) settings.inkscape = path_list[i];
|
1476
1491
|
}
|
@@ -5273,9 +5273,30 @@ class GUIController extends Controller {
|
|
5273
5273
|
md.element('actor').value = mapping.actor || '';
|
5274
5274
|
md.element('prefix').value = mapping.prefix || '';
|
5275
5275
|
const
|
5276
|
-
|
5276
|
+
tc = (mapping.top_clusters ?
|
5277
|
+
Object.keys(mapping.top_clusters).sort(ciCompare) : []),
|
5278
|
+
ft = (mapping.from_to ?
|
5279
|
+
Object.keys(mapping.from_to).sort(ciCompare) : []),
|
5277
5280
|
sl = [];
|
5281
|
+
if(tc.length) {
|
5282
|
+
sl.push('<div style="font-weight: bold; margin:4px 2px 2px 2px">',
|
5283
|
+
'Names for top-level clusters:</div>');
|
5284
|
+
// Add text inputs for selected cluster nodes
|
5285
|
+
for(let i = 0; i < tc.length; i++) {
|
5286
|
+
const
|
5287
|
+
ti = mapping.top_clusters[tc[i]],
|
5288
|
+
state = (ti === tc[i] ? 'color: #e09000; ' :
|
5289
|
+
this.validName(ti) ? 'color: #0000c0; ' :
|
5290
|
+
'font-style: italic; color: red; ');
|
5291
|
+
sl.push('<div class="paste-option"><span>', tc[i], '</span> ',
|
5292
|
+
'<div class="paste-select"><input id="paste-selc-', i,
|
5293
|
+
'" type="text" style="', state, 'font-size: 12px" value="',
|
5294
|
+
ti, '"></div></div>');
|
5295
|
+
}
|
5296
|
+
}
|
5278
5297
|
if(ft.length) {
|
5298
|
+
sl.push('<div style="font-weight: bold; margin:4px 2px 2px 2px">',
|
5299
|
+
'Mapping of nodes to link from/to:</div>');
|
5279
5300
|
// Add selectors for unresolved FROM/TO nodes
|
5280
5301
|
for(let i = 0; i < ft.length; i++) {
|
5281
5302
|
const ti = mapping.from_to[ft[i]];
|
@@ -5286,8 +5307,8 @@ class GUIController extends Controller {
|
|
5286
5307
|
'</option></select></div></div>');
|
5287
5308
|
}
|
5288
5309
|
}
|
5289
|
-
md.element('scroll-area').innerHTML = sl.join('');
|
5290
5310
|
}
|
5311
|
+
md.element('scroll-area').innerHTML = sl.join('');
|
5291
5312
|
// Open dialog, which will call pasteSelection(...) on OK
|
5292
5313
|
this.paste_modal.show();
|
5293
5314
|
}
|
@@ -5297,10 +5318,18 @@ class GUIController extends Controller {
|
|
5297
5318
|
// proceeds to paste
|
5298
5319
|
const
|
5299
5320
|
md = this.paste_modal,
|
5300
|
-
mapping = Object.assign(md.mapping, {})
|
5321
|
+
mapping = Object.assign(md.mapping, {}),
|
5322
|
+
tc = (mapping.top_clusters ?
|
5323
|
+
Object.keys(mapping.top_clusters).sort(ciCompare) : []),
|
5324
|
+
ft = (mapping.from_to ?
|
5325
|
+
Object.keys(mapping.from_to).sort(ciCompare) : []);
|
5301
5326
|
mapping.actor = md.element('actor').value;
|
5302
5327
|
mapping.prefix = md.element('prefix').value.trim();
|
5303
5328
|
mapping.increment = true;
|
5329
|
+
for(let i = 0; i < tc.length; i++) {
|
5330
|
+
const cn = md.element('selc-' + i).value.trim();
|
5331
|
+
if(this.validName(cn)) mapping.top_clusters[tc[i]] = cn;
|
5332
|
+
}
|
5304
5333
|
this.pasteSelection(mapping);
|
5305
5334
|
}
|
5306
5335
|
|
@@ -5325,6 +5354,7 @@ console.log('HERE xml', xml);
|
|
5325
5354
|
entities_node = childNodeByTag(xml, 'entities'),
|
5326
5355
|
from_tos_node = childNodeByTag(xml, 'from-tos'),
|
5327
5356
|
extras_node = childNodeByTag(xml, 'extras'),
|
5357
|
+
selc_node = childNodeByTag(xml, 'selected-clusters'),
|
5328
5358
|
selection_node = childNodeByTag(xml, 'selection'),
|
5329
5359
|
actor_names = [],
|
5330
5360
|
new_entities = [],
|
@@ -5335,7 +5365,7 @@ console.log('HERE xml', xml);
|
|
5335
5365
|
|
5336
5366
|
function fullName(node) {
|
5337
5367
|
// Returns full entity name inferred from XML node data
|
5338
|
-
if(node.nodeName === 'from-to') {
|
5368
|
+
if(node.nodeName === 'from-to' || node.nodeName === 'selc') {
|
5339
5369
|
const
|
5340
5370
|
n = xmlDecoded(nodeParameterValue(node, 'name')),
|
5341
5371
|
an = xmlDecoded(nodeParameterValue(node, 'actor-name'));
|
@@ -5525,6 +5555,17 @@ console.log('HERE xml', xml);
|
|
5525
5555
|
if(parseInt(mts) === MODEL.time_created.getTime() &&
|
5526
5556
|
ca === fca && mapping.from_prefix === mapping.to_prefix &&
|
5527
5557
|
!(mapping.prefix || mapping.actor || mapping.increment)) {
|
5558
|
+
// Prompt for names of selected cluster nodes
|
5559
|
+
if(selc_node.childNodes.length && !mapping.prefix) {
|
5560
|
+
mapping.top_clusters = {};
|
5561
|
+
for(let i = 0; i < selc_node.childNodes.length; i++) {
|
5562
|
+
const
|
5563
|
+
c = selc_node.childNodes[i],
|
5564
|
+
fn = fullName(c),
|
5565
|
+
mn = mappedName(fn);
|
5566
|
+
mapping.top_clusters[fn] = mn;
|
5567
|
+
}
|
5568
|
+
}
|
5528
5569
|
this.promptForMapping(mapping);
|
5529
5570
|
return;
|
5530
5571
|
}
|
@@ -6525,11 +6566,12 @@ class GUIMonitor {
|
|
6525
6566
|
})
|
6526
6567
|
.then((data) => {
|
6527
6568
|
try {
|
6528
|
-
const
|
6529
|
-
|
6530
|
-
|
6531
|
-
|
6569
|
+
const
|
6570
|
+
jsr = JSON.parse(data),
|
6571
|
+
svr = `Solver on ${jsr.server} is ${jsr.solver}`;
|
6572
|
+
if(jsr.solver !== VM.solver_name) UI.notify(svr);
|
6532
6573
|
VM.solver_name = jsr.solver;
|
6574
|
+
document.getElementById('host-logo').title = svr;
|
6533
6575
|
} catch(err) {
|
6534
6576
|
console.log(err, data);
|
6535
6577
|
UI.alert('ERROR: Unexpected data from server: ' +
|
@@ -1609,16 +1609,20 @@ class LinnyRModel {
|
|
1609
1609
|
extras = [],
|
1610
1610
|
from_tos = [],
|
1611
1611
|
xml = [],
|
1612
|
-
ft_xml = [],
|
1613
1612
|
extra_xml = [],
|
1613
|
+
ft_xml = [],
|
1614
|
+
selc_xml = [],
|
1614
1615
|
selected_xml = [];
|
1615
1616
|
for(let i = 0; i < this.selection.length; i++) {
|
1616
1617
|
const obj = this.selection[i];
|
1617
1618
|
entities[obj.type].push(obj);
|
1618
|
-
|
1619
|
+
if(obj instanceof Cluster) selc_xml.push(
|
1620
|
+
'<selc name="', xmlEncoded(obj.name),
|
1621
|
+
'" actor-name="', xmlEncoded(obj.actor.name), '"></selc>');
|
1622
|
+
selected_xml.push(`<sel>${xmlEncoded(obj.displayName)}</sel>`);
|
1619
1623
|
}
|
1620
|
-
// Expand clusters by adding all
|
1621
|
-
// lists
|
1624
|
+
// Expand (sub)clusters by adding all their model entities to their
|
1625
|
+
// respective lists
|
1622
1626
|
for(let i = 0; i < entities.Cluster.length; i++) {
|
1623
1627
|
const c = entities.Cluster[i];
|
1624
1628
|
c.clearAllProcesses();
|
@@ -1714,7 +1718,8 @@ class LinnyRModel {
|
|
1714
1718
|
'"><entities>', xml.join(''),
|
1715
1719
|
'</entities><from-tos>', ft_xml.join(''),
|
1716
1720
|
'</from-tos><extras>', extra_xml.join(''),
|
1717
|
-
'</extras><
|
1721
|
+
'</extras><selected-clusters>', selc_xml.join(''),
|
1722
|
+
'</selected-clusters><selection>', selected_xml.join(''),
|
1718
1723
|
'</selection></copy>'].join('');
|
1719
1724
|
}
|
1720
1725
|
|
@@ -3236,6 +3241,7 @@ class LinnyRModel {
|
|
3236
3241
|
for(let i = 0; i < constraints.length; i++) {
|
3237
3242
|
const
|
3238
3243
|
c = constraints[i],
|
3244
|
+
// NOTE: constraints in list have levels greater than near-zero
|
3239
3245
|
fl = c.from_node.actualLevel(t),
|
3240
3246
|
tl = c.to_node.actualLevel(t);
|
3241
3247
|
let tcp;
|
@@ -3306,10 +3312,10 @@ class LinnyRModel {
|
|
3306
3312
|
if(af > VM.NEAR_ZERO) {
|
3307
3313
|
// Prevent division by zero
|
3308
3314
|
// NOTE: level can be zero even if actual flow > 0!
|
3309
|
-
const al = p.
|
3315
|
+
const al = p.nonZeroLevel(dt);
|
3310
3316
|
// NOTE: scale to level only when level > 1, or fixed
|
3311
3317
|
// costs for start-up or first commit will be amplified
|
3312
|
-
if(al > VM.
|
3318
|
+
if(al > VM.NEAR_ZERO) cp -= pr * af / Math.max(al, 1);
|
3313
3319
|
}
|
3314
3320
|
}
|
3315
3321
|
}
|
@@ -3428,8 +3434,8 @@ class LinnyRModel {
|
|
3428
3434
|
p.cost_price[t] = cp;
|
3429
3435
|
// For stocks, the CP includes stock price on t-1
|
3430
3436
|
if(p.is_buffer) {
|
3431
|
-
const prevl = p.
|
3432
|
-
if(prevl >
|
3437
|
+
const prevl = p.nonZeroLevel(t-1);
|
3438
|
+
if(prevl > VM.NEAR_ZERO) {
|
3433
3439
|
cp = (cnp + prevl * p.stockPrice(t-1)) / (qnp + prevl);
|
3434
3440
|
}
|
3435
3441
|
p.stock_price[t] = cp;
|
@@ -3486,7 +3492,7 @@ class LinnyRModel {
|
|
3486
3492
|
// Then (also) look for links having AF = 0 ...
|
3487
3493
|
for(let i = links.length-1; i >= 0; i--) {
|
3488
3494
|
const af = links[i].actualFlow(t);
|
3489
|
-
if(Math.abs(af) < VM.
|
3495
|
+
if(Math.abs(af) < VM.NEAR_ZERO) {
|
3490
3496
|
// ... and set their UCP to 0
|
3491
3497
|
links[i].unit_cost_price = 0;
|
3492
3498
|
links.splice(i, 1);
|
@@ -3522,7 +3528,7 @@ class LinnyRModel {
|
|
3522
3528
|
let hcp = VM.MINUS_INFINITY;
|
3523
3529
|
for(let i = 0; i < p.inputs.length; i++) {
|
3524
3530
|
const l = p.inputs[i];
|
3525
|
-
if(l.from_node instanceof Process && l.actualFlow(t) >
|
3531
|
+
if(l.from_node instanceof Process && l.actualFlow(t) > VM.NEAR_ZERO) {
|
3526
3532
|
const ld = l.actualDelay(t);
|
3527
3533
|
// NOTE: only consider the allocated share of cost
|
3528
3534
|
let cp = l.from_node.costPrice(t - ld) * l.share_of_cost;
|
@@ -3255,19 +3255,22 @@ class VirtualMachine {
|
|
3255
3255
|
if(ubx.isStatic) {
|
3256
3256
|
// If UB is very high (typically: undefined, so +INF), try to infer
|
3257
3257
|
// a lower value for UB to use for the ON/OFF binary
|
3258
|
-
let ub = ubx.result(0)
|
3258
|
+
let ub = ubx.result(0),
|
3259
|
+
hub = ub;
|
3259
3260
|
if(ub > VM.MEGA_UPPER_BOUND) {
|
3260
|
-
|
3261
|
+
hub = p.highestUpperBound([]);
|
3261
3262
|
// If UB still very high, warn modeler on infoline and in monitor
|
3262
|
-
if(
|
3263
|
-
const msg = 'High upper bound (' + this.sig4Dig(
|
3263
|
+
if(hub > VM.MEGA_UPPER_BOUND) {
|
3264
|
+
const msg = 'High upper bound (' + this.sig4Dig(hub) +
|
3264
3265
|
') for <strong>' + p.displayName + '</strong>' +
|
3265
3266
|
' will compromise computation of its binary variables';
|
3266
3267
|
UI.warn(msg);
|
3267
3268
|
this.logMessage(this.block_count,
|
3268
3269
|
'WARNING: ' + msg.replace(/<\/?strong>/g, '"'));
|
3269
3270
|
}
|
3270
|
-
}
|
3271
|
+
}
|
3272
|
+
if(hub !== ub) {
|
3273
|
+
ub = hub;
|
3271
3274
|
this.logMessage(this.block_count,
|
3272
3275
|
`Inferred upper bound for ${p.displayName}: ${this.sig4Dig(ub)}`);
|
3273
3276
|
}
|
@@ -3771,7 +3774,7 @@ class VirtualMachine {
|
|
3771
3774
|
for(let k = 0; k < l; k++) {
|
3772
3775
|
const
|
3773
3776
|
vi = svl[k],
|
3774
|
-
slack = x[vi + j],
|
3777
|
+
slack = parseFloat(x[vi + j]),
|
3775
3778
|
absl = Math.abs(slack);
|
3776
3779
|
if(absl > VM.NEAR_ZERO) {
|
3777
3780
|
const v = this.variables[vi - 1];
|