linny-r 1.6.5 → 1.6.7
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.
@@ -3199,25 +3199,25 @@ class VirtualMachine {
|
|
3199
3199
|
}
|
3200
3200
|
|
3201
3201
|
// Now all variables that get a tableau column in each time step have
|
3202
|
-
// been defined; next step is to add "chunk variables"
|
3202
|
+
// been defined; next step is to add "chunk variables".
|
3203
3203
|
let cvi = 0;
|
3204
|
-
// Add *two* chunk variables for processes having a peak increase link
|
3204
|
+
// Add *two* chunk variables for processes having a peak increase link.
|
3205
3205
|
for(i = 0; i < process_keys.length; i++) {
|
3206
3206
|
k = process_keys[i];
|
3207
3207
|
p = MODEL.processes[k];
|
3208
3208
|
if(!MODEL.ignored_entities[k] && p.needsMaximumData) {
|
3209
|
-
// "peak increase" for block
|
3209
|
+
// First variable: "peak increase" for block.
|
3210
3210
|
p.peak_inc_var_index = cvi;
|
3211
3211
|
this.chunk_variables.push(['b-peak', p]);
|
3212
3212
|
cvi++;
|
3213
|
-
//
|
3214
|
-
// NOTE:
|
3215
|
-
// equal to block peak index + 1
|
3213
|
+
// Additional "peak increase" for the look-ahead period.
|
3214
|
+
// NOTE: No need to record the second index as it wil allways be
|
3215
|
+
// equal to block peak index + 1.
|
3216
3216
|
this.chunk_variables.push(['la-peak', p]);
|
3217
3217
|
cvi++;
|
3218
3218
|
}
|
3219
3219
|
}
|
3220
|
-
// Do likewise for such products
|
3220
|
+
// Do likewise for such products.
|
3221
3221
|
for(i = 0; i < product_keys.length; i++) {
|
3222
3222
|
k = product_keys[i];
|
3223
3223
|
p = MODEL.products[k];
|
@@ -3230,12 +3230,13 @@ class VirtualMachine {
|
|
3230
3230
|
}
|
3231
3231
|
}
|
3232
3232
|
|
3233
|
-
// Now *all* variables have been defined
|
3233
|
+
// Now *all* variables have been defined. The next step is to set
|
3234
|
+
// their bounds.
|
3234
3235
|
|
3235
|
-
// NOTE:
|
3236
|
-
//
|
3236
|
+
// NOTE: Chunk variables of node `p` have LB = 0 and UB = UB of `p`.
|
3237
|
+
// This is effectuated by the VM "set bounds" instructions at run time.
|
3237
3238
|
|
3238
|
-
// NOTE:
|
3239
|
+
// NOTE: Under normal assumptions (all processes having LB >= 0), bounds on
|
3239
3240
|
// actor cash flow variables need NOT be set because cash IN and cash OUT
|
3240
3241
|
// will then always be >= 0 (solver's default bounds).
|
3241
3242
|
// However, Linny-R does not prohibit negative bounds on processes, nor
|
@@ -3243,10 +3244,10 @@ class VirtualMachine {
|
|
3243
3244
|
// cash OUT of all actors are both allowed to become negative.
|
3244
3245
|
for(i = 0; i < actor_keys.length; i++) {
|
3245
3246
|
const a = MODEL.actors[actor_keys[i]];
|
3246
|
-
// NOTE:
|
3247
|
+
// NOTE: Add fourth parameter TRUE to signal that the SOLVER's
|
3247
3248
|
// infinity constants should be used, as this is likely to be more
|
3248
3249
|
// efficient, while cash flows are inferred properties and will not
|
3249
|
-
// result in an "unbounded problem" error message from the solver
|
3250
|
+
// result in an "unbounded problem" error message from the solver.
|
3250
3251
|
this.code.push(
|
3251
3252
|
[VMI_set_bounds, [a.cash_in_var_index,
|
3252
3253
|
VM.MINUS_INFINITY, VM.PLUS_INFINITY, true]],
|
@@ -3255,9 +3256,9 @@ class VirtualMachine {
|
|
3255
3256
|
);
|
3256
3257
|
}
|
3257
3258
|
|
3258
|
-
// NEXT: Define the bounds for all production level variables
|
3259
|
-
// NOTE:
|
3260
|
-
// is listed as "fixed" for the round that is being solved
|
3259
|
+
// NEXT: Define the bounds for all production level variables.
|
3260
|
+
// NOTE: The VM instructions check dynamically whether the variable
|
3261
|
+
// index is listed as "fixed" for the round that is being solved.
|
3261
3262
|
for(i = 0; i < process_keys.length; i++) {
|
3262
3263
|
k = process_keys[i];
|
3263
3264
|
if(!MODEL.ignored_entities[k]) {
|
@@ -3411,13 +3412,13 @@ class VirtualMachine {
|
|
3411
3412
|
} else if(l.multiplier === VM.LM_ZERO) {
|
3412
3413
|
vi = p.is_zero_var_index;
|
3413
3414
|
}
|
3414
|
-
// NOTE: "throughput", "spinning reserve" and "peak increase"
|
3415
|
-
// special cases that send a different parameter list
|
3415
|
+
// NOTE: "throughput", "spinning reserve" and "peak increase"
|
3416
|
+
// are special cases that send a different parameter list.
|
3416
3417
|
if(l.multiplier === VM.LM_THROUGHPUT) {
|
3417
3418
|
// When throughput is read from process Y, calculation
|
3418
3419
|
// is simple: no delays, so the flow over link `l`
|
3419
3420
|
// equals the (sum of all Ri) times the level of Y
|
3420
|
-
// times the rate of `l
|
3421
|
+
// times the rate of `l`.
|
3421
3422
|
for(k = 0; k < l.from_node.inputs.length; j++) {
|
3422
3423
|
ll = l.from_node.inputs[k];
|
3423
3424
|
// NOTE: no attempt for efficiency -- assume that
|
@@ -3441,7 +3442,7 @@ class VirtualMachine {
|
|
3441
3442
|
// of the block being optimized, and in the first step of the
|
3442
3443
|
// look-ahead period (if peak rises in that period), and will
|
3443
3444
|
// be 0 in all other time steps; the VM instruction handles this
|
3444
|
-
// NOTE:
|
3445
|
+
// NOTE: Delay is always 0 for this link flow.
|
3445
3446
|
this.code.push([VMI_update_cash_coefficient, [
|
3446
3447
|
VM.PRODUCE, VM.PEAK_INC, p.peak_inc_var_index, 0,
|
3447
3448
|
tnpx, l.relative_rate]]);
|
@@ -4295,7 +4296,7 @@ class VirtualMachine {
|
|
4295
4296
|
cv = this.chunk_variables[ci - this.chunk_offset];
|
4296
4297
|
}
|
4297
4298
|
// NOTE: Do not scale the coefficient of the cash variable.
|
4298
|
-
if(!cv[0].startsWith('C')) cc[ci] *= m;
|
4299
|
+
if(cv && !cv[0].startsWith('C')) cc[ci] *= m;
|
4299
4300
|
}
|
4300
4301
|
}
|
4301
4302
|
}
|
@@ -4399,22 +4400,22 @@ class VirtualMachine {
|
|
4399
4400
|
has_OO = (p.on_off_var_index >= 0),
|
4400
4401
|
has_SU = (p.start_up_var_index >= 0),
|
4401
4402
|
has_SD = (p.shut_down_var_index >= 0);
|
4402
|
-
// Clear all start-ups and shut-downs at t >= bb
|
4403
|
+
// Clear all start-ups and shut-downs at t >= bb.
|
4403
4404
|
if(has_SU) p.resetStartUps(bb);
|
4404
4405
|
if(has_SD) p.resetShutDowns(bb);
|
4405
|
-
// NOTE: `b` is the index to be used for the vectors
|
4406
|
+
// NOTE: `b` is the index to be used for the vectors.
|
4406
4407
|
let b = bb;
|
4407
|
-
// Iterate over all time steps in this block
|
4408
|
-
// NOTE: -1 because indices start at 1, but list is zero-based
|
4408
|
+
// Iterate over all time steps in this block.
|
4409
|
+
// NOTE: -1 because indices start at 1, but list is zero-based.
|
4409
4410
|
let j = -1;
|
4410
4411
|
for(let i = 0; i < abl; i++) {
|
4411
4412
|
p.level[b] = this.checkForInfinity(x[p.level_var_index + j]);
|
4412
4413
|
// @@TO DO: If ON/OFF is relevant, check whether it is correctly inferred
|
4413
4414
|
if(has_OO) {
|
4414
4415
|
if(has_SU) {
|
4415
|
-
// NOTE:
|
4416
|
+
// NOTE: Some solvers (Gurobi!) may return real numbers instead of
|
4416
4417
|
// integers, typically near-zero or near-one, so only consider
|
4417
|
-
// values near 1 to indicate start-up
|
4418
|
+
// values near 1 to indicate start-up.
|
4418
4419
|
if(x[p.start_up_var_index + j] > 0.999) {
|
4419
4420
|
p.start_ups.push(b);
|
4420
4421
|
}
|
@@ -4425,13 +4426,13 @@ class VirtualMachine {
|
|
4425
4426
|
}
|
4426
4427
|
}
|
4427
4428
|
}
|
4428
|
-
// Advance column offset in tableau by the # cols per time step
|
4429
|
+
// Advance column offset in tableau by the # cols per time step.
|
4429
4430
|
j += this.cols;
|
4430
|
-
// Advance to the next time step in this block
|
4431
|
+
// Advance to the next time step in this block.
|
4431
4432
|
b++;
|
4432
4433
|
}
|
4433
4434
|
}
|
4434
|
-
// Set stock levels for all products
|
4435
|
+
// Set stock levels for all products.
|
4435
4436
|
for(let o in MODEL.products) if(MODEL.products.hasOwnProperty(o) &&
|
4436
4437
|
!MODEL.ignored_entities[o]) {
|
4437
4438
|
const
|
@@ -4439,23 +4440,23 @@ class VirtualMachine {
|
|
4439
4440
|
has_OO = (p.on_off_var_index >= 0),
|
4440
4441
|
has_SU = (p.start_up_var_index >= 0),
|
4441
4442
|
has_SD = (p.shut_down_var_index >= 0);
|
4442
|
-
// Clear all start-ups and shut-downs at t >= bb
|
4443
|
+
// Clear all start-ups and shut-downs at t >= bb.
|
4443
4444
|
if(has_SU) p.resetStartUps(bb);
|
4444
4445
|
if(has_SD) p.resetShutDowns(bb);
|
4445
4446
|
let b = bb;
|
4446
|
-
// Iterate over all time steps in this block
|
4447
|
+
// Iterate over all time steps in this block.
|
4447
4448
|
let j = -1;
|
4448
4449
|
for(let i = 0; i < abl; i++) {
|
4449
4450
|
p.level[b] = this.checkForInfinity(x[p.level_var_index + j]);
|
4450
4451
|
// @@TO DO: If ON/OFF is relevant, check whether it is correctly inferred
|
4451
4452
|
if(has_OO) {
|
4452
|
-
// Check if start-up variable is set (see NOTE above)
|
4453
|
+
// Check if start-up variable is set (see NOTE above).
|
4453
4454
|
if(has_SU) {
|
4454
4455
|
if(x[p.start_up_var_index + j] > 0.999) {
|
4455
4456
|
p.start_ups.push(b);
|
4456
4457
|
}
|
4457
4458
|
}
|
4458
|
-
// Same for shut-down variable
|
4459
|
+
// Same for shut-down variable.
|
4459
4460
|
if(has_SD) {
|
4460
4461
|
if(x[p.shut_down_var_index + j] > 0.999) {
|
4461
4462
|
p.shut_downs.push(b);
|
@@ -4475,11 +4476,11 @@ class VirtualMachine {
|
|
4475
4476
|
p.b_peak_inc[block] = x[offset + i];
|
4476
4477
|
i++;
|
4477
4478
|
p.la_peak_inc[block] = x[offset + i];
|
4478
|
-
// Compute the peak from the peak increase
|
4479
|
+
// Compute the peak from the peak increase.
|
4479
4480
|
p.b_peak[block] = p.b_peak[block - 1] + p.b_peak_inc[block];
|
4480
4481
|
}
|
4481
|
-
// Add warning to messages if slack has been used
|
4482
|
-
// NOTE:
|
4482
|
+
// Add warning to messages if slack has been used.
|
4483
|
+
// NOTE: Only check after the last round has been evaluated.
|
4483
4484
|
if(round === this.lastRound) {
|
4484
4485
|
let b = bb;
|
4485
4486
|
// Iterate over all time steps in this block
|
@@ -4533,25 +4534,27 @@ class VirtualMachine {
|
|
4533
4534
|
// optimization period, hence start by calculating the offset `bb`
|
4534
4535
|
// being the first time step of this block.
|
4535
4536
|
// Blocks are numbered 1, 2, ...
|
4536
|
-
const
|
4537
|
+
const
|
4538
|
+
bb = (block - 1) * MODEL.block_length + 1,
|
4539
|
+
cbl = this.actualBlockLength(block);
|
4537
4540
|
|
4538
4541
|
// FIRST: Calculate the actual flows on links.
|
4539
4542
|
let b, bt, p, pl, ld;
|
4540
4543
|
for(let l in MODEL.links) if(MODEL.links.hasOwnProperty(l) &&
|
4541
4544
|
!MODEL.ignored_entities[l]) {
|
4542
4545
|
l = MODEL.links[l];
|
4543
|
-
// NOTE:
|
4544
|
-
// of a P -> P data link by the FROM product node
|
4546
|
+
// NOTE: Flow is determined by the process node, or in case
|
4547
|
+
// of a P -> P data link by the FROM product node.
|
4545
4548
|
p = (l.to_node instanceof Process ? l.to_node : l.from_node);
|
4546
4549
|
b = bb;
|
4547
|
-
// Iterate over all time steps in this chunk
|
4548
|
-
for(let i = 0; i <
|
4549
|
-
// NOTE:
|
4550
|
+
// Iterate over all time steps in this chunk.
|
4551
|
+
for(let i = 0; i < cbl; i++) {
|
4552
|
+
// NOTE: Flows may have a delay!
|
4550
4553
|
ld = l.actualDelay(b);
|
4551
4554
|
bt = b - ld;
|
4552
|
-
// NOTE:
|
4555
|
+
// NOTE: Use non-zero level here to ignore non-zero values that
|
4553
4556
|
// are very small relative to the bounds on the process
|
4554
|
-
// (typically values below the non-zero tolerance of the solver)
|
4557
|
+
// (typically values below the non-zero tolerance of the solver).
|
4555
4558
|
pl = p.nonZeroLevel(bt);
|
4556
4559
|
if(l.multiplier === VM.LM_SPINNING_RESERVE) {
|
4557
4560
|
pl = (pl > VM.NEAR_ZERO ? p.upper_bound.result(bt) - pl : 0);
|
@@ -4560,30 +4563,56 @@ class VirtualMachine {
|
|
4560
4563
|
} else if(l.multiplier === VM.LM_ZERO) {
|
4561
4564
|
pl = (Math.abs(pl) < VM.NEAR_ZERO ? 1 : 0);
|
4562
4565
|
} else if(l.multiplier === VM.LM_STARTUP) {
|
4563
|
-
// NOTE:
|
4566
|
+
// NOTE: For start-up, first commit and shut-down, the level
|
4567
|
+
// can be ignored, as it suffices to check whether time step
|
4568
|
+
// `bt` occurs in the list of start-up time steps.
|
4564
4569
|
pl = (p.start_ups.indexOf(bt) < 0 ? 0 : 1);
|
4565
4570
|
} else if(l.multiplier === VM.LM_FIRST_COMMIT) {
|
4566
|
-
// NOTE:
|
4571
|
+
// NOTE: Here, check whether FIRST start-up occurred at `bt`.
|
4572
|
+
// This means that `bt` must be the *first* value in the list.
|
4567
4573
|
pl = (p.start_ups.indexOf(bt) === 0 ? 1 : 0);
|
4568
4574
|
} else if(l.multiplier === VM.LM_SHUTDOWN) {
|
4569
|
-
//
|
4575
|
+
// Similar to STARTUP, but now look in the shut-down list.
|
4570
4576
|
pl = (p.shut_downs.indexOf(bt) < 0 ? 0 : 1);
|
4571
4577
|
} else if(l.multiplier === VM.LM_INCREASE) {
|
4572
|
-
pl
|
4578
|
+
pl = VM.keepException(pl, pl - p.actualLevel(bt - 1));
|
4573
4579
|
} else if(l.multiplier === VM.LM_SUM || l.multiplier === VM.LM_MEAN) {
|
4574
|
-
|
4575
|
-
|
4580
|
+
// Level for `bt` counts as first value.
|
4581
|
+
let count = 1;
|
4582
|
+
// NOTE: Link delay may be < 0!
|
4583
|
+
if(ld < 0) {
|
4584
|
+
// NOTE: Actual levels beyond end of period are undefined,
|
4585
|
+
// and should be ignored while summing / averaging.
|
4586
|
+
if(bt >= p.level.length) pl = 0;
|
4587
|
+
// If so, take sum over t, t+1, ..., t+(d-1).
|
4588
|
+
for(let j = ld + 1; j <= 0; j++) {
|
4589
|
+
// Again: ignore levels beyond end of period.
|
4590
|
+
if(b - j < p.level.length) {
|
4591
|
+
pl = VM.keepException(pl, pl + p.actualLevel(b - j));
|
4592
|
+
count++;
|
4593
|
+
}
|
4594
|
+
}
|
4595
|
+
} else {
|
4596
|
+
// If d > 0, take sum over t, t-1, ..., t-(d-1).
|
4597
|
+
for(let j = 0; j < ld; j++) {
|
4598
|
+
// NOTE: Actual levels before t=0 are considered equal to
|
4599
|
+
// the initial level, and hence should NOT be ignored.
|
4600
|
+
pl = VM.keepException(pl, pl + p.actualLevel(b - j));
|
4601
|
+
count++;
|
4602
|
+
}
|
4576
4603
|
}
|
4577
|
-
if(l.multiplier === VM.LM_MEAN &&
|
4578
|
-
|
4604
|
+
if(l.multiplier === VM.LM_MEAN && count > 1) {
|
4605
|
+
// Average if more than 1 values have been summed.
|
4606
|
+
pl = VM.keepException(pl, pl / count);
|
4579
4607
|
}
|
4580
4608
|
} else if(l.multiplier === VM.LM_THROUGHPUT) {
|
4581
4609
|
// NOTE: calculate throughput on basis of levels and rates,
|
4582
4610
|
// as not all actual flows may have been computed yet
|
4583
4611
|
pl = 0;
|
4584
4612
|
for(let j = 0; j < p.inputs.length; j++) {
|
4585
|
-
pl
|
4586
|
-
|
4613
|
+
pl = VM.keepException(pl,
|
4614
|
+
pl + (p.inputs[j].from_node.actualLevel(bt) *
|
4615
|
+
p.inputs[j].relative_rate.result(bt)));
|
4587
4616
|
}
|
4588
4617
|
} else if(l.multiplier === VM.LM_PEAK_INC) {
|
4589
4618
|
// Actual flow over "peak increase" link is zero unless...
|
@@ -4591,27 +4620,32 @@ class VirtualMachine {
|
|
4591
4620
|
// first time step, then "block peak increase"...
|
4592
4621
|
pl = p.b_peak_inc[block];
|
4593
4622
|
} else if(i === MODEL.block_length) {
|
4594
|
-
// or first step of look-ahead, then "additional increase"
|
4623
|
+
// ... or first step of look-ahead, then "additional increase".
|
4595
4624
|
pl = p.la_peak_inc[block];
|
4596
4625
|
} else {
|
4597
4626
|
pl = 0;
|
4598
4627
|
}
|
4599
4628
|
}
|
4600
|
-
// Preserve special values such as INF, UNDEFINED and VM error codes
|
4629
|
+
// Preserve special values such as INF, UNDEFINED and VM error codes.
|
4601
4630
|
if(pl <= VM.MINUS_INFINITY || pl > VM.PLUS_INFINITY) {
|
4602
4631
|
l.actual_flow[b] = pl;
|
4603
4632
|
} else {
|
4604
|
-
const
|
4605
|
-
|
4633
|
+
const rr = l.relative_rate.result(bt);
|
4634
|
+
if(rr <= VM.MINUS_INFINITY || rr > VM.PLUS_INFINITY) {
|
4635
|
+
l.actual_flow[b] = rr;
|
4636
|
+
} else {
|
4637
|
+
const af = rr * pl;
|
4638
|
+
l.actual_flow[b] = (Math.abs(af) > VM.NEAR_ZERO ? af : 0);
|
4639
|
+
}
|
4606
4640
|
}
|
4607
4641
|
b++;
|
4608
4642
|
}
|
4609
4643
|
}
|
4610
4644
|
|
4611
|
-
// THEN:
|
4645
|
+
// THEN: Calculate cash flows one step at a time because of delays.
|
4612
4646
|
b = bb;
|
4613
|
-
for(let i = 0; i <
|
4614
|
-
// Initialize cumulative cash flows for clusters
|
4647
|
+
for(let i = 0; i < cbl; i++) {
|
4648
|
+
// Initialize cumulative cash flows for clusters.
|
4615
4649
|
for(let o in MODEL.clusters) if(MODEL.clusters.hasOwnProperty(o) &&
|
4616
4650
|
!MODEL.ignored_entities[o]) {
|
4617
4651
|
const c = MODEL.clusters[o];
|
@@ -4619,14 +4653,14 @@ class VirtualMachine {
|
|
4619
4653
|
c.cash_out[b] = 0;
|
4620
4654
|
c.cash_flow[b] = 0;
|
4621
4655
|
}
|
4622
|
-
// NOTE:
|
4656
|
+
// NOTE: Cash flows ONLY result from processes.
|
4623
4657
|
for(let o in MODEL.processes) if(MODEL.processes.hasOwnProperty(o) &&
|
4624
4658
|
!MODEL.ignored_entities[o]) {
|
4625
4659
|
const p = MODEL.processes[o];
|
4626
4660
|
let ci = 0, co = 0;
|
4627
4661
|
// INPUT links from priced products generate cash OUT...
|
4628
4662
|
for(let j = 0; j < p.inputs.length; j++) {
|
4629
|
-
// NOTE:
|
4663
|
+
// NOTE: Input links do NOT have a delay.
|
4630
4664
|
const l = p.inputs[j],
|
4631
4665
|
af = l.actual_flow[b],
|
4632
4666
|
fnp = l.from_node.price;
|
@@ -4634,7 +4668,7 @@ class VirtualMachine {
|
|
4634
4668
|
const pp = fnp.result(b);
|
4635
4669
|
if(pp > 0 && pp < VM.PLUS_INFINITY) {
|
4636
4670
|
co += pp * af;
|
4637
|
-
// ... unless the product price is negative; then cash IN
|
4671
|
+
// ... unless the product price is negative; then cash IN.
|
4638
4672
|
} else if(pp < 0 && pp > VM.MINUS_INFINITY) {
|
4639
4673
|
ci -= pp * af;
|
4640
4674
|
}
|
@@ -4642,27 +4676,27 @@ class VirtualMachine {
|
|
4642
4676
|
}
|
4643
4677
|
// OUTPUT links to priced products generate cash IN ...
|
4644
4678
|
for(let j = 0; j < p.outputs.length; j++) {
|
4645
|
-
// NOTE:
|
4679
|
+
// NOTE: Actual flows already consider delay!
|
4646
4680
|
const l = p.outputs[j],
|
4647
4681
|
ld = l.actualDelay(b),
|
4648
4682
|
af = l.actual_flow[b],
|
4649
4683
|
tnp = l.to_node.price;
|
4650
4684
|
if(af > VM.NEAR_ZERO && tnp.defined) {
|
4651
|
-
// NOTE:
|
4685
|
+
// NOTE: To get the correct price, again consider delays.
|
4652
4686
|
const pp = tnp.result(b - ld);
|
4653
4687
|
if(pp > 0 && pp < VM.PLUS_INFINITY) {
|
4654
4688
|
ci += pp * af;
|
4655
|
-
// ... unless the product price is negative; then cash OUT
|
4689
|
+
// ... unless the product price is negative; then cash OUT.
|
4656
4690
|
} else if(pp < 0 && pp > VM.MINUS_INFINITY) {
|
4657
4691
|
co -= pp * af;
|
4658
4692
|
}
|
4659
4693
|
}
|
4660
4694
|
}
|
4661
|
-
// Cash flows of process p are now known
|
4695
|
+
// Cash flows of process p are now known.
|
4662
4696
|
p.cash_in[b] = ci;
|
4663
4697
|
p.cash_out[b] = co;
|
4664
4698
|
p.cash_flow[b] = ci - co;
|
4665
|
-
// Also add these flows to all parent clusters of the process
|
4699
|
+
// Also add these flows to all parent clusters of the process.
|
4666
4700
|
let c = p.cluster;
|
4667
4701
|
while(c) {
|
4668
4702
|
c.cash_in[b] += ci;
|
@@ -4674,50 +4708,51 @@ class VirtualMachine {
|
|
4674
4708
|
b++;
|
4675
4709
|
}
|
4676
4710
|
|
4677
|
-
// THEN:
|
4678
|
-
// time because of delays, and also because expressions may refer
|
4679
|
-
// for earlier time steps
|
4711
|
+
// THEN: If cost prices should be inferred, calculate them one step
|
4712
|
+
// at a time because of delays, and also because expressions may refer
|
4713
|
+
// to values for earlier time steps.
|
4680
4714
|
if(MODEL.infer_cost_prices) {
|
4681
4715
|
b = bb;
|
4682
|
-
for(let i = 0; i <
|
4716
|
+
for(let i = 0; i < cbl; i++) {
|
4683
4717
|
if(!MODEL.calculateCostPrices(b)) {
|
4684
4718
|
this.logMessage(block, `${this.WARNING}(t=${b}) ` +
|
4685
4719
|
'Invalid cost prices due to negative flow(s)');
|
4686
4720
|
}
|
4687
|
-
//
|
4721
|
+
// Move on to the next time step of the block.
|
4688
4722
|
b++;
|
4689
4723
|
}
|
4690
4724
|
}
|
4691
4725
|
|
4692
|
-
// THEN:
|
4726
|
+
// THEN: Reset all datasets that are outcomes or serve as "formulas".
|
4693
4727
|
for(let o in MODEL.datasets) if(MODEL.datasets.hasOwnProperty(o)) {
|
4694
4728
|
const ds = MODEL.datasets[o];
|
4695
|
-
// NOTE:
|
4696
|
-
// "formulas", i.e., expressions to be calculated AFTER a model run
|
4697
|
-
|
4729
|
+
// NOTE: Assume that datasets having modifiers but no data serve as
|
4730
|
+
// "formulas", i.e., expressions to be calculated AFTER a model run.
|
4731
|
+
// This will automatically include the equations dataset.
|
4732
|
+
if(ds.outcome || ds.data.length === 0) {
|
4698
4733
|
for(let m in ds.modifiers) if(ds.modifiers.hasOwnProperty(m)) {
|
4699
4734
|
ds.modifiers[m].expression.reset();
|
4700
4735
|
}
|
4701
4736
|
}
|
4702
4737
|
}
|
4703
4738
|
|
4704
|
-
// THEN:
|
4739
|
+
// THEN: Reset the vectors of all chart variables.
|
4705
4740
|
for(let i = 0; i < MODEL.charts.length; i++) {
|
4706
4741
|
MODEL.charts[i].resetVectors();
|
4707
4742
|
}
|
4708
4743
|
|
4709
|
-
// Update the chart dialog if it is visible
|
4710
|
-
// NOTE:
|
4711
|
-
// interfere with storing the run results
|
4744
|
+
// Update the chart dialog if it is visible.
|
4745
|
+
// NOTE: Do NOT do this while an experiment is running, as this may
|
4746
|
+
// interfere with storing the run results.
|
4712
4747
|
if(!MODEL.running_experiment) {
|
4713
4748
|
if(CHART_MANAGER.visible) CHART_MANAGER.updateDialog();
|
4714
4749
|
}
|
4715
4750
|
|
4716
|
-
// NOTE:
|
4751
|
+
// NOTE: Add a blank line to separate from next round (if any).
|
4717
4752
|
this.logMessage(block,
|
4718
4753
|
`Calculating dependent variables took ${this.elapsedTime} seconds.\n`);
|
4719
4754
|
|
4720
|
-
// FINALLY:
|
4755
|
+
// FINALLY: Reset the vectors of all note colors.
|
4721
4756
|
for(let o in MODEL.clusters) if(MODEL.clusters.hasOwnProperty(o)) {
|
4722
4757
|
const c = MODEL.clusters[o];
|
4723
4758
|
for(let i = 0; i < c.notes.length; i++) {
|
@@ -4871,7 +4906,8 @@ class VirtualMachine {
|
|
4871
4906
|
this.stopSolving();
|
4872
4907
|
return;
|
4873
4908
|
}
|
4874
|
-
// NOTE:
|
4909
|
+
// NOTE: Save an additional call when less than 20% of a segment would
|
4910
|
+
// remain.
|
4875
4911
|
var l;
|
4876
4912
|
const next_start = (start + this.tsl * 1.2 < abl ? start + this.tsl : abl);
|
4877
4913
|
for(let i = start; i < next_start; i++) {
|
@@ -4879,16 +4915,16 @@ class VirtualMachine {
|
|
4879
4915
|
l = this.code.length;
|
4880
4916
|
for(let j = 0; j < l; j++) {
|
4881
4917
|
this.IP = j;
|
4882
|
-
// Execute the instruction, which has form [function, argument list]
|
4918
|
+
// Execute the instruction, which has form [function, argument list].
|
4883
4919
|
const instr = this.code[j];
|
4884
4920
|
instr[0](instr[1]);
|
4885
|
-
// Trace the result when debugging
|
4921
|
+
// Trace the result when debugging.
|
4886
4922
|
this.logTrace([(' ' + j).slice(-5), ': coeff = ',
|
4887
4923
|
JSON.stringify(this.coefficients), '; rhs = ', this.rhs].join(''));
|
4888
4924
|
}
|
4889
4925
|
this.logTrace('STOP executing block code');
|
4890
|
-
// Add constraints for paced process variables
|
4891
|
-
// NOTE:
|
4926
|
+
// Add constraints for paced process variables.
|
4927
|
+
// NOTE: This is effectuated by *executing* VM instructions.
|
4892
4928
|
for(let j in this.paced_var_indices) if(Number(j)) {
|
4893
4929
|
const
|
4894
4930
|
// p is the pace (number of time steps)
|
@@ -5011,13 +5047,13 @@ class VirtualMachine {
|
|
5011
5047
|
// the simulation period.
|
5012
5048
|
const
|
5013
5049
|
abl = this.actualBlockLength(this.block_count),
|
5014
|
-
// Get the number digits for variable names
|
5050
|
+
// Get the number digits for variable names.
|
5015
5051
|
z = this.columnsInBlock.toString().length,
|
5016
|
-
// LP_solve uses semicolon as separator between equations
|
5052
|
+
// LP_solve uses semicolon as separator between equations.
|
5017
5053
|
EOL = (cplex ? '\n' : ';\n'),
|
5018
5054
|
// Local function that returns variable symbol (e.g. X001) with
|
5019
5055
|
// its coefficient if specified (e.g., -0.123 X001) in the
|
5020
|
-
// most compact notation
|
5056
|
+
// most compact notation.
|
5021
5057
|
vbl = (index, c=false) => {
|
5022
5058
|
const v = 'X' + index.toString().padStart(z, '0');
|
5023
5059
|
if(c === false) return v; // Only the symbol
|
@@ -5025,11 +5061,11 @@ class VirtualMachine {
|
|
5025
5061
|
if(c < 0) return ` ${c} ${v}`; // Number had minus sign
|
5026
5062
|
if(c === 1) return ` +${v}`; // No coefficient needed
|
5027
5063
|
return ` +${c} ${v}`; // Prefix coefficient with +
|
5028
|
-
// NOTE:
|
5064
|
+
// NOTE: This may return +0 X001.
|
5029
5065
|
};
|
5030
5066
|
|
5031
5067
|
this.numeric_issue = '';
|
5032
|
-
// First add the objective (always MAXimize)
|
5068
|
+
// First add the objective (always MAXimize).
|
5033
5069
|
if(cplex) {
|
5034
5070
|
this.lines = 'Maximize\n';
|
5035
5071
|
} else {
|
@@ -5038,19 +5074,19 @@ class VirtualMachine {
|
|
5038
5074
|
let c,
|
5039
5075
|
p,
|
5040
5076
|
line = '';
|
5041
|
-
// NOTE:
|
5077
|
+
// NOTE: Iterate over ALL columns to maintain variable order.
|
5042
5078
|
let n = abl * this.cols + this.chunk_variables.length;
|
5043
5079
|
for(p = 1; p <= n; p++) {
|
5044
5080
|
if(this.objective.hasOwnProperty(p)) {
|
5045
5081
|
c = this.objective[p];
|
5046
|
-
// Check for numeric issues
|
5082
|
+
// Check for numeric issues.
|
5047
5083
|
if (c < VM.MINUS_INFINITY || c > VM.PLUS_INFINITY) {
|
5048
5084
|
this.setNumericIssue(c, p, 'objective function coefficient');
|
5049
5085
|
break;
|
5050
5086
|
}
|
5051
5087
|
line += vbl(p, c);
|
5052
5088
|
}
|
5053
|
-
// Keep lines under approx. 110 chars
|
5089
|
+
// Keep lines under approx. 110 chars.
|
5054
5090
|
if(line.length >= 100) {
|
5055
5091
|
this.lines += line + '\n';
|
5056
5092
|
line = '';
|
@@ -5058,7 +5094,7 @@ class VirtualMachine {
|
|
5058
5094
|
}
|
5059
5095
|
this.lines += line + EOL;
|
5060
5096
|
line = '';
|
5061
|
-
// Add the row constraints
|
5097
|
+
// Add the row constraints.
|
5062
5098
|
if(cplex) {
|
5063
5099
|
this.lines += '\nSubject To\n';
|
5064
5100
|
} else {
|
@@ -5074,7 +5110,7 @@ class VirtualMachine {
|
|
5074
5110
|
break;
|
5075
5111
|
}
|
5076
5112
|
line += vbl(p, c);
|
5077
|
-
// Keep lines under approx. 110 chars
|
5113
|
+
// Keep lines under approx. 110 chars.
|
5078
5114
|
if(line.length >= 100) {
|
5079
5115
|
this.lines += line + '\n';
|
5080
5116
|
line = '';
|
@@ -5085,7 +5121,7 @@ class VirtualMachine {
|
|
5085
5121
|
this.constraint_symbols[this.constraint_types[r]] + ' ' + c + EOL;
|
5086
5122
|
line = '';
|
5087
5123
|
}
|
5088
|
-
// Add the variable bounds
|
5124
|
+
// Add the variable bounds.
|
5089
5125
|
if(cplex) {
|
5090
5126
|
this.lines += '\nBounds\n';
|
5091
5127
|
} else {
|
@@ -5097,7 +5133,7 @@ class VirtualMachine {
|
|
5097
5133
|
ub = null;
|
5098
5134
|
if(this.lower_bounds.hasOwnProperty(p)) {
|
5099
5135
|
lb = this.lower_bounds[p];
|
5100
|
-
// NOTE:
|
5136
|
+
// NOTE: For bounds, use the SOLVER values for +/- Infinity.
|
5101
5137
|
if (lb < VM.SOLVER_MINUS_INFINITY || lb > VM.SOLVER_PLUS_INFINITY) {
|
5102
5138
|
this.setNumericIssue(lb, p, 'lower bound');
|
5103
5139
|
break;
|
@@ -5114,56 +5150,71 @@ class VirtualMachine {
|
|
5114
5150
|
if(lb === ub) {
|
5115
5151
|
if(lb !== null) line = ` ${vbl(p)} = ${lb}`;
|
5116
5152
|
} else {
|
5117
|
-
// NOTE:
|
5153
|
+
// NOTE: By default, lower bound of variables is 0.
|
5118
5154
|
line = ` ${vbl(p)}`;
|
5119
5155
|
if(cplex) {
|
5120
|
-
// Explicitly denote free variables
|
5156
|
+
// Explicitly denote free variables.
|
5121
5157
|
if(lb === null && ub === null && !this.is_binary[p]) {
|
5122
5158
|
line += ' free';
|
5123
5159
|
} else {
|
5124
|
-
// Separate lines for LB and UB if specified
|
5160
|
+
// Separate lines for LB and UB if specified.
|
5125
5161
|
if(ub !== null) line += ' <= ' + ub;
|
5126
5162
|
if(lb !== null && lb !== 0) line += `\n ${vbl(p)} >= ${lb}`;
|
5127
5163
|
}
|
5128
5164
|
} else {
|
5129
|
-
// Bounds can be specified on a single line: lb <= X001 <= ub
|
5165
|
+
// Bounds can be specified on a single line: lb <= X001 <= ub.
|
5130
5166
|
if(lb !== null && lb !== 0) line = lb + ' <= ' + line;
|
5131
5167
|
if(ub !== null) line += ' <= ' + ub;
|
5132
5168
|
}
|
5133
5169
|
}
|
5134
5170
|
if(line) this.lines += line + EOL;
|
5135
5171
|
}
|
5136
|
-
// Add the special variable types
|
5172
|
+
// Add the special variable types.
|
5137
5173
|
if(cplex) {
|
5138
5174
|
line = '';
|
5139
|
-
let scv = 0
|
5175
|
+
let scv = 0,
|
5176
|
+
vcnt = 0;
|
5140
5177
|
for(let i in this.is_binary) if(Number(i)) {
|
5141
5178
|
line += ' ' + vbl(i);
|
5142
5179
|
scv++;
|
5143
|
-
|
5144
|
-
|
5180
|
+
vcnt++;
|
5181
|
+
// Max. 10 variables per line.
|
5182
|
+
if(vcnt >= 10) {
|
5183
|
+
line += '\n';
|
5184
|
+
vcnt = 0;
|
5185
|
+
}
|
5145
5186
|
}
|
5146
5187
|
if(scv) {
|
5147
5188
|
this.lines += `Binary\n${line}\n`;
|
5148
5189
|
line = '';
|
5149
5190
|
scv = 0;
|
5191
|
+
vcnt = 0;
|
5150
5192
|
}
|
5151
5193
|
for(let i in this.is_integer) if(Number(i)) {
|
5152
5194
|
line += ' ' + vbl(i);
|
5153
5195
|
scv++;
|
5154
|
-
|
5155
|
-
|
5196
|
+
vcnt++;
|
5197
|
+
// Max. 10 variables per line.
|
5198
|
+
if(vcnt >= 10) {
|
5199
|
+
line += '\n';
|
5200
|
+
vcnt = 0;
|
5201
|
+
}
|
5156
5202
|
}
|
5157
5203
|
if(scv) {
|
5158
5204
|
this.lines += `General\n${line}\n`;
|
5159
5205
|
line = '';
|
5160
5206
|
scv = 0;
|
5207
|
+
vcnt = 0;
|
5161
5208
|
}
|
5162
5209
|
for(let i in this.is_semi_continuous) if(Number(i)) {
|
5163
5210
|
line += ' '+ vbl(i);
|
5164
5211
|
scv++;
|
5165
|
-
|
5166
|
-
|
5212
|
+
vcnt++;
|
5213
|
+
// Max. 10 variables per line.
|
5214
|
+
if(vcnt >= 10) {
|
5215
|
+
line += '\n';
|
5216
|
+
vcnt = 0;
|
5217
|
+
}
|
5167
5218
|
}
|
5168
5219
|
if(scv) {
|
5169
5220
|
this.lines += `Semi-continuous\n${line}\n`;
|
@@ -5191,7 +5242,7 @@ class VirtualMachine {
|
|
5191
5242
|
this.lines += 'End';
|
5192
5243
|
} else {
|
5193
5244
|
// NOTE: LP_solve does not differentiate between binary and integer,
|
5194
|
-
// so for binary variables, the constraint <= 1 must be added
|
5245
|
+
// so for binary variables, the constraint <= 1 must be added.
|
5195
5246
|
const v_set = [];
|
5196
5247
|
for(let i in this.is_binary) if(Number(i)) {
|
5197
5248
|
const v = vbl(i);
|
@@ -5200,12 +5251,12 @@ class VirtualMachine {
|
|
5200
5251
|
}
|
5201
5252
|
for(let i in this.is_integer) if(Number(i)) v_set.push(vbl(i));
|
5202
5253
|
if(v_set.length > 0) this.lines += 'int ' + v_set.join(', ') + ';\n';
|
5203
|
-
// Clear the INT variable list
|
5254
|
+
// Clear the INT variable list.
|
5204
5255
|
v_set.length = 0;
|
5205
|
-
// Add the semi-continuous variables
|
5256
|
+
// Add the semi-continuous variables.
|
5206
5257
|
for(let i in this.is_semi_continuous) if(Number(i)) v_set.push(vbl(i));
|
5207
5258
|
if(v_set.length > 0) this.lines += 'sec ' + v_set.join(', ') + ';\n';
|
5208
|
-
// Add the SOS section
|
5259
|
+
// Add the SOS section.
|
5209
5260
|
if(this.sos_var_indices.length > 0) {
|
5210
5261
|
this.lines += 'sos\n';
|
5211
5262
|
let sos = 1;
|
@@ -5258,7 +5309,7 @@ class VirtualMachine {
|
|
5258
5309
|
for(c = 1; c <= ncol; c++) cols.push([]);
|
5259
5310
|
this.decimals = Math.max(nrow, ncol).toString().length;
|
5260
5311
|
this.lines += 'NAME block-' + this.blockWithRound + '\nROWS\n';
|
5261
|
-
// Start with the "free" row that will be the objective function
|
5312
|
+
// Start with the "free" row that will be the objective function.
|
5262
5313
|
this.lines += ' N OBJ\n';
|
5263
5314
|
for(r = 0; r < nrow; r++) {
|
5264
5315
|
const
|
@@ -5268,7 +5319,7 @@ class VirtualMachine {
|
|
5268
5319
|
' ' + row_lbl + '\n';
|
5269
5320
|
for(p in row) if (row.hasOwnProperty(p)) {
|
5270
5321
|
c = row[p];
|
5271
|
-
// Check for numeric issues
|
5322
|
+
// Check for numeric issues.
|
5272
5323
|
if(c === undefined || c < VM.SOLVER_MINUS_INFINITY ||
|
5273
5324
|
c > VM.SOLVER_PLUS_INFINITY) {
|
5274
5325
|
this.setNumericIssue(c, p, 'constraint');
|
@@ -5287,47 +5338,47 @@ class VirtualMachine {
|
|
5287
5338
|
rhs.push(' B ' + row_lbl + ' ' + c);
|
5288
5339
|
}
|
5289
5340
|
}
|
5290
|
-
// The objective function is a row like those for the constraints
|
5341
|
+
// The objective function is a row like those for the constraints.
|
5291
5342
|
for(p in this.objective) if(this.objective.hasOwnProperty(p)) {
|
5292
5343
|
c = this.objective[p];
|
5293
5344
|
if(c === null || c < VM.MINUS_INFINITY || c > VM.PLUS_INFINITY) {
|
5294
5345
|
this.setNumericIssue(c, p, 'objective function coefficient');
|
5295
5346
|
break;
|
5296
5347
|
}
|
5297
|
-
// NOTE: MPS assumes MINimization, hence negate all coefficients
|
5298
|
-
// NOTE: JavaScript differentiates between 0 and -0, so add 0 to
|
5299
|
-
// creating the special numeric value -0
|
5348
|
+
// NOTE: MPS assumes MINimization, hence negate all coefficients.
|
5349
|
+
// NOTE: JavaScript differentiates between 0 and -0, so add 0 to
|
5350
|
+
// prevent creating the special numeric value -0.
|
5300
5351
|
cols[p].push('OBJ ' + (-c + 0));
|
5301
5352
|
}
|
5302
|
-
// Abort if any invalid coefficient was detected
|
5353
|
+
// Abort if any invalid coefficient was detected.
|
5303
5354
|
if(this.numeric_issue) {
|
5304
5355
|
this.hideSetUpOrWriteProgress();
|
5305
5356
|
this.stopSolving();
|
5306
5357
|
return;
|
5307
5358
|
}
|
5308
|
-
// Add the columns section
|
5359
|
+
// Add the columns section.
|
5309
5360
|
this.lines += 'COLUMNS\n';
|
5310
5361
|
for(c = 1; c <= ncol; c++) {
|
5311
5362
|
const col_lbl = ' X' + c.toString().padStart(this.decimals, '0') + ' ';
|
5312
|
-
// NOTE:
|
5363
|
+
// NOTE: If processes have no in- or outgoing links their decision
|
5313
5364
|
// variable does not occur in any constraint, and this may cause
|
5314
5365
|
// problems for solvers that cannot handle columns having a blank
|
5315
5366
|
// row name (e.g., CPLEX). To prevent errors, these columns are
|
5316
|
-
// given coefficient 0 in the OBJ row
|
5367
|
+
// given coefficient 0 in the OBJ row.
|
5317
5368
|
if(cols[c].length) {
|
5318
5369
|
this.lines += col_lbl + cols[c].join('\n' + col_lbl) + '\n';
|
5319
5370
|
} else {
|
5320
5371
|
this.lines += col_lbl + ' OBJ 0\n';
|
5321
5372
|
}
|
5322
5373
|
}
|
5323
|
-
// Free up memory
|
5374
|
+
// Free up memory.
|
5324
5375
|
cols.length = 0;
|
5325
|
-
// Add the RHS section
|
5376
|
+
// Add the RHS section.
|
5326
5377
|
this.lines += 'RHS\n' + rhs.join('\n') + '\n';
|
5327
5378
|
rhs.length = 0;
|
5328
|
-
// Add the BOUNDS section
|
5379
|
+
// Add the BOUNDS section.
|
5329
5380
|
this.lines += 'BOUNDS\n';
|
5330
|
-
// NOTE:
|
5381
|
+
// NOTE: Start at column number 1, not 0.
|
5331
5382
|
setTimeout((c, n) => VM.showMPSProgress(c, n), 0, 1, ncol);
|
5332
5383
|
}
|
5333
5384
|
|
@@ -5338,7 +5389,7 @@ class VirtualMachine {
|
|
5338
5389
|
return;
|
5339
5390
|
}
|
5340
5391
|
if(this.show_progress) {
|
5341
|
-
// NOTE:
|
5392
|
+
// NOTE: Display 1 block more progress, or the bar never reaches 100%.
|
5342
5393
|
UI.setProgressNeedle((next_col + this.cbl) / ncol);
|
5343
5394
|
}
|
5344
5395
|
setTimeout((c, n) => VM.writeMPSColumns(c, n), 0, next_col, ncol);
|
@@ -5356,7 +5407,7 @@ class VirtualMachine {
|
|
5356
5407
|
ub = null;
|
5357
5408
|
if(this.lower_bounds.hasOwnProperty(p)) {
|
5358
5409
|
lb = this.lower_bounds[p];
|
5359
|
-
// NOTE:
|
5410
|
+
// NOTE: For bounds, use the SOLVER values for +/- Infinity.
|
5360
5411
|
if(lb < VM.SOLVER_MINUS_INFINITY || lb > VM.PLUS_INFINITY) {
|
5361
5412
|
this.setNumericIssue(lb, p, 'lower bound');
|
5362
5413
|
break;
|
@@ -5392,7 +5443,7 @@ class VirtualMachine {
|
|
5392
5443
|
} else if(lb !== null && lb === ub && !semic) {
|
5393
5444
|
this.lines += ' FX' + bnd + lb + '\n';
|
5394
5445
|
} else {
|
5395
|
-
// Assume "standard" bounds
|
5446
|
+
// Assume "standard" bounds.
|
5396
5447
|
lbc = ' LO';
|
5397
5448
|
ubc = ' UP';
|
5398
5449
|
if(p in this.is_integer) {
|
@@ -5403,7 +5454,7 @@ class VirtualMachine {
|
|
5403
5454
|
} else if(semic) {
|
5404
5455
|
ubc = ' SC';
|
5405
5456
|
}
|
5406
|
-
// NOTE: by default, lower bound of variables is 0
|
5457
|
+
// NOTE: by default, lower bound of variables is 0.
|
5407
5458
|
if(lb !== null && lb !== 0 || lbc !== ' LO') {
|
5408
5459
|
this.lines += lbc + bnd + lb + '\n';
|
5409
5460
|
}
|
@@ -5412,7 +5463,7 @@ class VirtualMachine {
|
|
5412
5463
|
}
|
5413
5464
|
}
|
5414
5465
|
}
|
5415
|
-
// Abort if any invalid coefficient was detected
|
5466
|
+
// Abort if any invalid coefficient was detected.
|
5416
5467
|
if(this.numeric_issue) this.submitFile();
|
5417
5468
|
if(next_col <= ncol) {
|
5418
5469
|
setTimeout((c, n) => VM.showMPSProgress(c, n), 0, next_col, ncol);
|
@@ -5631,7 +5682,6 @@ Solver status = ${json.status}`);
|
|
5631
5682
|
}
|
5632
5683
|
// Generate lines of code in format that should be accepted by solver.
|
5633
5684
|
if(this.solver_name === 'gurobi') {
|
5634
|
-
//this.writeMPSFormat();
|
5635
5685
|
this.writeLpFormat(true);
|
5636
5686
|
} else if(this.solver_name === 'scip' || this.solver_name === 'cplex') {
|
5637
5687
|
// NOTE: The CPLEX LP format that is also used by SCIP differs from
|
@@ -5697,8 +5747,8 @@ Solver status = ${json.status}`);
|
|
5697
5747
|
}
|
5698
5748
|
|
5699
5749
|
solveModel() {
|
5700
|
-
//
|
5701
|
-
// consecutive blocks, and finally calculating dependent variables
|
5750
|
+
// Start the sequence of data loading, model translation, solving
|
5751
|
+
// consecutive blocks, and finally calculating dependent variables.
|
5702
5752
|
const n = MODEL.loading_datasets.length;
|
5703
5753
|
if(n > 0) {
|
5704
5754
|
// Still within reasonable time? (3 seconds per dataset)
|
@@ -5711,7 +5761,7 @@ Solver status = ${json.status}`);
|
|
5711
5761
|
setTimeout(() => VM.solveModel(), 500);
|
5712
5762
|
return;
|
5713
5763
|
} else {
|
5714
|
-
// Wait no longer, but warn user that data may be incomplete
|
5764
|
+
// Wait no longer, but warn user that data may be incomplete.
|
5715
5765
|
const dsl = [];
|
5716
5766
|
for(let i = 0; i < MODEL.loading_datasets.length; i++) {
|
5717
5767
|
dsl.push(MODEL.loading_datasets[i].displayName);
|
@@ -5729,7 +5779,7 @@ Solver status = ${json.status}`);
|
|
5729
5779
|
}
|
5730
5780
|
|
5731
5781
|
halt() {
|
5732
|
-
//
|
5782
|
+
// Abort solving process. This prevents submitting the next block.
|
5733
5783
|
UI.waitToStop();
|
5734
5784
|
this.halted = true;
|
5735
5785
|
}
|
@@ -6701,20 +6751,21 @@ function VMI_push_statistic(x, args) {
|
|
6701
6751
|
}
|
6702
6752
|
|
6703
6753
|
function VMI_replace_undefined(x) {
|
6704
|
-
//
|
6705
|
-
// is undefined
|
6706
|
-
|
6754
|
+
// Replace one of the two top numbers on the stack by the other if the
|
6755
|
+
// one is undefined.
|
6756
|
+
// NOTE: pop(TRUE) denotes that "undefined" should be ignored as issue.
|
6757
|
+
const d = x.pop(true);
|
6707
6758
|
if(d !== false) {
|
6708
6759
|
if(DEBUGGING) console.log('REPLACE UNDEFINED (' + d.join(', ') + ')');
|
6709
6760
|
x.retop(d[0] === VM.UNDEFINED ? d[1] : d[0]);
|
6710
6761
|
}
|
6711
6762
|
}
|
6712
6763
|
|
6713
|
-
// NOTE:
|
6714
|
-
// is interpreted as TRUE
|
6764
|
+
// NOTE: When the VM computes logical OR, AND and NOT, any non-zero number
|
6765
|
+
// is interpreted as TRUE.
|
6715
6766
|
|
6716
6767
|
function VMI_or(x) {
|
6717
|
-
//
|
6768
|
+
// Perform a logical OR on the two top numbers on the stack.
|
6718
6769
|
const d = x.pop();
|
6719
6770
|
if(d !== false) {
|
6720
6771
|
if(DEBUGGING) console.log('OR (' + d.join(', ') + ')');
|
@@ -6723,7 +6774,7 @@ function VMI_or(x) {
|
|
6723
6774
|
}
|
6724
6775
|
|
6725
6776
|
function VMI_and(x) {
|
6726
|
-
//
|
6777
|
+
// Perform a logical AND on the two top numbers on the stack.
|
6727
6778
|
const d = x.pop();
|
6728
6779
|
if(d !== false) {
|
6729
6780
|
if(DEBUGGING) console.log('AND (' + d.join(', ') + ')');
|
@@ -6732,7 +6783,7 @@ function VMI_and(x) {
|
|
6732
6783
|
}
|
6733
6784
|
|
6734
6785
|
function VMI_not(x) {
|
6735
|
-
//
|
6786
|
+
// Perform a logical NOT on the top number of the stack.
|
6736
6787
|
const d = x.top();
|
6737
6788
|
if(d !== false) {
|
6738
6789
|
if(DEBUGGING) console.log('NOT ' + d);
|
@@ -6741,7 +6792,7 @@ function VMI_not(x) {
|
|
6741
6792
|
}
|
6742
6793
|
|
6743
6794
|
function VMI_abs(x) {
|
6744
|
-
//
|
6795
|
+
// Replace the top number of the stack by its absolute value.
|
6745
6796
|
const d = x.top();
|
6746
6797
|
if(d !== false) {
|
6747
6798
|
if(DEBUGGING) console.log('ABS ' + d);
|
@@ -6750,7 +6801,7 @@ function VMI_abs(x) {
|
|
6750
6801
|
}
|
6751
6802
|
|
6752
6803
|
function VMI_eq(x) {
|
6753
|
-
//
|
6804
|
+
// Test equality of the two top numbers on the stack.
|
6754
6805
|
const d = x.pop();
|
6755
6806
|
if(d !== false) {
|
6756
6807
|
if(DEBUGGING) console.log('EQ (' + d.join(', ') + ')');
|
@@ -6759,7 +6810,7 @@ function VMI_eq(x) {
|
|
6759
6810
|
}
|
6760
6811
|
|
6761
6812
|
function VMI_ne(x) {
|
6762
|
-
//
|
6813
|
+
// Test inequality of the two top numbers on the stack.
|
6763
6814
|
const d = x.pop();
|
6764
6815
|
if(d !== false) {
|
6765
6816
|
if(DEBUGGING) console.log('NE (' + d.join(', ') + ')');
|
@@ -6768,7 +6819,7 @@ function VMI_ne(x) {
|
|
6768
6819
|
}
|
6769
6820
|
|
6770
6821
|
function VMI_lt(x) {
|
6771
|
-
//
|
6822
|
+
// Test whether second number on the stack is less than the top number.
|
6772
6823
|
const d = x.pop();
|
6773
6824
|
if(d !== false) {
|
6774
6825
|
if(DEBUGGING) console.log('LT (' + d.join(', ') + ')');
|
@@ -6777,7 +6828,7 @@ function VMI_lt(x) {
|
|
6777
6828
|
}
|
6778
6829
|
|
6779
6830
|
function VMI_gt(x) {
|
6780
|
-
//
|
6831
|
+
// Test whether second number on the stack is greater than the top number.
|
6781
6832
|
const d = x.pop();
|
6782
6833
|
if(d !== false) {
|
6783
6834
|
if(DEBUGGING) console.log('GT (' + d.join(', ') + ')');
|
@@ -6786,8 +6837,8 @@ function VMI_gt(x) {
|
|
6786
6837
|
}
|
6787
6838
|
|
6788
6839
|
function VMI_le(x) {
|
6789
|
-
//
|
6790
|
-
// the top number
|
6840
|
+
// Test whether second number on the stack is less than, or equal to,
|
6841
|
+
// the top number.
|
6791
6842
|
const d = x.pop();
|
6792
6843
|
if(d !== false) {
|
6793
6844
|
if(DEBUGGING) console.log('LE (' + d.join(', ') + ')');
|
@@ -6796,8 +6847,8 @@ function VMI_le(x) {
|
|
6796
6847
|
}
|
6797
6848
|
|
6798
6849
|
function VMI_ge(x) {
|
6799
|
-
//
|
6800
|
-
// the top number
|
6850
|
+
// Test whether second number on the stack is greater than, or equal to,
|
6851
|
+
// the top number.
|
6801
6852
|
const d = x.pop();
|
6802
6853
|
if(d !== false) {
|
6803
6854
|
if(DEBUGGING) console.log('LE (' + d.join(', ') + ')');
|
@@ -6806,7 +6857,7 @@ function VMI_ge(x) {
|
|
6806
6857
|
}
|
6807
6858
|
|
6808
6859
|
function VMI_add(x) {
|
6809
|
-
//
|
6860
|
+
// Pop the top number on the stack, and add it to the new top number.
|
6810
6861
|
const d = x.pop();
|
6811
6862
|
if(d !== false) {
|
6812
6863
|
if(DEBUGGING) console.log('ADD (' + d.join(', ') + ')');
|
@@ -6815,8 +6866,8 @@ function VMI_add(x) {
|
|
6815
6866
|
}
|
6816
6867
|
|
6817
6868
|
function VMI_sub(x) {
|
6818
|
-
//
|
6819
|
-
// top number
|
6869
|
+
// Pop the top number on the stack, and subtract it from the new
|
6870
|
+
// top number.
|
6820
6871
|
const d = x.pop();
|
6821
6872
|
if(d !== false) {
|
6822
6873
|
if(DEBUGGING) console.log('SUB (' + d.join(', ') + ')');
|
@@ -6825,7 +6876,7 @@ function VMI_sub(x) {
|
|
6825
6876
|
}
|
6826
6877
|
|
6827
6878
|
function VMI_mul(x) {
|
6828
|
-
//
|
6879
|
+
// Pop the top number on the stack, and multiply it with the new
|
6829
6880
|
// top number
|
6830
6881
|
const d = x.pop();
|
6831
6882
|
if(d !== false) {
|
@@ -6835,8 +6886,8 @@ function VMI_mul(x) {
|
|
6835
6886
|
}
|
6836
6887
|
|
6837
6888
|
function VMI_div(x) {
|
6838
|
-
//
|
6839
|
-
// by it. In case of division by zero, the top
|
6889
|
+
// Pop the top number on the stack, and divide the new top number
|
6890
|
+
// by it. In case of division by zero, replace the top by #DIV/0!
|
6840
6891
|
const d = x.pop();
|
6841
6892
|
if(d !== false) {
|
6842
6893
|
if(DEBUGGING) console.log('DIV (' + d.join(', ') + ')');
|
@@ -6849,23 +6900,23 @@ function VMI_div(x) {
|
|
6849
6900
|
}
|
6850
6901
|
|
6851
6902
|
function VMI_mod(x) {
|
6852
|
-
//
|
6853
|
-
//
|
6854
|
-
//
|
6855
|
-
//
|
6903
|
+
// Perform a "floating point MOD operation" as explained below.
|
6904
|
+
// Pop the top number on the stack. If zero, push error code #DIV/0!.
|
6905
|
+
// Otherwise, proceed: divide the new top number by the divisor, take
|
6906
|
+
// the fraction part, and multiply this with the divisor.
|
6856
6907
|
const d = x.pop();
|
6857
6908
|
if(d !== false) {
|
6858
6909
|
if(DEBUGGING) console.log('DIV (' + d.join(', ') + ')');
|
6859
6910
|
if(Math.abs(d[1]) <= VM.NEAR_ZERO) {
|
6860
6911
|
x.retop(VM.DIV_ZERO);
|
6861
6912
|
} else {
|
6862
|
-
x.retop(d[0] % d[1]); // % is the modulo operator in JavaScript
|
6913
|
+
x.retop(d[0] % d[1]); // % is the modulo operator in JavaScript.
|
6863
6914
|
}
|
6864
6915
|
}
|
6865
6916
|
}
|
6866
6917
|
|
6867
6918
|
function VMI_negate(x) {
|
6868
|
-
//
|
6919
|
+
// Perform a negation on the top number of the stack.
|
6869
6920
|
const d = x.top();
|
6870
6921
|
if(d !== false) {
|
6871
6922
|
if(DEBUGGING) console.log('NEG ' + d);
|
@@ -6874,8 +6925,8 @@ function VMI_negate(x) {
|
|
6874
6925
|
}
|
6875
6926
|
|
6876
6927
|
function VMI_power(x) {
|
6877
|
-
//
|
6878
|
-
// to its power
|
6928
|
+
// Pop the top number on the stack, and raise the new top number
|
6929
|
+
// to its power.
|
6879
6930
|
const d = x.pop();
|
6880
6931
|
if(d !== false) {
|
6881
6932
|
if(DEBUGGING) console.log('POWER (' + d.join(', ') + ')');
|
@@ -6884,8 +6935,8 @@ function VMI_power(x) {
|
|
6884
6935
|
}
|
6885
6936
|
|
6886
6937
|
function VMI_sqrt(x) {
|
6887
|
-
//
|
6888
|
-
// error code #VALUE! if the top number is negative
|
6938
|
+
// Replace the top number of the stack by its square root, or by
|
6939
|
+
// error code #VALUE! if the top number is negative.
|
6889
6940
|
const d = x.top();
|
6890
6941
|
if(d !== false) {
|
6891
6942
|
if(DEBUGGING) console.log('SQRT ' + d);
|
@@ -6898,7 +6949,7 @@ function VMI_sqrt(x) {
|
|
6898
6949
|
}
|
6899
6950
|
|
6900
6951
|
function VMI_sin(x) {
|
6901
|
-
//
|
6952
|
+
// Replace the top number X of the stack by sin(X).
|
6902
6953
|
const d = x.top();
|
6903
6954
|
if(d !== false) {
|
6904
6955
|
if(DEBUGGING) console.log('SIN ' + d);
|
@@ -6907,7 +6958,7 @@ function VMI_sin(x) {
|
|
6907
6958
|
}
|
6908
6959
|
|
6909
6960
|
function VMI_cos(x) {
|
6910
|
-
//
|
6961
|
+
// Replace the top number X of the stack by cos(X).
|
6911
6962
|
const d = x.top();
|
6912
6963
|
if(d !== false) {
|
6913
6964
|
if(DEBUGGING) console.log('COS ' + d);
|
@@ -6916,7 +6967,7 @@ function VMI_cos(x) {
|
|
6916
6967
|
}
|
6917
6968
|
|
6918
6969
|
function VMI_atan(x) {
|
6919
|
-
//
|
6970
|
+
// Replace the top number X of the stack by atan(X).
|
6920
6971
|
const d = x.top();
|
6921
6972
|
if(d !== false) {
|
6922
6973
|
if(DEBUGGING) console.log('ATAN ' + d);
|
@@ -6925,8 +6976,8 @@ function VMI_atan(x) {
|
|
6925
6976
|
}
|
6926
6977
|
|
6927
6978
|
function VMI_ln(x) {
|
6928
|
-
//
|
6929
|
-
// code #VALUE! if X is negative
|
6979
|
+
// Replace the top number X of the stack by ln(X), or by error
|
6980
|
+
// code #VALUE! if X is negative.
|
6930
6981
|
const d = x.top();
|
6931
6982
|
if(d !== false) {
|
6932
6983
|
if(DEBUGGING) console.log('LN ' + d);
|
@@ -6939,7 +6990,7 @@ function VMI_ln(x) {
|
|
6939
6990
|
}
|
6940
6991
|
|
6941
6992
|
function VMI_exp(x) {
|
6942
|
-
//
|
6993
|
+
// Replace the top number X of the stack by exp(X).
|
6943
6994
|
const d = x.top();
|
6944
6995
|
if(d !== false) {
|
6945
6996
|
if(DEBUGGING) console.log('EXP ' + d);
|
@@ -6948,7 +6999,7 @@ function VMI_exp(x) {
|
|
6948
6999
|
}
|
6949
7000
|
|
6950
7001
|
function VMI_log(x) {
|
6951
|
-
//
|
7002
|
+
// Pop the top number B from the stack, and replace the new top
|
6952
7003
|
// number A by A log B. NOTE: x = A log B <=> x = ln(B) / ln(A)
|
6953
7004
|
let d = x.pop();
|
6954
7005
|
if(d !== false) {
|
@@ -6963,7 +7014,7 @@ function VMI_log(x) {
|
|
6963
7014
|
}
|
6964
7015
|
|
6965
7016
|
function VMI_round(x) {
|
6966
|
-
//
|
7017
|
+
// Replace the top number X of the stack by round(X).
|
6967
7018
|
const d = x.top();
|
6968
7019
|
if(d !== false) {
|
6969
7020
|
if(DEBUGGING) console.log('ROUND ' + d);
|
@@ -6972,7 +7023,7 @@ function VMI_round(x) {
|
|
6972
7023
|
}
|
6973
7024
|
|
6974
7025
|
function VMI_int(x) {
|
6975
|
-
//
|
7026
|
+
// Replace the top number X of the stack by its integer part.
|
6976
7027
|
const d = x.top();
|
6977
7028
|
if(d !== false) {
|
6978
7029
|
if(DEBUGGING) console.log('INT ' + d);
|
@@ -6981,7 +7032,7 @@ function VMI_int(x) {
|
|
6981
7032
|
}
|
6982
7033
|
|
6983
7034
|
function VMI_fract(x) {
|
6984
|
-
//
|
7035
|
+
// Replace the top number X of the stack by its fraction part.
|
6985
7036
|
const d = x.top();
|
6986
7037
|
if(d !== false) {
|
6987
7038
|
if(DEBUGGING) console.log('FRACT ' + d);
|
@@ -6990,9 +7041,9 @@ function VMI_fract(x) {
|
|
6990
7041
|
}
|
6991
7042
|
|
6992
7043
|
function VMI_exponential(x) {
|
6993
|
-
//
|
7044
|
+
// Replace the top number X of the stack by a random number from the
|
6994
7045
|
// negative exponential distribution with parameter X (so X is the lambda,
|
6995
|
-
// and the mean will be 1/X)
|
7046
|
+
// and the mean will be 1/X).
|
6996
7047
|
const d = x.top();
|
6997
7048
|
if(d !== false) {
|
6998
7049
|
const a = randomExponential(d);
|
@@ -7002,8 +7053,8 @@ function VMI_exponential(x) {
|
|
7002
7053
|
}
|
7003
7054
|
|
7004
7055
|
function VMI_poisson(x) {
|
7005
|
-
//
|
7006
|
-
// poisson distribution with parameter X (so X is the mean value lambda)
|
7056
|
+
// Replace the top number X of the stack by a random number from the
|
7057
|
+
// poisson distribution with parameter X (so X is the mean value lambda).
|
7007
7058
|
const d = x.top();
|
7008
7059
|
if(d !== false) {
|
7009
7060
|
const a = randomPoisson(d);
|
@@ -7013,8 +7064,9 @@ function VMI_poisson(x) {
|
|
7013
7064
|
}
|
7014
7065
|
|
7015
7066
|
function VMI_binomial(x) {
|
7016
|
-
//
|
7017
|
-
// number from the binomial distribution with n = A[0] and
|
7067
|
+
// Replace the top list (!) A of the stack by Bin(A[0], A[1]), i.e.,
|
7068
|
+
// a random number from the binomial distribution with n = A[0] and
|
7069
|
+
// p = A[1].
|
7018
7070
|
const d = x.top();
|
7019
7071
|
if(d !== false) {
|
7020
7072
|
if(d instanceof Array && d.length === 2) {
|
@@ -7029,8 +7081,9 @@ function VMI_binomial(x) {
|
|
7029
7081
|
}
|
7030
7082
|
|
7031
7083
|
function VMI_normal(x) {
|
7032
|
-
//
|
7033
|
-
// number from the normal distribution with mu = A[0] and
|
7084
|
+
// Replace the top list (!) A of the stack by N(A[0], A[1]), i.e.,
|
7085
|
+
// a random number from the normal distribution with mu = A[0] and
|
7086
|
+
// sigma = A[1].
|
7034
7087
|
const d = x.top();
|
7035
7088
|
if(d !== false) {
|
7036
7089
|
if(d instanceof Array && d.length === 2) {
|
@@ -7045,8 +7098,9 @@ function VMI_normal(x) {
|
|
7045
7098
|
}
|
7046
7099
|
|
7047
7100
|
function VMI_weibull(x) {
|
7048
|
-
//
|
7049
|
-
// random number from the Weibull distribution with lambda = A[0]
|
7101
|
+
// Replace the top list (!) A of the stack by Weibull(A[0], A[1]), i.e.,
|
7102
|
+
// a random number from the Weibull distribution with lambda = A[0]
|
7103
|
+
// and k = A[1].
|
7050
7104
|
const d = x.top();
|
7051
7105
|
if(d !== false) {
|
7052
7106
|
if(d instanceof Array && d.length === 2) {
|
@@ -7061,10 +7115,10 @@ function VMI_weibull(x) {
|
|
7061
7115
|
}
|
7062
7116
|
|
7063
7117
|
function VMI_triangular(x) {
|
7064
|
-
// Replaces the top list (!) A of the stack by Tri(A[0], A[1]), A[2]),
|
7065
|
-
// a random number from the triangular distribution with a = A[0],
|
7066
|
-
// and c = A[2]. NOTE: if only 2 parameters are passed, c is
|
7067
|
-
// (a + b) / 2
|
7118
|
+
// Replaces the top list (!) A of the stack by Tri(A[0], A[1]), A[2]),
|
7119
|
+
// i.e., a random number from the triangular distribution with a = A[0],
|
7120
|
+
// b = A[1], and c = A[2]. NOTE: if only 2 parameters are passed, c is
|
7121
|
+
// assumed to equal (a + b) / 2.
|
7068
7122
|
const d = x.top();
|
7069
7123
|
if(d !== false) {
|
7070
7124
|
if(d instanceof Array && (d.length === 2 || d.length === 3)) {
|
@@ -7079,14 +7133,15 @@ function VMI_triangular(x) {
|
|
7079
7133
|
}
|
7080
7134
|
|
7081
7135
|
function VMI_npv(x) {
|
7082
|
-
//
|
7083
|
-
// of the arguments in A. A[0] is the interest rate r, A[1] is the number
|
7084
|
-
// time periods n. If A has only 1 or 2 elements, the NPV is 0.
|
7085
|
-
// elements, A[2] is the constant cash flow C, and the NPV is
|
7086
|
-
// (for t = 0 to n-1) of C/(1+r)^t. If A has N>2 elements, A[2]
|
7087
|
-
// are considered as a cash flow time series C0, C1, ..., CN-2
|
7088
|
-
//
|
7089
|
-
//
|
7136
|
+
// Replace the top list (!) A of the stack by the net present value (NPV)
|
7137
|
+
// of the arguments in A. A[0] is the interest rate r, A[1] is the number
|
7138
|
+
// of time periods n. If A has only 1 or 2 elements, the NPV is 0.
|
7139
|
+
// If A has 3 elements, A[2] is the constant cash flow C, and the NPV is
|
7140
|
+
// the sum (for t = 0 to n-1) of C/(1+r)^t. If A has N>2 elements, A[2]
|
7141
|
+
// through A[N] are considered as a cash flow time series C0, C1, ..., CN-2
|
7142
|
+
// that is then discounted.
|
7143
|
+
// NOTE: If A is not a list, A considered to be the single argument, and
|
7144
|
+
// is hence replaced by 0.
|
7090
7145
|
const d = x.top();
|
7091
7146
|
if(d !== false) {
|
7092
7147
|
if(d instanceof Array && d.length > 2) {
|
@@ -7119,8 +7174,8 @@ function VMI_npv(x) {
|
|
7119
7174
|
}
|
7120
7175
|
|
7121
7176
|
function VMI_min(x) {
|
7122
|
-
//
|
7123
|
-
//
|
7177
|
+
// Replace the top list (!) A of the stack by the lowest value in this
|
7178
|
+
// list. If A is not a list, A is left on the stack.
|
7124
7179
|
const d = x.top();
|
7125
7180
|
if(d !== false && d instanceof Array) {
|
7126
7181
|
if(DEBUGGING) console.log('MIN (' + d.join(', ') + ')');
|
@@ -7131,8 +7186,8 @@ function VMI_min(x) {
|
|
7131
7186
|
}
|
7132
7187
|
|
7133
7188
|
function VMI_max(x) {
|
7134
|
-
//
|
7135
|
-
//
|
7189
|
+
// Replace the top list (!) A of the stack by the highest value in this
|
7190
|
+
// list. If A is not a list, A is left on the stack.
|
7136
7191
|
const d = x.top();
|
7137
7192
|
if(d !== false && d instanceof Array) {
|
7138
7193
|
if(DEBUGGING) console.log('MAX (' + d.join(', ') + ')');
|
@@ -7143,9 +7198,9 @@ function VMI_max(x) {
|
|
7143
7198
|
}
|
7144
7199
|
|
7145
7200
|
function VMI_concat(x) {
|
7146
|
-
//
|
7147
|
-
// element A by [A, B] if A is a number, or
|
7148
|
-
// of numbers (!) or
|
7201
|
+
// Pop the top number B from the stack, and then replace the new top
|
7202
|
+
// element A by [A, B] if A is a number, or add B to A if A is a list
|
7203
|
+
// of numbers (!), or concatenate if A and B both are lists.
|
7149
7204
|
const d = x.pop();
|
7150
7205
|
if(d !== false) {
|
7151
7206
|
if(DEBUGGING) console.log('CONCAT (' + d.join(', ') + ')');
|
@@ -7164,8 +7219,8 @@ function VMI_concat(x) {
|
|
7164
7219
|
}
|
7165
7220
|
|
7166
7221
|
function VMI_jump(x, index) {
|
7167
|
-
//
|
7168
|
-
// counter is ALWAYS increased by 1 after calling a VMI function
|
7222
|
+
// Set the program counter of the VM to `index` minus 1, as the
|
7223
|
+
// counter is ALWAYS increased by 1 after calling a VMI function.
|
7169
7224
|
if(DEBUGGING) console.log('JUMP ' + index);
|
7170
7225
|
x.program_counter = index - 1;
|
7171
7226
|
}
|
@@ -7179,7 +7234,7 @@ function VMI_jump_if_false(x, index) {
|
|
7179
7234
|
if(r === 0 || r === VM.UNDEFINED || r === false) {
|
7180
7235
|
// Only jump on FALSE, leaving the stack "as is", so that in case
|
7181
7236
|
// of no THEN, the expression result equals the IF condition value.
|
7182
|
-
// NOTE: Also do this on a stack error (r === false)
|
7237
|
+
// NOTE: Also do this on a stack error (r === false).
|
7183
7238
|
x.program_counter = index - 1;
|
7184
7239
|
} else {
|
7185
7240
|
// Remove the value from the stack.
|
@@ -7209,22 +7264,22 @@ function VMI_if_else(x) {
|
|
7209
7264
|
}
|
7210
7265
|
|
7211
7266
|
//
|
7212
|
-
// Functions that implement random numbers from specific distribution
|
7267
|
+
// Functions that implement random numbers from specific distribution.
|
7213
7268
|
//
|
7214
7269
|
|
7215
7270
|
function randomExponential(lambda) {
|
7216
|
-
//
|
7271
|
+
// Return a random number drawn from a Exp(lambda) distribution.
|
7217
7272
|
return -Math.log(Math.random()) / lambda;
|
7218
7273
|
}
|
7219
7274
|
|
7220
7275
|
function randomWeibull(lambda, k) {
|
7221
|
-
//
|
7276
|
+
// Return a random number drawn from a Weibull(lambda, k) distribution.
|
7222
7277
|
if(Math.abs(k) < VM.NEAR_ZERO) return VM.DIV_ZERO;
|
7223
7278
|
return lambda * Math.pow(-Math.log(Math.random()), 1.0 / k);
|
7224
7279
|
}
|
7225
7280
|
|
7226
7281
|
function randomTriangular(a, b, c=0.5*(a + b)) {
|
7227
|
-
//
|
7282
|
+
// Return a random number drawn from a Triangular(a, b, c) distribution.
|
7228
7283
|
const u = Math.random(), b_a = b - a, c_a = c - a;
|
7229
7284
|
if(u < c_a / b_a) {
|
7230
7285
|
return a + Math.sqrt(u * b_a * c_a);
|
@@ -7234,8 +7289,8 @@ function randomTriangular(a, b, c=0.5*(a + b)) {
|
|
7234
7289
|
}
|
7235
7290
|
|
7236
7291
|
function randomNormal(mean, std) {
|
7237
|
-
//
|
7238
|
-
// distribution
|
7292
|
+
// Return a random number drawn from a N(mean, standard deviation)
|
7293
|
+
// distribution.
|
7239
7294
|
const
|
7240
7295
|
a1 = -39.6968302866538, a2 = 220.946098424521, a3 = -275.928510446969,
|
7241
7296
|
a4 = 138.357751867269, a5 = -30.6647980661472, a6 = 2.50662827745924,
|
@@ -7273,11 +7328,11 @@ function randomBinomial(n, p) {
|
|
7273
7328
|
}
|
7274
7329
|
}
|
7275
7330
|
|
7276
|
-
// Global array as cache for computation of factorial numbers
|
7331
|
+
// Global array as cache for computation of factorial numbers.
|
7277
7332
|
const FACTORIALS = [0, 1];
|
7278
7333
|
|
7279
7334
|
function factorial(n) {
|
7280
|
-
// Fast factorial function using pre-calculated values up to n = 100
|
7335
|
+
// Fast factorial function using pre-calculated values up to n = 100.
|
7281
7336
|
const l = FACTORIALS.length;
|
7282
7337
|
if(n < l) return FACTORIALS[n];
|
7283
7338
|
let f = FACTORIALS[l - 1];
|
@@ -7290,7 +7345,7 @@ function factorial(n) {
|
|
7290
7345
|
|
7291
7346
|
function randomPoisson(lambda) {
|
7292
7347
|
if(lambda < 30) {
|
7293
|
-
// Use Knuth's algorithm
|
7348
|
+
// Use Knuth's algorithm.
|
7294
7349
|
const L = Math.exp(-lambda);
|
7295
7350
|
let k = 0, p = 1;
|
7296
7351
|
do {
|
@@ -7299,8 +7354,8 @@ function randomPoisson(lambda) {
|
|
7299
7354
|
} while(p > L);
|
7300
7355
|
return k - 1;
|
7301
7356
|
} else {
|
7302
|
-
// Use "method PA" from Atkinson, A.C. (1979). The Computer Generation
|
7303
|
-
// Poisson Random Variables, Journal of the Royal Statistical Society
|
7357
|
+
// Use "method PA" from Atkinson, A.C. (1979). The Computer Generation
|
7358
|
+
// of Poisson Random Variables, Journal of the Royal Statistical Society
|
7304
7359
|
// Series C (Applied Statistics), 28(1): 29-35.
|
7305
7360
|
const c = 0.767 - 3.36 / lambda,
|
7306
7361
|
beta = Math.PI / Math.sqrt(3.0 * lambda),
|
@@ -7467,14 +7522,18 @@ function VMI_add_const_to_coefficient(args) {
|
|
7467
7522
|
if(DEBUGGING) {
|
7468
7523
|
console.log(`add_const_to_coefficient [${k}]: ${VM.sig4Dig(n)}`);
|
7469
7524
|
}
|
7525
|
+
// A negative delay may result in a variable index beyond the tableau
|
7526
|
+
// column range. Such "future variables" should be ignored.
|
7527
|
+
if(d < 0 && k > VM.chunk_offset) return;
|
7470
7528
|
if(k <= 0) {
|
7471
|
-
// NOTE:
|
7472
|
-
// means that the value of the decision variable X for which the
|
7473
|
-
// C is to be set by this instruction has been calculated
|
7474
|
-
// previous block. Since the value of X is known,
|
7475
|
-
// implemented as subtracting n*X from the right hand
|
7476
|
-
// constraint.
|
7477
|
-
// NOTE:
|
7529
|
+
// NOTE: If `k` falls PRIOR to the start of the block being solved,
|
7530
|
+
// this means that the value of the decision variable X for which the
|
7531
|
+
// coefficient C is to be set by this instruction has been calculated
|
7532
|
+
// while solving a previous block. Since the value of X is known,
|
7533
|
+
// adding n to C is implemented as subtracting n*X from the right hand
|
7534
|
+
// side of the constraint.
|
7535
|
+
// NOTE: Subtract 1 from index `vi` because VM.variables is a 0-based
|
7536
|
+
// array.
|
7478
7537
|
const
|
7479
7538
|
vbl = VM.variables[vi - 1],
|
7480
7539
|
pv = VM.priorValue(vbl, t);
|
@@ -7493,10 +7552,9 @@ function VMI_add_const_to_coefficient(args) {
|
|
7493
7552
|
function VMI_add_const_to_sum_coefficients(args) {
|
7494
7553
|
// NOTE: used to implement data links with SUM multiplier
|
7495
7554
|
// `args`: [var_index, number, delay (, 1)]
|
7496
|
-
const
|
7497
|
-
|
7498
|
-
|
7499
|
-
let k = VM.offset + vi - d * VM.cols,
|
7555
|
+
const vi = args[0];
|
7556
|
+
let d = args[2].object.actualDelay(VM.t),
|
7557
|
+
k = VM.offset + vi - d * VM.cols,
|
7500
7558
|
t = VM.t - d,
|
7501
7559
|
n = args[1];
|
7502
7560
|
if(args.length > 3) n /= (d + 1);
|
@@ -7504,7 +7562,16 @@ function VMI_add_const_to_sum_coefficients(args) {
|
|
7504
7562
|
console.log('add_const_to_sum_coefficients [' + k + ']: ' +
|
7505
7563
|
VM.sig4Dig(n) + '; delay = ' + d);
|
7506
7564
|
}
|
7565
|
+
// NOTE: When delay is negative, start at time t, not t - d.
|
7566
|
+
if(d < 0) {
|
7567
|
+
k = VM.offset + vi;
|
7568
|
+
t = VM.t;
|
7569
|
+
d = -d;
|
7570
|
+
}
|
7507
7571
|
for(let i = 0; i <= d; i++) {
|
7572
|
+
// A negative delay may result in a variable index beyond the tableau
|
7573
|
+
// column range. Such "future variables" should be ignored.
|
7574
|
+
if(k > VM.chunk_offset) return;
|
7508
7575
|
if(k <= 0) {
|
7509
7576
|
// See NOTE in VMI_add_const_to_coefficient instruction
|
7510
7577
|
const vbl = VM.variables[vi - 1];
|
@@ -7541,6 +7608,9 @@ function VMI_add_var_to_coefficient(args) {
|
|
7541
7608
|
console.log('add_var_to_coefficient [' + k + ']: ' +
|
7542
7609
|
args[1].variableName + ' (t = ' + t + ')');
|
7543
7610
|
}
|
7611
|
+
// A negative delay may result in a variable index beyond the tableau
|
7612
|
+
// column range. Such "future variables" should be ignored.
|
7613
|
+
if(k > VM.chunk_offset) return;
|
7544
7614
|
if(k <= 0) {
|
7545
7615
|
// See NOTE in VMI_add_const_to_coefficient instruction
|
7546
7616
|
const vbl = VM.variables[vi - 1];
|
@@ -7560,15 +7630,24 @@ function VMI_add_var_to_weighted_sum_coefficients(args) {
|
|
7560
7630
|
// `args`: [var_index, number, delay (, 1)]
|
7561
7631
|
const
|
7562
7632
|
vi = args[0],
|
7563
|
-
v = args[1]
|
7564
|
-
|
7565
|
-
|
7633
|
+
v = args[1];
|
7634
|
+
let d = args[2].object.actualDelay(VM.t),
|
7635
|
+
k = VM.offset + vi - d * VM.cols,
|
7566
7636
|
t = VM.t - d;
|
7567
7637
|
if(DEBUGGING) {
|
7568
7638
|
console.log('add_var_to_weighted_sum_coefficients [' + k + ']: ' +
|
7569
7639
|
VM.sig4Dig(w) + ' * ' + v.variableName + ' (t = ' + t + ')');
|
7570
7640
|
}
|
7641
|
+
// NOTE: When delay is negative, start at time t, not t - d.
|
7642
|
+
if(d < 0) {
|
7643
|
+
k = VM.offset + vi;
|
7644
|
+
t = VM.t;
|
7645
|
+
d = -d;
|
7646
|
+
}
|
7571
7647
|
for(let i = 0; i <= d; i++) {
|
7648
|
+
// A negative delay may result in a variable index beyond the tableau
|
7649
|
+
// column range. Such "future variables" should be ignored.
|
7650
|
+
if(k > VM.chunk_offset) return;
|
7572
7651
|
let r = v.result(t);
|
7573
7652
|
if(args.length > 3) r /= (d + 1);
|
7574
7653
|
if(k <= 0) {
|
@@ -7605,6 +7684,9 @@ function VMI_subtract_const_from_coefficient(args) {
|
|
7605
7684
|
if(DEBUGGING) {
|
7606
7685
|
console.log('subtract_const_from_coefficient [' + k + ']: ' + VM.sig4Dig(n));
|
7607
7686
|
}
|
7687
|
+
// A negative delay may result in a variable index beyond the tableau
|
7688
|
+
// column range. Such "future variables" should be ignored.
|
7689
|
+
if(k > VM.chunk_offset) return;
|
7608
7690
|
if(k <= 0) {
|
7609
7691
|
// See NOTE in VMI_add_const_to_coefficient instruction
|
7610
7692
|
const vbl = VM.variables[vi - 1];
|
@@ -7645,6 +7727,9 @@ function VMI_subtract_var_from_coefficient(args) {
|
|
7645
7727
|
console.log('subtract_var_from_coefficient [' + k + ']: ' +
|
7646
7728
|
args[1].variableName + ' (t = ' + t + ')');
|
7647
7729
|
}
|
7730
|
+
// A negative delay may result in a variable index beyond the tableau
|
7731
|
+
// column range. Such "future variables" should be ignored.
|
7732
|
+
if(k > VM.chunk_offset) return;
|
7648
7733
|
if(k <= 0) {
|
7649
7734
|
// See NOTE in VMI_add_const_to_coefficient instruction
|
7650
7735
|
const vbl = VM.variables[vi - 1];
|
@@ -7683,8 +7768,9 @@ function VMI_update_cash_coefficient(args) {
|
|
7683
7768
|
// not the expressions for rates or prices!
|
7684
7769
|
const t = VM.t - d;
|
7685
7770
|
// NOTE: this instruction is used only for objective function
|
7686
|
-
// coefficients; previously computed decision variables
|
7687
|
-
|
7771
|
+
// coefficients; previously computed decision variables and variables
|
7772
|
+
// beyond the tableau column range (when delay < 0) can be ignored.
|
7773
|
+
if(k <= 0 || k > VM.chunk_offset) return;
|
7688
7774
|
// NOTE: peak increase can generate cash only at the first time
|
7689
7775
|
// step of a block (when VM.offset = 0) and at the first time step
|
7690
7776
|
// of the look-ahead period (when VM.offset = block length)
|
@@ -7778,6 +7864,9 @@ function VMI_add_throughput_to_coefficient(args) {
|
|
7778
7864
|
args[1].variableName + ' * ' + args[3].variableName +
|
7779
7865
|
' (t = ' + VM.t + ')');
|
7780
7866
|
}
|
7867
|
+
// A negative delay may result in a variable index beyond the tableau
|
7868
|
+
// column range. Such "future variables" should be ignored.
|
7869
|
+
if(k > VM.chunk_offset) return;
|
7781
7870
|
if(k <= 0) {
|
7782
7871
|
const vbl = VM.variables[vi - 1];
|
7783
7872
|
if(DEBUGGING) {
|
@@ -7866,13 +7955,13 @@ function VMI_toggle_add_constraints_flag() {
|
|
7866
7955
|
|
7867
7956
|
function VMI_add_constraint(ct) {
|
7868
7957
|
// Appends the current coefficients as a row to the matrix, the current
|
7869
|
-
// RHS to the RHS vector, and `ct` to the constraint type vector
|
7870
|
-
// NOTE:
|
7958
|
+
// RHS to the RHS vector, and `ct` to the constraint type vector.
|
7959
|
+
// NOTE: Constraint is NOT added when the "add constraints flag" is FALSE.
|
7871
7960
|
if(DEBUGGING) console.log('add_constraint: ' + VM.constraint_codes[ct]);
|
7872
7961
|
if(VM.add_constraints_flag) {
|
7873
7962
|
const row = {};
|
7874
7963
|
for(let i in VM.coefficients) if(Number(i)) {
|
7875
|
-
// Do not add (near)zero coefficients to the matrix
|
7964
|
+
// Do not add (near)zero coefficients to the matrix.
|
7876
7965
|
const c = VM.coefficients[i];
|
7877
7966
|
if(Math.abs(c) >= VM.NEAR_ZERO) {
|
7878
7967
|
row[i] = c;
|