linny-r 1.9.0 → 1.9.2
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/package.json
CHANGED
package/static/index.html
CHANGED
@@ -750,6 +750,13 @@ NOTE: Unit symbols are case-sensitive, so BTU ≠ Btu">
|
|
750
750
|
placeholder="1e-4" type="text" autocomplete="off">
|
751
751
|
</td>
|
752
752
|
</tr>
|
753
|
+
<tr title="When checked, small slack uses are reported in monitor">
|
754
|
+
<td style="padding: 0px; width: 20px">
|
755
|
+
<div id="solver-show-notices" class="box clear"></div>
|
756
|
+
</td>
|
757
|
+
<td style="padding-bottom:4px">Report small slack uses</td>
|
758
|
+
</td>
|
759
|
+
</tr>
|
753
760
|
</table>
|
754
761
|
</div>
|
755
762
|
</div>
|
@@ -3689,6 +3689,7 @@ console.log('HERE name conflicts', name_conflicts, mapping);
|
|
3689
3689
|
md.element('preference').innerHTML = html.join('');
|
3690
3690
|
md.element('int-feasibility').value = MODEL.integer_tolerance;
|
3691
3691
|
md.element('mip-gap').value = MODEL.MIP_gap;
|
3692
|
+
this.setBox('solver-show-notices', MODEL.show_notices);
|
3692
3693
|
md.show();
|
3693
3694
|
}
|
3694
3695
|
|
@@ -3717,6 +3718,7 @@ console.log('HERE name conflicts', name_conflicts, mapping);
|
|
3717
3718
|
}
|
3718
3719
|
MODEL.integer_tolerance = Math.max(1e-9, Math.min(0.1, itol));
|
3719
3720
|
MODEL.MIP_gap = Math.max(0, Math.min(0.5, mgap));
|
3721
|
+
MODEL.show_notices = this.boxChecked('solver-show-notices');
|
3720
3722
|
// Close the dialog.
|
3721
3723
|
md.hide();
|
3722
3724
|
}
|
@@ -66,6 +66,7 @@ Attributes, however, are case sensitive!">[Actor X|CF]</code> for cash flow.
|
|
66
66
|
<code title="Relative time step (t − t₀ + 1)">rt</code>,
|
67
67
|
<code title="Number of current block">b</code>,
|
68
68
|
<code title="Time step within current block">bt</code>,
|
69
|
+
<code title="Time step within current chunk">ct</code>,
|
69
70
|
<code title="Duration of 1 time step (in hours)">dt</code>,
|
70
71
|
<code title="Run length (# time steps)">N</code>,
|
71
72
|
<code title="Block length (# time steps)">n</code>,
|
@@ -107,6 +107,7 @@ class LinnyRModel {
|
|
107
107
|
this.integer_tolerance = 5e-7; // integer feasibility tolerance
|
108
108
|
this.MIP_gap = 1e-4; // relative MIP gap
|
109
109
|
this.always_diagnose = true;
|
110
|
+
this.show_notices = true;
|
110
111
|
|
111
112
|
// Sensitivity-related properties
|
112
113
|
this.base_case_selectors = '';
|
@@ -2670,6 +2671,7 @@ class LinnyRModel {
|
|
2670
2671
|
this.report_results = nodeParameterValue(node, 'report-results') === '1';
|
2671
2672
|
this.show_block_arrows = nodeParameterValue(node, 'block-arrows') === '1';
|
2672
2673
|
this.always_diagnose = nodeParameterValue(node, 'diagnose') === '1';
|
2674
|
+
this.show_notices = nodeParameterValue(node, 'show-notices') === '1';
|
2673
2675
|
this.name = xmlDecoded(nodeContentByTag(node, 'name'));
|
2674
2676
|
this.author = xmlDecoded(nodeContentByTag(node, 'author'));
|
2675
2677
|
this.comments = xmlDecoded(nodeContentByTag(node, 'notes'));
|
@@ -3023,6 +3025,7 @@ class LinnyRModel {
|
|
3023
3025
|
if(this.report_results) p += ' report-results="1"';
|
3024
3026
|
if(this.show_block_arrows) p += ' block-arrows="1"';
|
3025
3027
|
if(this.always_diagnose) p += ' diagnose="1"';
|
3028
|
+
if(this.show_notices) p += ' show-notices="1"';
|
3026
3029
|
let xml = this.xml_header + ['<model', p, '><name>', xmlEncoded(this.name),
|
3027
3030
|
'</name><author>', xmlEncoded(this.author),
|
3028
3031
|
'</author><notes>', xmlEncoded(this.comments),
|
@@ -430,20 +430,25 @@ class Expression {
|
|
430
430
|
}
|
431
431
|
|
432
432
|
result(t, number=false) {
|
433
|
-
//
|
433
|
+
// Compute (only if needed) and then returns result for time step t.
|
434
434
|
// The `number` is passed only by the VMI_push_dataset_modifier
|
435
|
-
// instruction so as to force recomputation of the expression
|
436
|
-
//
|
437
|
-
// "initial value" (these follow from the variables used in the expression)
|
438
|
-
// Select the vector to use
|
435
|
+
// instruction so as to force recomputation of the expression.
|
436
|
+
// Select the vector to use.
|
439
437
|
const v = this.chooseVector(number);
|
440
438
|
if(!Array.isArray(v)) {
|
441
439
|
console.log('ANOMALY: No vector for result(t)');
|
442
440
|
return VM.UNDEFINED;
|
443
441
|
}
|
442
|
+
// NOTE: For t < 1 return the value for t = 1, since expressions have
|
443
|
+
// no "initial value" (these follow from the variables used in the
|
444
|
+
// expression).
|
444
445
|
if(t < 0 || this.isStatic) t = 0;
|
445
446
|
if(t >= v.length) return VM.UNDEFINED;
|
446
|
-
|
447
|
+
// NOTE: When VM is setting up a tableau, values computed for the
|
448
|
+
// look-ahead period must be recomputed.
|
449
|
+
if(v[t] === VM.NOT_COMPUTED || v[t] === VM.COMPUTING ||
|
450
|
+
(!this.isStatic && VM.inLookAhead(t))) {
|
451
|
+
v[t] = VM.NOT_COMPUTED;
|
447
452
|
this.compute(t, number);
|
448
453
|
}
|
449
454
|
// NOTE: when this expression is the "active" parameter for sensitivity
|
@@ -2477,8 +2482,19 @@ class VirtualMachine {
|
|
2477
2482
|
this.t = 0;
|
2478
2483
|
// Prepare for halt.
|
2479
2484
|
this.halted = false;
|
2485
|
+
// Flag to indicate that VM is executing its tableau construction code.
|
2486
|
+
// This affects how chunk time (ct) is computed, and whether expression
|
2487
|
+
// results must be recomputed (see inLookAhead below).
|
2488
|
+
this.executing_tableau_code = false;
|
2480
2489
|
UI.readyToSolve();
|
2481
2490
|
}
|
2491
|
+
|
2492
|
+
inLookAhead(t) {
|
2493
|
+
// Return TRUE if VM is executing its tableau construction code AND
|
2494
|
+
// time step `t` falls in the look-ahead period of the previous block.
|
2495
|
+
return this.executing_tableau_code &&
|
2496
|
+
t - (this.block_count - 1) * MODEL.block_length <= MODEL.look_ahead;
|
2497
|
+
}
|
2482
2498
|
|
2483
2499
|
errorMessage(n) {
|
2484
2500
|
// VM errors are very big NEGATIVE numbers, so start comparing `n`
|
@@ -2551,7 +2567,7 @@ class VirtualMachine {
|
|
2551
2567
|
return Math.round(n);
|
2552
2568
|
}
|
2553
2569
|
|
2554
|
-
sig4Dig(n) {
|
2570
|
+
sig4Dig(n, tiny=false) {
|
2555
2571
|
// Return number `n` formatted so as to show 4-5 significant digits.
|
2556
2572
|
// NOTE: As `n` should be a number, a warning sign will typically
|
2557
2573
|
// indicate a bug in the software.
|
@@ -2560,8 +2576,13 @@ class VirtualMachine {
|
|
2560
2576
|
// If `n` has a special value, return its representation.
|
2561
2577
|
if(sv[0]) return sv[1];
|
2562
2578
|
const a = Math.abs(n);
|
2579
|
+
if(a === 0) return 0;
|
2563
2580
|
// Signal small differences from true 0 by a leading + or - sign.
|
2564
|
-
if(
|
2581
|
+
if(a <= this.ON_OFF_THRESHOLD) {
|
2582
|
+
// The `tiny` flag indicates: display small number in E-notation.
|
2583
|
+
if(tiny) return n.toPrecision(1);
|
2584
|
+
return n > 0 ? '+0' : '-0';
|
2585
|
+
}
|
2565
2586
|
if(a >= 9999995) return n.toPrecision(4);
|
2566
2587
|
if(Math.abs(a-Math.round(a)) < 0.0005) return Math.round(n);
|
2567
2588
|
if(a < 1) return Math.round(n*10000) / 10000;
|
@@ -2871,7 +2892,7 @@ class VirtualMachine {
|
|
2871
2892
|
const
|
2872
2893
|
bm = r.block_messages[i],
|
2873
2894
|
err = (bm.messages.indexOf('Solver status = 0') < 0 ||
|
2874
|
-
bm.messages.indexOf(
|
2895
|
+
bm.messages.indexOf(this.WARNING) >= 0);
|
2875
2896
|
this.solver_times.push(bm.solver_time);
|
2876
2897
|
this.messages.push(bm.messages);
|
2877
2898
|
this.variables.push(this.no_variables);
|
@@ -4650,14 +4671,16 @@ class VirtualMachine {
|
|
4650
4671
|
}
|
4651
4672
|
if(b <= this.nr_of_time_steps && absl > VM.ON_OFF_THRESHOLD) {
|
4652
4673
|
this.logMessage(block, `${this.WARNING}(t=${b}${round}) ` +
|
4653
|
-
`${v[1].displayName} ${v[0]} slack =
|
4674
|
+
`${v[1].displayName} ${v[0]} slack = ` +
|
4675
|
+
// NOTE: TRUE denotes "show tiny values with precision".
|
4676
|
+
this.sig4Dig(slack, true));
|
4654
4677
|
if(v[1] instanceof Product) {
|
4655
4678
|
const ppc = v[1].productPositionClusters;
|
4656
4679
|
for(let ci = 0; ci < ppc.length; ci++) {
|
4657
4680
|
ppc[ci].usesSlack(b, v[1], v[0]);
|
4658
4681
|
}
|
4659
4682
|
}
|
4660
|
-
} else if(
|
4683
|
+
} else if(MODEL.show_notices) {
|
4661
4684
|
this.logMessage(block, '---- Notice: (t=' + b + round + ') ' +
|
4662
4685
|
v[1].displayName + ' ' + v[0] + ' slack = ' +
|
4663
4686
|
slack.toPrecision(1));
|
@@ -5223,7 +5246,7 @@ class VirtualMachine {
|
|
5223
5246
|
}
|
5224
5247
|
|
5225
5248
|
addTableauSegment(start, abl) {
|
5226
|
-
if(
|
5249
|
+
if(this.halted) {
|
5227
5250
|
this.hideSetUpOrWriteProgress();
|
5228
5251
|
this.stopSolving();
|
5229
5252
|
return;
|
@@ -5233,6 +5256,7 @@ class VirtualMachine {
|
|
5233
5256
|
var l;
|
5234
5257
|
const next_start = (start + this.tsl * 1.2 < abl ? start + this.tsl : abl);
|
5235
5258
|
for(let i = start; i < next_start; i++) {
|
5259
|
+
this.executing_tableau_code = true;
|
5236
5260
|
this.logTrace('EXECUTE for t=' + this.t);
|
5237
5261
|
l = this.code.length;
|
5238
5262
|
for(let j = 0; j < l; j++) {
|
@@ -5244,6 +5268,7 @@ class VirtualMachine {
|
|
5244
5268
|
this.logTrace([(' ' + j).slice(-5), ': coeff = ',
|
5245
5269
|
JSON.stringify(this.coefficients), '; rhs = ', this.rhs].join(''));
|
5246
5270
|
}
|
5271
|
+
this.executing_tableau_code = false;
|
5247
5272
|
this.logTrace('STOP executing block code');
|
5248
5273
|
// Add constraints for paced process variables.
|
5249
5274
|
// NOTE: This is effectuated by *executing* VM instructions.
|
@@ -6268,11 +6293,26 @@ function VMI_push_block_time(x) {
|
|
6268
6293
|
const
|
6269
6294
|
lt = x.step[x.step.length - 1] - 1,
|
6270
6295
|
bnr = Math.floor(lt / MODEL.block_length),
|
6271
|
-
t = lt - bnr * MODEL.block_length + 1;
|
6296
|
+
t = lt - bnr * MODEL.block_length + 1;
|
6272
6297
|
if(DEBUGGING) console.log('push block time bt = ' + t);
|
6273
6298
|
x.push(t);
|
6274
6299
|
}
|
6275
6300
|
|
6301
|
+
function VMI_push_chunk_time(x) {
|
6302
|
+
// Push the time step for which the VM is preparing the tableau.
|
6303
|
+
// NOTE: Chunk time is meaningful only while the VM is solving a block.
|
6304
|
+
// If not, the block time is pushed.
|
6305
|
+
if(VM.executing_tableau_code) {
|
6306
|
+
const
|
6307
|
+
ct = VM.t - (VM.block_count - 1) * MODEL.block_length;
|
6308
|
+
if(DEBUGGING) console.log('push chunk time ct = ' + ct);
|
6309
|
+
x.push(ct);
|
6310
|
+
} else {
|
6311
|
+
if(DEBUGGING) console.log('push chunk time: NOT constructing tableau');
|
6312
|
+
VMI_push_block_time(x);
|
6313
|
+
}
|
6314
|
+
}
|
6315
|
+
|
6276
6316
|
function VMI_push_block_number(x) {
|
6277
6317
|
// Push the number of the block currently being optimized.
|
6278
6318
|
// NOTE: Block numbering starts at 1.
|
@@ -6549,28 +6589,32 @@ function relativeTimeStep(t, anchor, offset, dtm, x) {
|
|
6549
6589
|
// Offset relative to current time step, scaled to time unit of run.
|
6550
6590
|
return Math.floor((t + offset) * dtm);
|
6551
6591
|
}
|
6592
|
+
if(anchor === 'f') {
|
6593
|
+
// Last: offset relative to index 1 in the vector.
|
6594
|
+
return 1 + offset;
|
6595
|
+
}
|
6596
|
+
if(anchor === 'l') {
|
6597
|
+
// Last: offset relative to the last index in the vector.
|
6598
|
+
return VM.nr_of_time_steps + offset;
|
6599
|
+
}
|
6600
|
+
const cb = Math.trunc((t - 1) / MODEL.block_length);
|
6552
6601
|
if(anchor === 'c') {
|
6553
6602
|
// Relative to start of current optimization block.
|
6554
|
-
return
|
6603
|
+
return cb * MODEL.block_length + 1 + offset;
|
6555
6604
|
}
|
6556
6605
|
if(anchor === 'p') {
|
6557
6606
|
// Relative to start of previous optimization block.
|
6558
|
-
return (
|
6607
|
+
return (cb - 1) * MODEL.block_length + 1 + offset;
|
6559
6608
|
}
|
6560
6609
|
if(anchor === 'n') {
|
6561
6610
|
// Relative to start of next optimization block.
|
6562
|
-
return (
|
6563
|
-
}
|
6564
|
-
if(anchor === 'l') {
|
6565
|
-
// Last: offset relative to the last index in the vector.
|
6566
|
-
return VM.nr_of_time_steps + offset;
|
6611
|
+
return (cb + 1) * MODEL.block_length + 1 + offset;
|
6567
6612
|
}
|
6568
6613
|
if(anchor === 's') {
|
6569
6614
|
// Scaled: offset is scaled to time unit of run.
|
6570
6615
|
return Math.floor(offset * dtm);
|
6571
6616
|
}
|
6572
6617
|
// Fall-through: offset relative to the initial value index (0).
|
6573
|
-
// NOTE: this also applies to anchor f (First).
|
6574
6618
|
return offset;
|
6575
6619
|
}
|
6576
6620
|
|
@@ -7088,7 +7132,8 @@ function VMI_push_statistic(x, args) {
|
|
7088
7132
|
t2 = Math.max(0, Math.min(tmax, t2));
|
7089
7133
|
// Trace only now that time step range has been computed
|
7090
7134
|
if(DEBUGGING) {
|
7091
|
-
const trc = ['push statistic: [', stat, ': N = ', list.length, ']',
|
7135
|
+
const trc = ['push statistic: [', stat, ': N = ', list.length, ']',
|
7136
|
+
ao1, ao2, ' (t = ', t1, '-', t2, ')'];
|
7092
7137
|
console.log(trc.join(''));
|
7093
7138
|
}
|
7094
7139
|
// Establish whether statistic pertains to non-zero values only
|
@@ -7124,7 +7169,7 @@ function VMI_push_statistic(x, args) {
|
|
7124
7169
|
const
|
7125
7170
|
n = vlist.length,
|
7126
7171
|
// NOTE: count is the number of values used in the statistic
|
7127
|
-
count = (nz ? n : list.length);
|
7172
|
+
count = (nz ? n : list.length * (t2 - t1 + 1));
|
7128
7173
|
if(stat === 'N') {
|
7129
7174
|
x.push(count);
|
7130
7175
|
return;
|
@@ -8683,11 +8728,12 @@ const
|
|
8683
8728
|
SEPARATOR_CHARS = PARENTHESES + OPERATOR_CHARS + "[ '",
|
8684
8729
|
COMPOUND_OPERATORS = ['!=', '<>', '>=', '<='],
|
8685
8730
|
CONSTANT_SYMBOLS = [
|
8686
|
-
't', 'rt', 'bt', 'b', 'N', 'n', 'l', 'r', 'lr', 'nr', 'x', 'nx',
|
8731
|
+
't', 'rt', 'bt', 'ct', 'b', 'N', 'n', 'l', 'r', 'lr', 'nr', 'x', 'nx',
|
8687
8732
|
'random', 'dt', 'true', 'false', 'pi', 'infinity', '#',
|
8688
8733
|
'i', 'j', 'k', 'yr', 'wk', 'd', 'h', 'm', 's'],
|
8689
8734
|
CONSTANT_CODES = [
|
8690
8735
|
VMI_push_time_step, VMI_push_relative_time, VMI_push_block_time,
|
8736
|
+
VMI_push_chunk_time,
|
8691
8737
|
VMI_push_block_number, VMI_push_run_length, VMI_push_block_length,
|
8692
8738
|
VMI_push_look_ahead, VMI_push_round, VMI_push_last_round,
|
8693
8739
|
VMI_push_number_of_rounds, VMI_push_run_number, VMI_push_number_of_runs,
|
@@ -8696,7 +8742,7 @@ const
|
|
8696
8742
|
VMI_push_i, VMI_push_j, VMI_push_k,
|
8697
8743
|
VMI_push_year, VMI_push_week, VMI_push_day, VMI_push_hour,
|
8698
8744
|
VMI_push_minute, VMI_push_second],
|
8699
|
-
DYNAMIC_SYMBOLS = ['t', 'rt', 'bt', 'b', 'r', 'random', 'i', 'j', 'k'],
|
8745
|
+
DYNAMIC_SYMBOLS = ['t', 'rt', 'bt', 'ct', 'b', 'r', 'random', 'i', 'j', 'k'],
|
8700
8746
|
MONADIC_OPERATORS = [
|
8701
8747
|
'~', 'not', 'abs', 'sin', 'cos', 'atan', 'ln',
|
8702
8748
|
'exp', 'sqrt', 'round', 'int', 'fract', 'min', 'max',
|