linny-r 1.5.8 → 1.6.1
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 +1 -1
- package/static/index.html +7 -3
- package/static/linny-r.css +19 -0
- package/static/scripts/linny-r-gui-actor-manager.js +3 -3
- package/static/scripts/linny-r-gui-chart-manager.js +10 -9
- package/static/scripts/linny-r-gui-controller.js +49 -36
- package/static/scripts/linny-r-gui-dataset-manager.js +17 -49
- package/static/scripts/linny-r-gui-documentation-manager.js +71 -47
- package/static/scripts/linny-r-gui-equation-manager.js +26 -10
- package/static/scripts/linny-r-gui-experiment-manager.js +41 -23
- package/static/scripts/linny-r-gui-expression-editor.js +26 -21
- package/static/scripts/linny-r-gui-finder.js +1 -0
- package/static/scripts/linny-r-gui-monitor.js +4 -4
- package/static/scripts/linny-r-milp.js +18 -15
- package/static/scripts/linny-r-model.js +354 -137
- package/static/scripts/linny-r-utils.js +68 -18
- package/static/scripts/linny-r-vm.js +593 -300
@@ -33,7 +33,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
33
33
|
SOFTWARE.
|
34
34
|
*/
|
35
35
|
|
36
|
-
// CLASS Expression
|
36
|
+
// CLASS Expression
|
37
37
|
class Expression {
|
38
38
|
constructor(obj, attr, text) {
|
39
39
|
// Expressions are typically defined for some attribute of some
|
@@ -42,12 +42,22 @@ class Expression {
|
|
42
42
|
this.object = obj;
|
43
43
|
this.attribute = attr;
|
44
44
|
this.text = text;
|
45
|
+
// For method expressions only: the object to which they apply.
|
46
|
+
// This will be set dynamically by the VMI_push_method instruction.
|
47
|
+
this.method_object = null;
|
48
|
+
// Likewise, VMI_push_method may set the method object prefix if
|
49
|
+
// the specific entity needs to be inferred dynamically.
|
50
|
+
this.method_object_prefix = '';
|
45
51
|
// A stack for local time step (to allow lazy evaluation).
|
46
52
|
this.step = [];
|
47
53
|
// An operand stack for computation (elements must be numeric).
|
48
54
|
this.stack = [];
|
49
55
|
// NOTE: code = NULL indicates: not compiled yet.
|
50
56
|
this.code = null;
|
57
|
+
// Error message when last compiled.
|
58
|
+
this.compile_issue = '';
|
59
|
+
// Error message when last computed.
|
60
|
+
this.compute_issue = '';
|
51
61
|
// NOTE: Use a semaphore to prevent cyclic recursion.
|
52
62
|
this.compiling = false;
|
53
63
|
// While compiling, check whether any operand depends on time.
|
@@ -69,28 +79,67 @@ class Expression {
|
|
69
79
|
// by VMI_push_dataset_modifier (read and/or write) and by
|
70
80
|
// VMI_push_contextual_number (read only).
|
71
81
|
this.wildcard_vector_index = false;
|
82
|
+
// Method expressions are similar to wildcard expressions, but have
|
83
|
+
// not natural numbering scheme. By keeping a list of all objects
|
84
|
+
// to which a method has been applied, the index of such an object
|
85
|
+
// in this list serves as the vector number.
|
86
|
+
this.method_object_list = [];
|
72
87
|
// Special instructions can store results as cache properties to save
|
73
88
|
// (re)computation time; cache is cleared when expression is reset.
|
74
89
|
this.cache = {};
|
75
90
|
}
|
76
91
|
|
77
92
|
get isWildcardExpression() {
|
78
|
-
//
|
93
|
+
// Return TRUE if the owner is a dataset, and the attribute contains
|
79
94
|
// wildcards.
|
80
95
|
return this.object instanceof Dataset &&
|
81
96
|
this.object.isWildcardSelector(this.attribute);
|
82
97
|
}
|
83
98
|
|
99
|
+
get isMethod() {
|
100
|
+
// Return TRUE if the owner is the equations dataset, and the
|
101
|
+
// attribute starts with a colon.
|
102
|
+
return this.object === MODEL.equations_dataset &&
|
103
|
+
this.attribute.startsWith(':');
|
104
|
+
}
|
105
|
+
|
106
|
+
get noMethodObject() {
|
107
|
+
// Return TRUE if expression is a method that does not apply to
|
108
|
+
// any entity group.
|
109
|
+
return this.isMethod && !(this.eligible_prefixes &&
|
110
|
+
Object.keys(this.eligible_prefixes).length > 0);
|
111
|
+
}
|
112
|
+
|
113
|
+
matchWithEligiblePrefixes(pref) {
|
114
|
+
// Return the entity for which `pref` matches with an eligible prefix
|
115
|
+
// of this expression.
|
116
|
+
// NOTE: This expression must have been compiled to "know" its
|
117
|
+
// eligible prefixes.
|
118
|
+
this.compile();
|
119
|
+
// NOTE: Prevent infinite recursion, but do not generate a warning;
|
120
|
+
// the expression parser will do this.
|
121
|
+
if(this.compiling || !this.eligible_prefixes) return false;
|
122
|
+
return this.eligible_prefixes[pref.toLowerCase()] || false;
|
123
|
+
}
|
124
|
+
|
125
|
+
isEligible(prefix) {
|
126
|
+
// Return TRUE if `prefix` is an eligible prefix for this method.
|
127
|
+
if(this.eligible_prefixes) {
|
128
|
+
return this.eligible_prefixes[prefix.toLowerCase()] || false;
|
129
|
+
}
|
130
|
+
return false;
|
131
|
+
}
|
132
|
+
|
84
133
|
get variableName() {
|
85
|
-
// Return the name of the variable computed by this expression
|
134
|
+
// Return the name of the variable computed by this expression.
|
86
135
|
if(this.object === MODEL.equations_dataset) return 'equation ' + this.attribute;
|
87
136
|
if(this.object) return this.object.displayName + UI.OA_SEPARATOR + this.attribute;
|
88
137
|
return 'Unknown variable (no object)';
|
89
138
|
}
|
90
139
|
|
91
140
|
get timeStepDuration() {
|
92
|
-
//
|
93
|
-
// otherwise dt for the current model
|
141
|
+
// Return dt for dataset if this is a dataset modifier expression;
|
142
|
+
// otherwise dt for the current model.
|
94
143
|
if(this.object instanceof Dataset) {
|
95
144
|
return this.object.time_scale * VM.time_unit_values[this.object.time_unit];
|
96
145
|
}
|
@@ -98,47 +147,39 @@ class Expression {
|
|
98
147
|
}
|
99
148
|
|
100
149
|
get referencedEntities() {
|
101
|
-
//
|
102
|
-
|
103
|
-
const
|
104
|
-
el = [],
|
105
|
-
ml = [...this.text.matchAll(/\[(\{[^\}]+\}){0,1}([^\]]+)\]/g)];
|
106
|
-
for(let i = 0; i < ml.length; i++) {
|
107
|
-
const n = ml[i][2].trim();
|
108
|
-
let sep = n.lastIndexOf('|');
|
109
|
-
if(sep < 0) sep = n.lastIndexOf('@');
|
110
|
-
const
|
111
|
-
en = (sep < 0 ? n : n.substring(0, sep)),
|
112
|
-
e = MODEL.objectByName(en.trim());
|
113
|
-
if(e) addDistinct(e, el);
|
114
|
-
}
|
115
|
-
return el;
|
150
|
+
// Return a list of entities referenced in this expression.
|
151
|
+
return MODEL.entitiesInString(this.text);
|
116
152
|
}
|
117
|
-
|
153
|
+
|
118
154
|
update(parser) {
|
119
|
-
// Must be called after successful compilation by the expression parser
|
155
|
+
// Must be called after successful compilation by the expression parser.
|
120
156
|
this.text = parser.expr;
|
121
157
|
this.code = parser.code;
|
122
|
-
|
123
|
-
//
|
158
|
+
this.eligible_prefixes = parser.eligible_prefixes;
|
159
|
+
// NOTE: Overrule `is_static` to make that the "initial level" attribute
|
160
|
+
// is always evaluated for t=1.
|
124
161
|
this.is_static = (this.attribute === 'IL' ? true : parser.is_static);
|
125
162
|
this.is_level_based = parser.is_level_based;
|
126
163
|
this.reset();
|
127
164
|
}
|
128
165
|
|
129
166
|
reset(default_value=VM.NOT_COMPUTED) {
|
130
|
-
//
|
167
|
+
// Clear result of previous computation (if any).
|
168
|
+
this.method_object = null;
|
169
|
+
this.compile_issue = '';
|
170
|
+
this.compute_issue = '';
|
131
171
|
this.step.length = 0;
|
132
172
|
this.stack.length = 0;
|
133
173
|
this.wildcard_vectors = {};
|
134
174
|
this.wildcard_vector_index = false;
|
175
|
+
this.method_object_list.length = 0;
|
135
176
|
this.cache = {};
|
136
177
|
this.compile(); // if(!this.compiled) REMOVED to ensure correct isStatic!!
|
137
178
|
// Static expressions only need a vector with one element (having index 0)
|
138
179
|
if(this.is_static) {
|
139
|
-
// NOTE:
|
180
|
+
// NOTE: Empty expressions (i.e., no text) may default to different
|
140
181
|
// values: typically 0 for lower bounds, infinite for upper process
|
141
|
-
// bounds, etc., so this value must be passed as parameter
|
182
|
+
// bounds, etc., so this value must be passed as parameter.
|
142
183
|
this.vector.length = 1;
|
143
184
|
if(this.text.length === 0) {
|
144
185
|
this.vector[0] = default_value;
|
@@ -180,6 +221,7 @@ class Expression {
|
|
180
221
|
}
|
181
222
|
this.update(xp);
|
182
223
|
} else {
|
224
|
+
this.compile_issue = xp.error;
|
183
225
|
this.is_static = true;
|
184
226
|
this.vector.length = 0;
|
185
227
|
this.vector[0] = VM.INVALID;
|
@@ -271,9 +313,22 @@ class Expression {
|
|
271
313
|
|
272
314
|
chooseVector(number) {
|
273
315
|
// Return the vector to use for computation (defaults to "own" vector).
|
274
|
-
// NOTE: Static wildcard expressions must also choose a vector!
|
275
|
-
if(typeof number !== 'number' ||
|
276
|
-
|
316
|
+
// NOTE: Static wildcard and method expressions must also choose a vector!
|
317
|
+
if((typeof number !== 'number' ||
|
318
|
+
(this.isStatic && !this.isWildcardExpression)) &&
|
319
|
+
!this.isMethod) return this.vector;
|
320
|
+
// Method expressions are not "numbered" but differentiate by the
|
321
|
+
// entity to which they are applied. Their "vector number" is then
|
322
|
+
// inferred by looking up this entity in a method object list.
|
323
|
+
const mop = (this.method_object && this.method_object.identifier) ||
|
324
|
+
this.method_object_prefix || '';
|
325
|
+
if(mop) {
|
326
|
+
number = this.method_object_list.indexOf(mop);
|
327
|
+
if(number < 0) {
|
328
|
+
this.method_object_list.push(mop);
|
329
|
+
number = this.method_object_list.length - 1;
|
330
|
+
}
|
331
|
+
}
|
277
332
|
// Use the vector for the wildcard number (create it if necessary).
|
278
333
|
if(!this.wildcard_vectors.hasOwnProperty(number)) {
|
279
334
|
this.wildcard_vectors[number] = [];
|
@@ -358,8 +413,10 @@ class Expression {
|
|
358
413
|
this.wildcard_vector_index = false;
|
359
414
|
// If error, display the call stack (only once).
|
360
415
|
// NOTE: "undefined", "not computed" and "still computing" are NOT
|
361
|
-
// problematic unless they result in an error (stack over/underflow)
|
416
|
+
// problematic unless they result in an error (stack over/underflow).
|
362
417
|
if(v[t] <= VM.ERROR) {
|
418
|
+
// NOTE: Record the first issue that is detected.
|
419
|
+
if(!this.compute_issue) this.compute_issue = VM.errorMessage(v[t]);
|
363
420
|
MONITOR.showCallStack(t);
|
364
421
|
VM.logCallStack(t);
|
365
422
|
}
|
@@ -397,17 +454,19 @@ class Expression {
|
|
397
454
|
}
|
398
455
|
|
399
456
|
get asAttribute() {
|
400
|
-
//
|
401
|
-
// (special values as human-readable string), or the
|
457
|
+
// Return the result for the current time step if the model has been
|
458
|
+
// solved (with special values as human-readable string), or the
|
459
|
+
// expression as text.
|
402
460
|
if(!(MODEL.solved || this.isStatic)) return this.text;
|
403
461
|
const sv = VM.specialValue(this.result(MODEL.t))[1];
|
404
|
-
// NOTE: ?? is replaced by empty string
|
462
|
+
// NOTE: ?? is replaced by empty string to facilitate copy/paste to
|
463
|
+
// Excel-like spreadsheets, where an empty cell indicates "undefined".
|
405
464
|
if(sv === '\u2047') return '';
|
406
465
|
return sv;
|
407
466
|
}
|
408
467
|
|
409
468
|
push(value) {
|
410
|
-
//
|
469
|
+
// Push a numeric value onto the computation stack.
|
411
470
|
if(this.stack.length >= VM.MAX_STACK) {
|
412
471
|
this.trace('STACK OVERFLOW');
|
413
472
|
this.stack.push(VM.OVERFLOW);
|
@@ -419,7 +478,7 @@ class Expression {
|
|
419
478
|
}
|
420
479
|
|
421
480
|
top(no_check=false) {
|
422
|
-
//
|
481
|
+
// Return the top element of the stack, or FALSE if the stack was empty.
|
423
482
|
if(this.stack.length < 1) {
|
424
483
|
this.trace('TOP: UNDERFLOW');
|
425
484
|
this.stack = [VM.UNDERFLOW];
|
@@ -427,12 +486,12 @@ class Expression {
|
|
427
486
|
return false;
|
428
487
|
}
|
429
488
|
const top = this.stack[this.stack.length - 1];
|
430
|
-
// Check for errors, "undefined", "not computed", and "still computing"
|
489
|
+
// Check for errors, "undefined", "not computed", and "still computing".
|
431
490
|
if(top < VM.MINUS_INFINITY || top > VM.EXCEPTION) {
|
432
|
-
// If error or exception, ignore UNDEFINED if `no_check` is TRUE
|
491
|
+
// If error or exception, ignore UNDEFINED if `no_check` is TRUE.
|
433
492
|
if(no_check && top <= VM.UNDEFINED) return top;
|
434
493
|
// Otherwise, leave the special value on top of the stack, and
|
435
|
-
// return FALSE so that the VM instruction will not alter it
|
494
|
+
// return FALSE so that the VM instruction will not alter it.
|
436
495
|
this.trace(
|
437
496
|
VM.errorMessage(top) + ' at top of stack: ' + this.stack.toString());
|
438
497
|
return false;
|
@@ -441,26 +500,26 @@ class Expression {
|
|
441
500
|
}
|
442
501
|
|
443
502
|
pop(no_check=false) {
|
444
|
-
//
|
445
|
-
// element B from the stack, or FALSE if the stack contains fewer
|
446
|
-
// elements, or if A and/or B are error values
|
503
|
+
// Return the two top elements A and B as [A, B] after popping the
|
504
|
+
// top element B from the stack, or FALSE if the stack contains fewer
|
505
|
+
// than 2 elements, or if A and/or B are error values.
|
447
506
|
if(this.stack.length < 2) {
|
448
507
|
this.trace('POP: UNDERFLOW');
|
449
508
|
this.stack.push(VM.UNDERFLOW);
|
450
509
|
this.computed = true;
|
451
510
|
return false;
|
452
511
|
}
|
453
|
-
// Get the top two numbers on the stack as a list
|
512
|
+
// Get the top two numbers on the stack as a list.
|
454
513
|
const dyad = this.stack.slice(-2);
|
455
|
-
// Pop only the top one
|
514
|
+
// Pop only the top one.
|
456
515
|
this.stack.pop();
|
457
|
-
// Check whether either number is an error code
|
516
|
+
// Check whether either number is an error code.
|
458
517
|
let check = Math.min(dyad[0], dyad[1]);
|
459
518
|
if(check < VM.MINUS_INFINITY &&
|
460
519
|
// Exception: "array index out of bounds" error may also be
|
461
520
|
// ignored by using the | operator.
|
462
521
|
!(no_check && check === VM.ARRAY_INDEX)) {
|
463
|
-
// If error, leave the
|
522
|
+
// If error, leave the most severe error on top of the stack.
|
464
523
|
this.retop(check);
|
465
524
|
this.trace(VM.errorMessage(check) + ' in dyad: ' + dyad.toString());
|
466
525
|
return false;
|
@@ -482,38 +541,40 @@ class Expression {
|
|
482
541
|
this.trace(VM.errorMessage(check) + ' in dyad: ' + dyad.toString());
|
483
542
|
return false;
|
484
543
|
}
|
485
|
-
// No
|
544
|
+
// No issue(s)? Then return the dyad.
|
486
545
|
return dyad;
|
487
546
|
}
|
488
547
|
|
489
548
|
retop(value) {
|
490
|
-
//
|
491
|
-
// NOTE:
|
492
|
-
// follows a TOP or POP instruction
|
549
|
+
// Replace the top element of the stack by the new value.
|
550
|
+
// NOTE: Do not check the stack length, as this instruction typically
|
551
|
+
// follows a TOP or POP instruction.
|
493
552
|
this.stack[this.stack.length - 1] = value;
|
494
553
|
return true;
|
495
554
|
}
|
496
555
|
|
497
556
|
replaceAttribute(re, a1, a2) {
|
498
|
-
//
|
499
|
-
// match the regular expression `re
|
557
|
+
// Replace occurrences of attribute `a1` by `a2` for all variables
|
558
|
+
// that match the regular expression `re`.
|
500
559
|
let n = 0;
|
501
560
|
const matches = this.text.match(re);
|
502
561
|
if(matches) {
|
503
|
-
// Match is case-insensitive, so check each for matching case of
|
562
|
+
// Match is case-insensitive, so check each for matching case of
|
563
|
+
// attribute.
|
504
564
|
for(let i = 0; i < matches.length; i++) {
|
505
565
|
const
|
506
566
|
m = matches[i],
|
507
567
|
e = m.split('|');
|
508
|
-
// Let `ao` be attribute + offset (if any) without right bracket
|
568
|
+
// Let `ao` be attribute + offset (if any) without right bracket.
|
509
569
|
let ao = e.pop().slice(0, -1),
|
510
|
-
// Then also trim offset and spaces
|
570
|
+
// Then also trim offset and spaces.
|
511
571
|
a = ao.split('@')[0].trim();
|
512
|
-
// Check
|
572
|
+
// Check whether `a` (without bracket and without spaces) indeed
|
573
|
+
// matches `a1`.
|
513
574
|
if(a === a1) {
|
514
575
|
// If so, append new attribute plus offset plus right bracket...
|
515
576
|
e.push(ao.replace(a, a2) + ']');
|
516
|
-
// ... and replace the original match by the ensemble
|
577
|
+
// ... and replace the original match by the ensemble.
|
517
578
|
this.text = this.text.replace(m, e.join('|'));
|
518
579
|
n += 1;
|
519
580
|
}
|
@@ -564,13 +625,20 @@ class ExpressionParser {
|
|
564
625
|
constructor(text, owner=null, attribute='') {
|
565
626
|
// Setting TRACE to TRUE will log parsing information to the console.
|
566
627
|
this.TRACE = false;
|
567
|
-
// `text` is the expression string to be parsed.
|
568
|
-
this.expr = text;
|
569
628
|
// NOTE: When expressions for dataset modifiers or equations are
|
570
629
|
// parsed, `owner` is their dataset, and `attribute` is their name.
|
571
630
|
this.owner = owner;
|
572
631
|
this.owner_prefix = '';
|
573
632
|
this.attribute = attribute;
|
633
|
+
// `text` is the expression string to be parsed.
|
634
|
+
this.expr = text;
|
635
|
+
this.expansions = [];
|
636
|
+
// Initialize eligible entities as NULL so it will be initialized
|
637
|
+
// when the first method expression variable is parsed.
|
638
|
+
this.eligible_prefixes = null;
|
639
|
+
// When parsing a method expression, keep a list of all attributes
|
640
|
+
// used in variables.
|
641
|
+
this.method_attributes = [];
|
574
642
|
this.dataset = null;
|
575
643
|
this.dot = null;
|
576
644
|
this.selector = '';
|
@@ -652,7 +720,7 @@ class ExpressionParser {
|
|
652
720
|
// Set the above IF condition to FALSE to profile dynamic expressions.
|
653
721
|
console.log(`Expression for ${this.ownerName}: ${this.expr}\n${msg}`);
|
654
722
|
}
|
655
|
-
|
723
|
+
|
656
724
|
// The method parseVariable(name) checks whether `name` fits this pattern:
|
657
725
|
// {run}statistic$entity|attribute@offset_1:offset_2
|
658
726
|
// allowing spaces within {run} and around | and @ and :
|
@@ -667,8 +735,8 @@ class ExpressionParser {
|
|
667
735
|
// NOTE: this array is used as argument for the virtual machine instructions
|
668
736
|
// VMI_push_var, VMI_push_statistic and VMI_push_run_result.
|
669
737
|
parseVariable(name) {
|
670
|
-
//
|
671
|
-
name = name.replace(/\s+/g, ' ');
|
738
|
+
// Remove non-functional whitespace.
|
739
|
+
name = name.replace(/\s+/g, ' ').trim();
|
672
740
|
|
673
741
|
// For debugging, TRACE can be used to log to the console for
|
674
742
|
// specific expressions and/or variables, for example:
|
@@ -975,23 +1043,6 @@ class ExpressionParser {
|
|
975
1043
|
return false;
|
976
1044
|
}
|
977
1045
|
}
|
978
|
-
/*
|
979
|
-
// DEPRECATED -- Modeler can deal with this by smartly using AND
|
980
|
-
// clauses like "&x: &y:" to limit set to specific prefixes.
|
981
|
-
|
982
|
-
// Deal with "prefix inheritance" when pattern starts with a colon.
|
983
|
-
if(pat.startsWith(':') && this.owner_prefix) {
|
984
|
-
// Add a "must start with" AND condition to all OR clauses of the
|
985
|
-
// pattern.
|
986
|
-
// NOTE: Issues may occur when prefix contains &, ^ or #.
|
987
|
-
// @@TO DO: See if this can be easily prohibited.
|
988
|
-
const oc = pat.substring(1).split('|');
|
989
|
-
for(let i = 0; i < oc.length; i++) {
|
990
|
-
oc[i] = `~${this.owner_prefix}&${oc[i]}`;
|
991
|
-
}
|
992
|
-
pat = oc.join('|');
|
993
|
-
}
|
994
|
-
*/
|
995
1046
|
// NOTE: For patterns, assume that # *always* denotes the context-
|
996
1047
|
// sensitive number #, because if modelers wishes to include
|
997
1048
|
// ANY number, they can make their pattern less selective.
|
@@ -1118,7 +1169,7 @@ class ExpressionParser {
|
|
1118
1169
|
}
|
1119
1170
|
// A leading "!" denotes: pass variable reference instead of its value.
|
1120
1171
|
// NOTE: This also applies to the "dot", so [!.] is a valid variable.
|
1121
|
-
|
1172
|
+
const by_reference = name.startsWith('!');
|
1122
1173
|
if(by_reference) name = name.substring(1);
|
1123
1174
|
// When `name` is a single dot, it refers to the dataset for which the
|
1124
1175
|
// modifier expression is being parsed. Like all datasets, the "dot"
|
@@ -1134,11 +1185,163 @@ class ExpressionParser {
|
|
1134
1185
|
return false;
|
1135
1186
|
}
|
1136
1187
|
// Check whether name refers to a Linny-R entity defined by the model.
|
1188
|
+
|
1189
|
+
// NOTE: When parsing the expression of a "method", variables starting
|
1190
|
+
// with a colon may be special cases.
|
1191
|
+
if(this.attribute.startsWith(':') && name.startsWith(':')) {
|
1192
|
+
// When `name` identifies a method ":m" then this method can be
|
1193
|
+
// called "as is".
|
1194
|
+
const method = MODEL.equationByID(UI.nameToID(name));
|
1195
|
+
if(method) {
|
1196
|
+
// Check for auto-reference.
|
1197
|
+
if(method.selector === this.attribute) {
|
1198
|
+
this.error = 'Method cannot reference itself';
|
1199
|
+
return false;
|
1200
|
+
}
|
1201
|
+
if(attr) {
|
1202
|
+
// Methods are expressions and hence always return a number,
|
1203
|
+
// not an entity.
|
1204
|
+
this.error = 'Method cannot have an attribute';
|
1205
|
+
return false;
|
1206
|
+
}
|
1207
|
+
// NOTE: If it has no eligible prefixes yet, the method being
|
1208
|
+
// parsed "inherits" those of an "as is" method, or should
|
1209
|
+
// intersect its eligible prefixes with the "inherited" ones.
|
1210
|
+
method.expression.compile();
|
1211
|
+
if(method.expression.compiling) {
|
1212
|
+
this.error = 'Cannot resolve method "' + method.selector +
|
1213
|
+
'" because this would create a cyclic reference';
|
1214
|
+
return false;
|
1215
|
+
}
|
1216
|
+
const
|
1217
|
+
ep = {},
|
1218
|
+
mep = method.expression.eligible_prefixes,
|
1219
|
+
prefs = Object.keys(mep);
|
1220
|
+
// NOTE: Prefix keys will always be in lower case.
|
1221
|
+
for(let i = 0; i < prefs.length; i++) {
|
1222
|
+
const pref = prefs[i];
|
1223
|
+
if(this.eligible_prefixes === null || this.eligible_prefixes[pref]) {
|
1224
|
+
ep[pref] = true;
|
1225
|
+
}
|
1226
|
+
}
|
1227
|
+
this.eligible_prefixes = ep;
|
1228
|
+
// NOTE: The method may be dynamic and/or level-dependent.
|
1229
|
+
if(!method.expression.isStatic) {
|
1230
|
+
this.is_static = false;
|
1231
|
+
this.log('dynamic because dynamic method is used');
|
1232
|
+
}
|
1233
|
+
this.is_level_based = this.is_level_based ||
|
1234
|
+
method.expression.is_level_based;
|
1235
|
+
// Generate "call method" VM instruction with no additional
|
1236
|
+
// arguments; the method equation will be applied to the object
|
1237
|
+
// of the calling method.
|
1238
|
+
return [{meq: method}, anchor1, offset1, anchor2, offset2];
|
1239
|
+
}
|
1240
|
+
// If `name` does not identify a method, it must match the "tail"
|
1241
|
+
// of some prefixed entity "prefix: name", because a method can
|
1242
|
+
// only be used as [prefix: method name] in another expression.
|
1243
|
+
// When compiling a method, a list of eligible prefixes is made.
|
1244
|
+
// This should not be empty when a method reference is parsed.
|
1245
|
+
const
|
1246
|
+
tail = UI.PREFIXER + name.substring(1).trim(),
|
1247
|
+
ee = MODEL.entitiesEndingOn(tail, attr),
|
1248
|
+
ep = {};
|
1249
|
+
for(let i = 0; i < ee.length; i++) {
|
1250
|
+
const
|
1251
|
+
en = ee[i].displayName,
|
1252
|
+
pref = en.substring(0, en.length - tail.length).toLowerCase();
|
1253
|
+
if(this.eligible_prefixes === null || this.eligible_prefixes[pref]) {
|
1254
|
+
ep[pref] = true;
|
1255
|
+
}
|
1256
|
+
}
|
1257
|
+
this.eligible_prefixes = ep;
|
1258
|
+
const uca = attr.toUpperCase();
|
1259
|
+
// Capitalize `attr` if it is a standard entity attribute.
|
1260
|
+
if(VM.attribute_names[uca]) attr = uca;
|
1261
|
+
// Add attribute to method attribute list (for post-parsing check).
|
1262
|
+
this.method_attributes.push(attr);
|
1263
|
+
if(Object.keys(this.eligible_prefixes).length <= 0) {
|
1264
|
+
const n = name + (attr ? `|${attr}` : '');
|
1265
|
+
this.error =
|
1266
|
+
`No match for variable [${n}] in this method expression`;
|
1267
|
+
return false;
|
1268
|
+
}
|
1269
|
+
// NOTE: Some attributes make the method expression level-dependent.
|
1270
|
+
this.is_level_based = this.is_level_based ||
|
1271
|
+
VM.level_based_attr.indexOf(attr) >= 0;
|
1272
|
+
// NOTE: Postpone check whether method will make the expression
|
1273
|
+
// dynamic to after the expression has been parsed and the exact
|
1274
|
+
// set of eligible entities and the set of attributes is known.
|
1275
|
+
|
1276
|
+
// Colon-prefixed variables in method expressions are similar to
|
1277
|
+
// wildcard variables, so the same VM instruction is coded for,
|
1278
|
+
// except that the entity that is the object of the method will
|
1279
|
+
// be set (as `method_object` property) for expressions that "call"
|
1280
|
+
// the method. The distinction is indicated by passing the string
|
1281
|
+
// "MO" instead of the list of eligible entities.
|
1282
|
+
if(this.TRACE) console.log('TRACE: Variable', name,
|
1283
|
+
'references the method object. Attribute used:', attr);
|
1284
|
+
return [{n: name, ee: 'MO', a: attr, br: by_reference},
|
1285
|
+
anchor1, offset1, anchor2, offset2];
|
1286
|
+
}
|
1287
|
+
|
1288
|
+
// Special "method-parsing" cases will now have been handled.
|
1289
|
+
// The other cases apply also to normal expressions.
|
1137
1290
|
if(!obj) {
|
1138
|
-
//
|
1139
|
-
//
|
1291
|
+
// If variable name starts with a colon, then the owner prefix
|
1292
|
+
// should be added.
|
1140
1293
|
name = UI.colonPrefixedName(name, this.owner_prefix);
|
1141
|
-
//
|
1294
|
+
// Now check whether the variable appends a method.
|
1295
|
+
const
|
1296
|
+
parts = name.split(UI.PREFIXER),
|
1297
|
+
tail = parts.pop();
|
1298
|
+
if(parts.length > 0) {
|
1299
|
+
// Name contains at least one prefix => last part *could* be a
|
1300
|
+
// method name, so look it up after adding a leading colon.
|
1301
|
+
const method = MODEL.equationByID(UI.nameToID(':' + tail));
|
1302
|
+
// If tail matches with a method, the head must identify an
|
1303
|
+
// entity.
|
1304
|
+
if(method) {
|
1305
|
+
const
|
1306
|
+
en = parts.join(UI.PREFIXER),
|
1307
|
+
mep = method.expression.matchWithEligiblePrefixes(en);
|
1308
|
+
if(!mep) {
|
1309
|
+
if(method.expression.compiling) {
|
1310
|
+
this.error = `Cannot resolve "${en}", possibly because ` +
|
1311
|
+
`method "${method.selector}" creates a cyclic reference`;
|
1312
|
+
} else {
|
1313
|
+
this.error = 'Method "'+ method.selector +
|
1314
|
+
`" does not apply to "${en}"`;
|
1315
|
+
}
|
1316
|
+
return false;
|
1317
|
+
}
|
1318
|
+
// NOTE: The method may be dynamic and/or level-dependent.
|
1319
|
+
if(!method.expression.isStatic) {
|
1320
|
+
this.is_static = false;
|
1321
|
+
this.log('dynamic because dynamic method is used');
|
1322
|
+
}
|
1323
|
+
this.is_level_based = this.is_level_based ||
|
1324
|
+
method.expression.is_level_based;
|
1325
|
+
// NOTE: `en` may be an incomplete identification of the object
|
1326
|
+
// of the method, which can be completed only at execution time.
|
1327
|
+
// For example: [x: m] is a valid method call when "x: a: b"
|
1328
|
+
// and "x: c" identify model entities, and the expression of
|
1329
|
+
// method "m" contains variables [:a :b] and [:c] as operands.
|
1330
|
+
// These operands code as VMI_push_dataset_modifier instructions
|
1331
|
+
// with specifier {n: name, ee: "MO"} where, for the examples
|
1332
|
+
// above, "name" would be ":a :b" and ":c". By passing `en`
|
1333
|
+
// as the "method object prefix" to VMI_push_method, this
|
1334
|
+
// instruction can set the `method_object_prefix` attribute
|
1335
|
+
// of the expression so that it can be used by the VMI_push_
|
1336
|
+
// _dataset_modifier instruction to identify the method object
|
1337
|
+
// by assembling prefix + name (with its leading colon replaced
|
1338
|
+
// by the prefixer ": ").
|
1339
|
+
return [{meq: method, mo: en}, anchor1, offset1, anchor2, offset2];
|
1340
|
+
}
|
1341
|
+
}
|
1342
|
+
}
|
1343
|
+
if(!obj) {
|
1344
|
+
// Now check wildcard equations, as these are likely to be few
|
1142
1345
|
// (so a quick scan) and constitute a special case.
|
1143
1346
|
const
|
1144
1347
|
id = UI.nameToID(name),
|
@@ -1151,7 +1354,7 @@ class ExpressionParser {
|
|
1151
1354
|
// so this equation must be evaluated for that number.
|
1152
1355
|
return [
|
1153
1356
|
{d: w[0].dataset, s: w[1], x: w[0].expression},
|
1154
|
-
anchor1, offset1, anchor2, offset2];
|
1357
|
+
anchor1, offset1, anchor2, offset2];
|
1155
1358
|
}
|
1156
1359
|
// If no match, try to match the object ID with any type of entity.
|
1157
1360
|
obj = MODEL.objectByID(id);
|
@@ -1313,8 +1516,7 @@ class ExpressionParser {
|
|
1313
1516
|
// No explicit selector means that this variable is dynamic if
|
1314
1517
|
// the dataset has time series data, or if some of its modifier
|
1315
1518
|
// expressions are dynamic.
|
1316
|
-
if(obj.
|
1317
|
-
!obj.allModifiersAreStatic) {
|
1519
|
+
if(obj.mayBeDynamic) {
|
1318
1520
|
this.is_static = false;
|
1319
1521
|
this.log('dynamic because dataset without explicit selector is used');
|
1320
1522
|
}
|
@@ -1377,19 +1579,18 @@ class ExpressionParser {
|
|
1377
1579
|
// NOTE: `arg0` can now be a single value, a vector, or NULL.
|
1378
1580
|
if(arg0 === null) arg0 = obj.attributeValue(attr);
|
1379
1581
|
if(Array.isArray(arg0)) {
|
1380
|
-
if(obj instanceof Dataset) {
|
1381
|
-
|
1382
|
-
|
1383
|
-
this.log('dynamic because dataset vector is used');
|
1384
|
-
}
|
1582
|
+
if(obj instanceof Dataset && obj.mayBeDynamic) {
|
1583
|
+
this.is_static = false;
|
1584
|
+
this.log('dynamic because dataset vector is used');
|
1385
1585
|
} else if(VM.level_based_attr.indexOf(attr) >= 0) {
|
1386
1586
|
this.is_static = false;
|
1387
1587
|
this.log('dynamic because level-based attribute');
|
1388
|
-
} else {
|
1389
|
-
//
|
1588
|
+
} else if(new Set(arg0).size > 1) {
|
1589
|
+
// Not all values qre equal => dynamic.
|
1390
1590
|
this.is_static = false;
|
1391
|
-
this.log('
|
1392
|
-
console.log('ANOMALY: array for', obj.displayName, obj, attr, arg0);
|
1591
|
+
this.log('Dynamic because array contains different values');
|
1592
|
+
// console.log('ANOMALY: array for', obj.type, obj.displayName, obj, attr, arg0);
|
1593
|
+
// console.log('Expression for', this.ownerName, '; text =', this.expr);
|
1393
1594
|
}
|
1394
1595
|
if(this.TRACE) console.log('TRACE: arg[0] is a vector');
|
1395
1596
|
}
|
@@ -1441,10 +1642,10 @@ class ExpressionParser {
|
|
1441
1642
|
}
|
1442
1643
|
|
1443
1644
|
getSymbol() {
|
1444
|
-
//
|
1645
|
+
// Get the next substring in the expression that is a valid symbol
|
1445
1646
|
// while advancing the position-in-text (`pit`) and length-of-symbol
|
1446
1647
|
// (`los`), which are used to highlight the position of a syntax error
|
1447
|
-
// in the expression editor
|
1648
|
+
// in the expression editor.
|
1448
1649
|
let c, f, i, l, v;
|
1449
1650
|
this.prev_sym = this.sym;
|
1450
1651
|
this.sym = null;
|
@@ -1652,35 +1853,35 @@ class ExpressionParser {
|
|
1652
1853
|
}
|
1653
1854
|
|
1654
1855
|
compile() {
|
1655
|
-
//
|
1656
|
-
// NOTE:
|
1657
|
-
// become the code attribute of an expression object
|
1856
|
+
// Compile expression into array of VM instructions `code`.
|
1857
|
+
// NOTE: Always create a new code array instance, as it will typically
|
1858
|
+
// become the code attribute of an expression object.
|
1658
1859
|
if(DEBUGGING) console.log('COMPILING', this.ownerName, ':\n',
|
1659
1860
|
this.expr, '\ncontext number =', this.context_number);
|
1660
1861
|
this.code = [];
|
1661
|
-
// Position in text
|
1862
|
+
// Position in text.
|
1662
1863
|
this.pit = 0;
|
1663
|
-
// Length of symbol
|
1864
|
+
// Length of symbol.
|
1664
1865
|
this.los = 0;
|
1665
|
-
// Error message also serves as flag: stop compiling if not empty
|
1866
|
+
// Error message also serves as flag: stop compiling if not empty.
|
1666
1867
|
this.error = '';
|
1667
|
-
// `is_static` becomes FALSE when a time-dependent operand is detected
|
1868
|
+
// `is_static` becomes FALSE when a time-dependent operand is detected.
|
1668
1869
|
this.is_static = true;
|
1669
|
-
// `is_level_based` becomes TRUE when a level-based variable is detected
|
1870
|
+
// `is_level_based` becomes TRUE when a level-based variable is detected.
|
1670
1871
|
this.is_level_based = false;
|
1671
|
-
// `concatenating` becomes TRUE when a concatenation operator
|
1672
|
-
// is pushed, and FALSE when a reducing operator (min, max,
|
1673
|
-
// triangular) is pushed
|
1872
|
+
// `concatenating` becomes TRUE when a concatenation operator
|
1873
|
+
// (semicolon) is pushed, and FALSE when a reducing operator (min, max,
|
1874
|
+
// normal, weibull, triangular) is pushed.
|
1674
1875
|
this.concatenating = false;
|
1675
|
-
// An empty expression should return the "undefined" value
|
1876
|
+
// An empty expression should return the "undefined" value.
|
1676
1877
|
if(this.expr.trim() === '') {
|
1677
1878
|
this.code.push([VMI_push_number, VM.UNDEFINED]);
|
1678
1879
|
return;
|
1679
1880
|
}
|
1680
|
-
// Parse the expression using Edsger Dijkstra's shunting-yard algorithm
|
1681
|
-
// vmi = virtual machine instruction (a function)
|
1881
|
+
// Parse the expression using Edsger Dijkstra's shunting-yard algorithm.
|
1882
|
+
// vmi = virtual machine instruction (a function).
|
1682
1883
|
let vmi;
|
1683
|
-
// eot = end of text (index of last character in string)
|
1884
|
+
// eot = end of text (index of last character in string).
|
1684
1885
|
this.eot = this.expr.length - 1;
|
1685
1886
|
this.sym = null; // current symbol
|
1686
1887
|
this.prev_sym = null; // previous symbol
|
@@ -1693,27 +1894,27 @@ class ExpressionParser {
|
|
1693
1894
|
this.getSymbol();
|
1694
1895
|
if(this.error !== '') break;
|
1695
1896
|
if(this.sym === '(') {
|
1696
|
-
// Opening parenthesis is ALWAYS pushed onto the stack
|
1897
|
+
// Opening parenthesis is ALWAYS pushed onto the stack.
|
1697
1898
|
this.op_stack.push(this.sym);
|
1698
1899
|
} else if(this.sym === ')') {
|
1699
1900
|
// Closing parenthesis => pop all operators until its matching
|
1700
|
-
// opening parenthesis is found
|
1901
|
+
// opening parenthesis is found.
|
1701
1902
|
if(this.op_stack.indexOf('(') < 0) {
|
1702
1903
|
this.error = 'Unmatched \')\'';
|
1703
1904
|
} else if(this.prev_sym === '(' ||
|
1704
1905
|
OPERATOR_CODES.indexOf(this.prev_sym) >= 0) {
|
1705
|
-
// Parenthesis immediately after an operator => missing operand
|
1906
|
+
// Parenthesis immediately after an operator => missing operand.
|
1706
1907
|
this.error = 'Missing operand';
|
1707
1908
|
} else {
|
1708
|
-
// Pop all operators up to and including the matching parenthesis
|
1909
|
+
// Pop all operators up to and including the matching parenthesis.
|
1709
1910
|
vmi = null;
|
1710
1911
|
while(this.op_stack.length > 0 &&
|
1711
1912
|
this.op_stack[this.op_stack.length - 1] !== '(') {
|
1712
|
-
// Pop the operator
|
1913
|
+
// Pop the operator.
|
1713
1914
|
vmi = this.op_stack.pop();
|
1714
1915
|
this.codeOperation(vmi);
|
1715
1916
|
}
|
1716
|
-
// Also pop the opening parenthesis
|
1917
|
+
// Also pop the opening parenthesis.
|
1717
1918
|
this.op_stack.pop();
|
1718
1919
|
}
|
1719
1920
|
} else if(this.sym === VMI_if_else &&
|
@@ -1725,13 +1926,13 @@ class ExpressionParser {
|
|
1725
1926
|
this.op_stack[this.op_stack.length - 1] : null),
|
1726
1927
|
topprio = PRIORITIES[OPERATOR_CODES.indexOf(topop)],
|
1727
1928
|
symprio = PRIORITIES[OPERATOR_CODES.indexOf(this.sym)];
|
1728
|
-
// Pop all operators having a higher or equal priority than the
|
1729
|
-
// to be pushed EXCEPT when this priority equals 9, as monadic
|
1730
|
-
// bind right-to-left
|
1929
|
+
// Pop all operators having a higher or equal priority than the
|
1930
|
+
// one to be pushed EXCEPT when this priority equals 9, as monadic
|
1931
|
+
// operators bind right-to-left.
|
1731
1932
|
while(this.op_stack.length > 0 && OPERATOR_CODES.indexOf(topop) >= 0 &&
|
1732
1933
|
topprio >= symprio && symprio !== 9) {
|
1733
1934
|
// The stack may be emptied, but if it contains a (, this
|
1734
|
-
// parenthesis is unmatched
|
1935
|
+
// parenthesis is unmatched.
|
1735
1936
|
if(topop === '(') {
|
1736
1937
|
this.error = 'Missing \')\'';
|
1737
1938
|
} else {
|
@@ -1747,27 +1948,27 @@ class ExpressionParser {
|
|
1747
1948
|
}
|
1748
1949
|
}
|
1749
1950
|
|
1750
|
-
// NOTE:
|
1951
|
+
// NOTE: As of version 1.0.14, (a ? b : c) is implemented with
|
1751
1952
|
// "jump"-instructions so that only b OR c is evaluated instead
|
1752
|
-
// of both
|
1953
|
+
// of both.
|
1753
1954
|
if(this.sym === VMI_if_then) {
|
1754
1955
|
// Push index of JUMP-IF-FALSE instruction on if_stack so that
|
1755
1956
|
// later its dummy argument (NULL) can be replaced by the
|
1756
|
-
// index of the first instruction after the THEN part
|
1957
|
+
// index of the first instruction after the THEN part.
|
1757
1958
|
this.if_stack.push(this.code.length);
|
1758
1959
|
this.code.push([VMI_jump_if_false, null]);
|
1759
1960
|
} else if(this.sym === VMI_if_else) {
|
1760
1961
|
this.then_stack.push(this.code.length);
|
1761
1962
|
this.code.push([VMI_jump, null]);
|
1762
|
-
// NOTE:
|
1763
|
-
// start by popping the FALSE result of the IF condition
|
1963
|
+
// NOTE: If : is not omitted, the code for the ELSE part must
|
1964
|
+
// start by popping the FALSE result of the IF condition.
|
1764
1965
|
this.code.push([VMI_pop_false, null]);
|
1765
1966
|
}
|
1766
1967
|
// END of new code for IF-THEN-ELSE
|
1767
1968
|
|
1768
1969
|
this.op_stack.push(this.sym);
|
1769
1970
|
} else if(this.sym !== null) {
|
1770
|
-
// Symbol is an operand
|
1971
|
+
// Symbol is an operand.
|
1771
1972
|
if(CONSTANT_CODES.indexOf(this.sym) >= 0) {
|
1772
1973
|
this.code.push([this.sym, null]);
|
1773
1974
|
} else if(Array.isArray(this.sym)) {
|
@@ -1778,6 +1979,8 @@ class ExpressionParser {
|
|
1778
1979
|
this.code.push([VMI_push_statistic, this.sym]);
|
1779
1980
|
} else if(this.sym[0].hasOwnProperty('d')) {
|
1780
1981
|
this.code.push([VMI_push_dataset_modifier, this.sym]);
|
1982
|
+
} else if(this.sym[0].hasOwnProperty('meq')) {
|
1983
|
+
this.code.push([VMI_push_method, this.sym]);
|
1781
1984
|
} else if(this.sym[0].hasOwnProperty('ee')) {
|
1782
1985
|
this.code.push([VMI_push_wildcard_entity, this.sym]);
|
1783
1986
|
} else if(this.sym[0].hasOwnProperty('x')) {
|
@@ -1811,6 +2014,21 @@ class ExpressionParser {
|
|
1811
2014
|
this.error = 'Invalid parameter list';
|
1812
2015
|
}
|
1813
2016
|
}
|
2017
|
+
// When compiling a method, check for all eligible prefixes whether
|
2018
|
+
// they might make the expression dynamic.
|
2019
|
+
if(this.is_static && this.eligible_prefixes) {
|
2020
|
+
const epl = Object.keys(this.eligible_prefixes);
|
2021
|
+
for(let i = 0; i < epl.length; i++) {
|
2022
|
+
const ep = epl[i];
|
2023
|
+
for(let j = 0; j < ep.length; j++) {
|
2024
|
+
if(ep[j] instanceof Dataset && ep[j].mayBeDynamic) {
|
2025
|
+
this.is_static = false;
|
2026
|
+
this.log('dynamic because some modifiers of eligible datasets are dynamic');
|
2027
|
+
break;
|
2028
|
+
}
|
2029
|
+
}
|
2030
|
+
}
|
2031
|
+
}
|
1814
2032
|
if(this.TRACE || DEBUGGING) console.log('PARSED', this.ownerName, ':',
|
1815
2033
|
this.expr, this.code);
|
1816
2034
|
}
|
@@ -2741,7 +2959,7 @@ class VirtualMachine {
|
|
2741
2959
|
return prior_level;
|
2742
2960
|
}
|
2743
2961
|
|
2744
|
-
variablesLegend(
|
2962
|
+
variablesLegend() {
|
2745
2963
|
// Return a string with each variable code and full name on a
|
2746
2964
|
// separate line.
|
2747
2965
|
const
|
@@ -4101,7 +4319,7 @@ class VirtualMachine {
|
|
4101
4319
|
// `cbl` is the cropped block length (applies only to last block).
|
4102
4320
|
let bb = (block - 1) * MODEL.block_length + 1,
|
4103
4321
|
abl = this.chunk_length,
|
4104
|
-
cbl = this.actualBlockLength;
|
4322
|
+
cbl = this.actualBlockLength(block);
|
4105
4323
|
// For the last block, crop the actual block length so it does not
|
4106
4324
|
// extend beyond the simulation period (these results should be ignored).
|
4107
4325
|
// If no results computed, preserve those already computed for the
|
@@ -4543,7 +4761,7 @@ class VirtualMachine {
|
|
4543
4761
|
|
4544
4762
|
setupBlock() {
|
4545
4763
|
if(DEBUGGING) this.logCode();
|
4546
|
-
const abl = this.actualBlockLength;
|
4764
|
+
const abl = this.actualBlockLength(this.block_count);
|
4547
4765
|
// NOTE: Tableau segment length is the number of time steps between
|
4548
4766
|
// updates of the progress needle. The default progress needle interval
|
4549
4767
|
// is calibrated for 1000 VMI instructions.
|
@@ -4737,12 +4955,12 @@ class VirtualMachine {
|
|
4737
4955
|
setTimeout(() => VM.solveBlock(), 0);
|
4738
4956
|
}
|
4739
4957
|
|
4740
|
-
|
4958
|
+
actualBlockLength(block) {
|
4741
4959
|
// The actual block length is the number of time steps to be considered
|
4742
4960
|
// by the solver; the abl of the last block is likely to be shorter
|
4743
4961
|
// than the standard, as it should not go beyond the end time plus
|
4744
4962
|
// look-ahead.
|
4745
|
-
if(
|
4963
|
+
if(block < this.nr_of_blocks) return this.chunk_length;
|
4746
4964
|
// Last block length equals remainder of simulation period divided
|
4747
4965
|
// by block length.
|
4748
4966
|
let rem = (MODEL.runLength - MODEL.look_ahead) % MODEL.block_length;
|
@@ -4787,7 +5005,7 @@ class VirtualMachine {
|
|
4787
5005
|
// behavior can still be generated by limiting time series length to
|
4788
5006
|
// the simulation period.
|
4789
5007
|
const
|
4790
|
-
abl = this.actualBlockLength,
|
5008
|
+
abl = this.actualBlockLength(this.block_count),
|
4791
5009
|
// Get the number digits for variable names
|
4792
5010
|
z = this.columnsInBlock.toString().length,
|
4793
5011
|
// LP_solve uses semicolon as separator between equations
|
@@ -5022,7 +5240,7 @@ class VirtualMachine {
|
|
5022
5240
|
// instead of row-based, hence for each column a separate string list.
|
5023
5241
|
// NOTE: Columns are numbered from 1 to N, hence a dummy list for c=0.
|
5024
5242
|
const
|
5025
|
-
abl = this.actualBlockLength,
|
5243
|
+
abl = this.actualBlockLength(this.block_count),
|
5026
5244
|
cols = [[]],
|
5027
5245
|
rhs = [];
|
5028
5246
|
let nrow = this.matrix.length,
|
@@ -5030,7 +5248,7 @@ class VirtualMachine {
|
|
5030
5248
|
c,
|
5031
5249
|
p,
|
5032
5250
|
r;
|
5033
|
-
|
5251
|
+
this.numeric_issue = '';
|
5034
5252
|
this.lines = '';
|
5035
5253
|
for(c = 1; c <= ncol; c++) cols.push([]);
|
5036
5254
|
this.decimals = Math.max(nrow, ncol).toString().length;
|
@@ -5204,7 +5422,7 @@ class VirtualMachine {
|
|
5204
5422
|
// Add the SOS section.
|
5205
5423
|
if(this.sos_var_indices.length > 0) {
|
5206
5424
|
this.lines += 'SOS\n';
|
5207
|
-
const abl = this.actualBlockLength;
|
5425
|
+
const abl = this.actualBlockLength(this.block_count);
|
5208
5426
|
let sos = 1;
|
5209
5427
|
for(let j = 0; j < abl; j++) {
|
5210
5428
|
for(let i = 0; i < this.sos_var_indices.length; i++) {
|
@@ -5376,7 +5594,7 @@ Solver status = ${json.status}`);
|
|
5376
5594
|
const
|
5377
5595
|
bwr = this.blockWithRound,
|
5378
5596
|
fromt = (this.block_count - 1) * MODEL.block_length + 1,
|
5379
|
-
abl = this.actualBlockLength;
|
5597
|
+
abl = this.actualBlockLength(this.block_count);
|
5380
5598
|
MONITOR.updateBlockNumber(bwr);
|
5381
5599
|
// NOTE: Add blank line to message to visually separate rounds.
|
5382
5600
|
this.logMessage(this.block_count, ['\nSetting up block #', bwr,
|
@@ -5408,7 +5626,8 @@ Solver status = ${json.status}`);
|
|
5408
5626
|
}
|
5409
5627
|
// Generate lines of code in format that should be accepted by solver.
|
5410
5628
|
if(this.solver_name === 'gurobi') {
|
5411
|
-
this.writeMPSFormat();
|
5629
|
+
//this.writeMPSFormat();
|
5630
|
+
this.writeLpFormat(true);
|
5412
5631
|
} else if(this.solver_name === 'scip' || this.solver_name === 'cplex') {
|
5413
5632
|
// NOTE: The CPLEX LP format that is also used by SCIP differs from
|
5414
5633
|
// the LP_solve format that was used by the first versions of Linny-R.
|
@@ -5533,96 +5752,99 @@ Solver status = ${json.status}`);
|
|
5533
5752
|
// automaton instruction has parameters x and a, where x is the computing
|
5534
5753
|
// expression and a the argument, which may be a single number or a list
|
5535
5754
|
// (array) of objects. When no arguments need to be passed, the second
|
5536
|
-
// parameter is
|
5755
|
+
// parameter is omitted.
|
5537
5756
|
|
5538
5757
|
function VMI_push_number(x, number) {
|
5539
|
-
//
|
5758
|
+
// Push a numeric constant on the VM stack.
|
5540
5759
|
if(DEBUGGING) console.log('push number = ' + number);
|
5541
5760
|
x.push(number);
|
5542
5761
|
}
|
5543
5762
|
|
5544
|
-
function VMI_push_time_step(x
|
5545
|
-
//
|
5546
|
-
// NOTE:
|
5547
|
-
// starts at 1), adjusted for the first time step of the simulation period
|
5763
|
+
function VMI_push_time_step(x) {
|
5764
|
+
// Push the current time step.
|
5765
|
+
// NOTE: This is the "local" time step for expression `x` (which always
|
5766
|
+
// starts at 1), adjusted for the first time step of the simulation period.
|
5548
5767
|
const t = x.step[x.step.length - 1] + MODEL.start_period - 1;
|
5549
5768
|
if(DEBUGGING) console.log('push absolute t = ' + t);
|
5550
5769
|
x.push(t);
|
5551
5770
|
}
|
5552
5771
|
|
5553
|
-
function VMI_push_delta_t(x
|
5554
|
-
//
|
5772
|
+
function VMI_push_delta_t(x) {
|
5773
|
+
// Push the duration of 1 time step (in hours).
|
5555
5774
|
const dt = MODEL.time_scale * VM.time_unit_values[MODEL.time_unit];
|
5556
5775
|
if(DEBUGGING) console.log('push delta-t = ' + dt);
|
5557
5776
|
x.push(dt);
|
5558
5777
|
}
|
5559
5778
|
|
5560
|
-
function VMI_push_relative_time(x
|
5561
|
-
//
|
5779
|
+
function VMI_push_relative_time(x) {
|
5780
|
+
// Push the "local" time step for expression `x`.
|
5781
|
+
// NOTE: Time step for optimization period always starts at 1.
|
5562
5782
|
const t = x.step[x.step.length - 1];
|
5563
5783
|
if(DEBUGGING) console.log('push relative t = ' + t);
|
5564
5784
|
x.push(t);
|
5565
5785
|
}
|
5566
5786
|
|
5567
|
-
function VMI_push_block_time(x
|
5568
|
-
//
|
5569
|
-
// adjusted for the first time step of the current block
|
5570
|
-
const
|
5571
|
-
|
5572
|
-
|
5787
|
+
function VMI_push_block_time(x) {
|
5788
|
+
// Push the "local" time step for expression `x` (which always starts
|
5789
|
+
// at 1) adjusted for the first time step of the current block.
|
5790
|
+
const
|
5791
|
+
lt = x.step[x.step.length - 1] - 1,
|
5792
|
+
bnr = Math.floor(lt / MODEL.block_length),
|
5793
|
+
t = lt - bnr * MODEL.block_length + 1;
|
5573
5794
|
if(DEBUGGING) console.log('push block time bt = ' + t);
|
5574
5795
|
x.push(t);
|
5575
5796
|
}
|
5576
5797
|
|
5577
|
-
function VMI_push_block_number(x
|
5578
|
-
//
|
5798
|
+
function VMI_push_block_number(x) {
|
5799
|
+
// Push the number of the block currently being optimized.
|
5800
|
+
// NOTE: Block numbering starts at 1.
|
5579
5801
|
const local_t = x.step[x.step.length - 1] - 1,
|
5580
5802
|
bnr = Math.floor(local_t / MODEL.block_length) + 1;
|
5581
5803
|
if(DEBUGGING) console.log('push current block number = ' + bnr);
|
5582
5804
|
x.push(bnr);
|
5583
5805
|
}
|
5584
5806
|
|
5585
|
-
function VMI_push_run_length(x
|
5586
|
-
//
|
5807
|
+
function VMI_push_run_length(x) {
|
5808
|
+
// Push the run length (excl. look-ahead!).
|
5587
5809
|
const n = MODEL.end_period - MODEL.start_period + 1;
|
5588
5810
|
if(DEBUGGING) console.log('push run length N = ' + n);
|
5589
5811
|
x.push(n);
|
5590
5812
|
}
|
5591
5813
|
|
5592
|
-
function VMI_push_block_length(x
|
5593
|
-
//
|
5814
|
+
function VMI_push_block_length(x) {
|
5815
|
+
// Push the block length.
|
5594
5816
|
if(DEBUGGING) console.log('push block length n = ' + MODEL.block_length);
|
5595
5817
|
x.push(MODEL.block_length);
|
5596
5818
|
}
|
5597
5819
|
|
5598
|
-
function VMI_push_look_ahead(x
|
5599
|
-
//
|
5820
|
+
function VMI_push_look_ahead(x) {
|
5821
|
+
// Push the look-ahead.
|
5600
5822
|
if(DEBUGGING) console.log('push look-ahead l = ' + MODEL.look_ahead);
|
5601
5823
|
x.push(MODEL.look_ahead);
|
5602
5824
|
}
|
5603
5825
|
|
5604
|
-
function VMI_push_round(x
|
5605
|
-
//
|
5826
|
+
function VMI_push_round(x) {
|
5827
|
+
// Push the current round number (a=1, z=26, etc.).
|
5606
5828
|
const r = VM.round_letters.indexOf(VM.round_sequence[VM.current_round]);
|
5607
5829
|
if(DEBUGGING) console.log('push round number R = ' + r);
|
5608
5830
|
x.push(r);
|
5609
5831
|
}
|
5610
5832
|
|
5611
|
-
function VMI_push_last_round(x
|
5612
|
-
//
|
5833
|
+
function VMI_push_last_round(x) {
|
5834
|
+
// Push the last round number (a=1, z=26, etc.).
|
5613
5835
|
const r = VM.round_letters.indexOf(VM.round_sequence[MODEL.rounds - 1]);
|
5614
5836
|
if(DEBUGGING) console.log('push last round number LR = ' + r);
|
5615
5837
|
x.push(r);
|
5616
5838
|
}
|
5617
5839
|
|
5618
|
-
function VMI_push_number_of_rounds(x
|
5619
|
-
//
|
5840
|
+
function VMI_push_number_of_rounds(x) {
|
5841
|
+
// Push the number of rounds (= length of round sequence).
|
5620
5842
|
if(DEBUGGING) console.log('push number of rounds NR = ' + MODEL.rounds);
|
5621
5843
|
x.push(MODEL.rounds);
|
5622
5844
|
}
|
5623
5845
|
|
5624
|
-
function VMI_push_run_number(x
|
5625
|
-
//
|
5846
|
+
function VMI_push_run_number(x) {
|
5847
|
+
// Push the number of the current run in the selected experiment (or 0).
|
5626
5848
|
const
|
5627
5849
|
sx = EXPERIMENT_MANAGER.selected_experiment,
|
5628
5850
|
nox = (sx ? ` (in ${sx.title})` : ' (no experiment)'),
|
@@ -5631,8 +5853,8 @@ function VMI_push_run_number(x, empty) {
|
|
5631
5853
|
x.push(xr);
|
5632
5854
|
}
|
5633
5855
|
|
5634
|
-
function VMI_push_number_of_runs(x
|
5635
|
-
//
|
5856
|
+
function VMI_push_number_of_runs(x) {
|
5857
|
+
// Push the number of runs in the current experiment (0 if no experiment).
|
5636
5858
|
const
|
5637
5859
|
sx = EXPERIMENT_MANAGER.selected_experiment,
|
5638
5860
|
nox = (sx ? `(in ${sx.title})` : '(no experiment)'),
|
@@ -5641,40 +5863,41 @@ function VMI_push_number_of_runs(x, empty) {
|
|
5641
5863
|
x.push(nx);
|
5642
5864
|
}
|
5643
5865
|
|
5644
|
-
function VMI_push_random(x
|
5645
|
-
//
|
5866
|
+
function VMI_push_random(x) {
|
5867
|
+
// Push a random number from the interval [0, 1).
|
5646
5868
|
const r = Math.random();
|
5647
5869
|
if(DEBUGGING) console.log('push random =', r);
|
5648
5870
|
x.push(r);
|
5649
5871
|
}
|
5650
5872
|
|
5651
|
-
function VMI_push_pi(x
|
5652
|
-
//
|
5873
|
+
function VMI_push_pi(x) {
|
5874
|
+
// Push the goniometric constant pi.
|
5653
5875
|
if(DEBUGGING) console.log('push pi');
|
5654
5876
|
x.push(Math.PI);
|
5655
5877
|
}
|
5656
5878
|
|
5657
|
-
function VMI_push_true(x
|
5658
|
-
//
|
5879
|
+
function VMI_push_true(x) {
|
5880
|
+
// Push the Boolean constant TRUE.
|
5659
5881
|
if(DEBUGGING) console.log('push TRUE');
|
5660
5882
|
x.push(1);
|
5661
5883
|
}
|
5662
5884
|
|
5663
|
-
function VMI_push_false(x
|
5664
|
-
//
|
5885
|
+
function VMI_push_false(x) {
|
5886
|
+
// Push the Boolean constant FALSE.
|
5665
5887
|
if(DEBUGGING) console.log('push FALSE');
|
5666
5888
|
x.push(0);
|
5667
5889
|
}
|
5668
5890
|
|
5669
|
-
function VMI_push_infinity(x
|
5670
|
-
//
|
5891
|
+
function VMI_push_infinity(x) {
|
5892
|
+
// Push the constant representing infinity for the solver.
|
5671
5893
|
if(DEBUGGING) console.log('push +INF');
|
5672
5894
|
x.push(VM.PLUS_INFINITY);
|
5673
5895
|
}
|
5674
5896
|
|
5675
5897
|
function valueOfIndexVariable(v) {
|
5676
|
-
// AUXILIARY FUNCTION for the VMI_push_(i, j or k) instructions
|
5677
|
-
//
|
5898
|
+
// AUXILIARY FUNCTION for the VMI_push_(i, j or k) instructions.
|
5899
|
+
// Return the value of the iterator index variable for the current
|
5900
|
+
// experiment.
|
5678
5901
|
if(MODEL.running_experiment) {
|
5679
5902
|
const
|
5680
5903
|
lead = v + '=',
|
@@ -5687,68 +5910,69 @@ function valueOfIndexVariable(v) {
|
|
5687
5910
|
return 0;
|
5688
5911
|
}
|
5689
5912
|
|
5690
|
-
function VMI_push_i(x
|
5691
|
-
//
|
5913
|
+
function VMI_push_i(x) {
|
5914
|
+
// Push the value of iterator index i.
|
5692
5915
|
const i = valueOfIndexVariable('i');
|
5693
5916
|
if(DEBUGGING) console.log('push i = ' + i);
|
5694
5917
|
x.push(i);
|
5695
5918
|
}
|
5696
5919
|
|
5697
|
-
function VMI_push_j(x
|
5698
|
-
//
|
5920
|
+
function VMI_push_j(x) {
|
5921
|
+
// Push the value of iterator index j.
|
5699
5922
|
const j = valueOfIndexVariable('j');
|
5700
5923
|
if(DEBUGGING) console.log('push j = ' + j);
|
5701
5924
|
x.push(j);
|
5702
5925
|
}
|
5703
5926
|
|
5704
|
-
function VMI_push_k(x
|
5705
|
-
//
|
5927
|
+
function VMI_push_k(x) {
|
5928
|
+
// Push the value of iterator index k.
|
5706
5929
|
const k = valueOfIndexVariable('k');
|
5707
5930
|
if(DEBUGGING) console.log('push k = ' + k);
|
5708
5931
|
x.push(k);
|
5709
5932
|
}
|
5710
5933
|
|
5711
5934
|
function pushTimeStepsPerTimeUnit(x, unit) {
|
5712
|
-
// AUXILIARY FUNCTION for the VMI_push_(time unit) instructions
|
5713
|
-
//
|
5935
|
+
// AUXILIARY FUNCTION for the VMI_push_(time unit) instructions.
|
5936
|
+
// Push the number of model time steps represented by 1 unit.
|
5937
|
+
// NOTE: This will typically be a real number -- no rounding.
|
5714
5938
|
const t = VM.time_unit_values[unit] / MODEL.time_scale /
|
5715
5939
|
VM.time_unit_values[MODEL.time_unit];
|
5716
5940
|
if(DEBUGGING) console.log(`push ${unit} = ${VM.sig4Dig(t)}`);
|
5717
5941
|
x.push(t);
|
5718
5942
|
}
|
5719
5943
|
|
5720
|
-
function VMI_push_year(x
|
5721
|
-
//
|
5944
|
+
function VMI_push_year(x) {
|
5945
|
+
// Push the number of time steps in one year.
|
5722
5946
|
pushTimeStepsPerTimeUnit(x, 'year');
|
5723
5947
|
}
|
5724
5948
|
|
5725
|
-
function VMI_push_week(x
|
5726
|
-
//
|
5949
|
+
function VMI_push_week(x) {
|
5950
|
+
// Push the number of time steps in one week.
|
5727
5951
|
pushTimeStepsPerTimeUnit(x, 'week');
|
5728
5952
|
}
|
5729
5953
|
|
5730
|
-
function VMI_push_day(x
|
5731
|
-
//
|
5954
|
+
function VMI_push_day(x) {
|
5955
|
+
// Push the number of time steps in one day.
|
5732
5956
|
pushTimeStepsPerTimeUnit(x, 'day');
|
5733
5957
|
}
|
5734
5958
|
|
5735
|
-
function VMI_push_hour(x
|
5736
|
-
//
|
5959
|
+
function VMI_push_hour(x) {
|
5960
|
+
// Push the number of time steps in one hour.
|
5737
5961
|
pushTimeStepsPerTimeUnit(x, 'hour');
|
5738
5962
|
}
|
5739
5963
|
|
5740
|
-
function VMI_push_minute(x
|
5741
|
-
//
|
5964
|
+
function VMI_push_minute(x) {
|
5965
|
+
// Push the number of time steps in one minute.
|
5742
5966
|
pushTimeStepsPerTimeUnit(x, 'minute');
|
5743
5967
|
}
|
5744
5968
|
|
5745
|
-
function VMI_push_second(x
|
5746
|
-
//
|
5969
|
+
function VMI_push_second(x) {
|
5970
|
+
// Push the number of time steps in one second.
|
5747
5971
|
pushTimeStepsPerTimeUnit(x, 'second');
|
5748
5972
|
}
|
5749
5973
|
|
5750
|
-
function VMI_push_contextual_number(x
|
5751
|
-
//
|
5974
|
+
function VMI_push_contextual_number(x) {
|
5975
|
+
// Push the numeric value of the context-sensitive number #.
|
5752
5976
|
const n = valueOfNumberSign(x);
|
5753
5977
|
if(DEBUGGING) {
|
5754
5978
|
console.log('push contextual number: # = ' + VM.sig2Dig(n));
|
@@ -5759,11 +5983,12 @@ function VMI_push_contextual_number(x, empty) {
|
|
5759
5983
|
/* VM instruction helper functions */
|
5760
5984
|
|
5761
5985
|
function valueOfNumberSign(x) {
|
5762
|
-
//
|
5763
|
-
// NOTE:
|
5764
|
-
// ending on digits, or
|
5765
|
-
// is the number its name or any of its prefixes ends on, but
|
5766
|
-
// more "creative" and can return the number context of nearby
|
5986
|
+
// Push the numeric value of the # sign for the context of expression `x`.
|
5987
|
+
// NOTE: This can be a wildcard match, an active experiment run selector
|
5988
|
+
// ending on digits, or the number context of an entity. The latter
|
5989
|
+
// typically is the number its name or any of its prefixes ends on, but
|
5990
|
+
// notes are more "creative" and can return the number context of nearby
|
5991
|
+
// entities.
|
5767
5992
|
let s = '!NO SELECTOR',
|
5768
5993
|
m = '!NO MATCH',
|
5769
5994
|
n = VM.UNDEFINED;
|
@@ -5792,7 +6017,7 @@ function valueOfNumberSign(x) {
|
|
5792
6017
|
}
|
5793
6018
|
}
|
5794
6019
|
// If selector contains no wildcards, get number context (typically
|
5795
|
-
// inferred from a number in the name of the object)
|
6020
|
+
// inferred from a number in the name of the object).
|
5796
6021
|
if(s.indexOf('*') < 0 && s.indexOf('?') < 0) {
|
5797
6022
|
const d = x.object.numberContext;
|
5798
6023
|
if(d) {
|
@@ -5802,7 +6027,7 @@ function valueOfNumberSign(x) {
|
|
5802
6027
|
}
|
5803
6028
|
}
|
5804
6029
|
}
|
5805
|
-
// For datasets, set the parent anchor to be the context-sensitive number
|
6030
|
+
// For datasets, set the parent anchor to be the context-sensitive number.
|
5806
6031
|
if(x.object instanceof Dataset) x.object.parent_anchor = n;
|
5807
6032
|
if(DEBUGGING) {
|
5808
6033
|
console.log(`context for # in expression for ${x.variableName}
|
@@ -5813,7 +6038,7 @@ function valueOfNumberSign(x) {
|
|
5813
6038
|
}
|
5814
6039
|
|
5815
6040
|
function relativeTimeStep(t, anchor, offset, dtm, x) {
|
5816
|
-
//
|
6041
|
+
// Return the relative time step, given t, anchor, offset,
|
5817
6042
|
// delta-t-multiplier and the expression being evaluated (to provide
|
5818
6043
|
// context for anchor #).
|
5819
6044
|
// NOTE: t = 1 corresponds with first time step of simulation period.
|
@@ -5833,7 +6058,7 @@ function relativeTimeStep(t, anchor, offset, dtm, x) {
|
|
5833
6058
|
if(DEBUGGING) {
|
5834
6059
|
console.log('Parent anchor', x.object.parent_anchor);
|
5835
6060
|
}
|
5836
|
-
// NOTE: For not array-type datasets, ^ is equivalent to
|
6061
|
+
// NOTE: For not array-type datasets, ^ is equivalent to #.
|
5837
6062
|
return x.object.parent_anchor;
|
5838
6063
|
}
|
5839
6064
|
return valueOfNumberSign(x) + offset;
|
@@ -5872,17 +6097,18 @@ function relativeTimeStep(t, anchor, offset, dtm, x) {
|
|
5872
6097
|
}
|
5873
6098
|
|
5874
6099
|
function twoOffsetTimeStep(t, a1, o1, a2, o2, dtm, x) {
|
5875
|
-
//
|
5876
|
-
// are anchor-offset shorthand for the debugging message,
|
5877
|
-
//
|
5878
|
-
//
|
5879
|
-
//
|
6100
|
+
// Return the list [rt, ao1, ao2] where `rt` is the time step, and
|
6101
|
+
// `ao1` and `ao2` are anchor-offset shorthand for the debugging message,
|
6102
|
+
// given `t`, the two anchors plus offsets, and the delta-t-multiplier.
|
6103
|
+
// NOTES:
|
6104
|
+
// (1) `dtm` will differ from 1 only for experiment results.
|
6105
|
+
// (2) Expression `x` is passed to provide context for evaluation of #.
|
5880
6106
|
let t1 = relativeTimeStep(t, a1, o1, dtm, x),
|
5881
6107
|
ao1 = [' @ ', a1, (o1 > 0 ? '+' : ''), (o1 ? o1 : ''),
|
5882
6108
|
' = ', t1].join(''),
|
5883
6109
|
ao2 = '';
|
5884
6110
|
if(o2 !== o1 || a2 !== a1) {
|
5885
|
-
// Two different offsets => use the midpoint as time (NO aggregation!)
|
6111
|
+
// Two different offsets => use the midpoint as time (NO aggregation!).
|
5886
6112
|
const t2 = relativeTimeStep(t, a2, o2, dtm, x);
|
5887
6113
|
ao2 = [' : ', a2, (o2 > 0 ? '+' : ''), (o2 ? o2 : ''), ' = ', t2].join('');
|
5888
6114
|
t1 = Math.floor((t1 + t2) / 2);
|
@@ -5894,51 +6120,51 @@ function twoOffsetTimeStep(t, a1, o1, a2, o2, dtm, x) {
|
|
5894
6120
|
/* VM instructions (continued) */
|
5895
6121
|
|
5896
6122
|
function VMI_push_var(x, args) {
|
5897
|
-
//
|
6123
|
+
// Push the value of the variable specified by `args`, being the list
|
5898
6124
|
// [obj, anchor1, offset1, anchor2, offset2] where `obj` can be a vector
|
5899
|
-
// or an expression, or a cluster unit balance specifier
|
6125
|
+
// or an expression, or a cluster unit balance specifier.
|
5900
6126
|
const
|
5901
6127
|
obj = args[0],
|
5902
|
-
// NOTE:
|
6128
|
+
// NOTE: Use the "local" time step for expression `x`.
|
5903
6129
|
tot = twoOffsetTimeStep(x.step[x.step.length - 1],
|
5904
6130
|
args[1], args[2], args[3], args[4], 1, x);
|
5905
6131
|
let t = tot[0];
|
5906
|
-
// Negative time step is evaluated as t = 0 (initial value), while t
|
5907
|
-
// optimization period is evaluated as its last time step
|
5908
|
-
// used in a self-referencing variable
|
6132
|
+
// Negative time step is evaluated as t = 0 (initial value), while t
|
6133
|
+
// beyond the optimization period is evaluated as its last time step
|
6134
|
+
// UNLESS t is used in a self-referencing variable.
|
5909
6135
|
const xv = obj.hasOwnProperty('xv');
|
5910
6136
|
if(!xv) {
|
5911
6137
|
t = Math.max(0, Math.min(
|
5912
6138
|
MODEL.end_period - MODEL.start_period + MODEL.look_ahead + 1, t));
|
5913
6139
|
}
|
5914
|
-
// Trace only now that time step t has been computed
|
6140
|
+
// Trace only now that time step t has been computed.
|
5915
6141
|
if(DEBUGGING) {
|
5916
6142
|
console.log('push var:', (xv ? '[SELF]' :
|
5917
6143
|
(obj instanceof Expression ? obj.text : '[' + obj.toString() + ']')),
|
5918
6144
|
tot[1] + ' ' + tot[2]);
|
5919
6145
|
}
|
5920
6146
|
if(Array.isArray(obj)) {
|
5921
|
-
// Object is a vector
|
6147
|
+
// Object is a vector.
|
5922
6148
|
let v = t < obj.length ? obj[t] : VM.UNDEFINED;
|
5923
|
-
// NOTE:
|
5924
|
-
// analysis, the value is multiplied by 1 + delta
|
6149
|
+
// NOTE: When the vector is the "active" parameter for sensitivity
|
6150
|
+
// analysis, the value is multiplied by 1 + delta %.
|
5925
6151
|
if(obj === MODEL.active_sensitivity_parameter) {
|
5926
|
-
// NOTE:
|
6152
|
+
// NOTE: Do NOT scale exceptional values.
|
5927
6153
|
if(v > VM.MINUS_INFINITY && v < VM.PLUS_INFINITY) {
|
5928
6154
|
v *= (1 + MODEL.sensitivity_delta * 0.01);
|
5929
6155
|
}
|
5930
6156
|
}
|
5931
6157
|
x.push(v);
|
5932
6158
|
} else if(xv) {
|
5933
|
-
// Variable references an earlier value computed for this expression `x
|
6159
|
+
// Variable references an earlier value computed for this expression `x`.
|
5934
6160
|
x.push(t >= 0 && t < x.vector.length ? x.vector[t] : obj.dv);
|
5935
6161
|
} else if(obj.hasOwnProperty('c') && obj.hasOwnProperty('u')) {
|
5936
|
-
// Object holds link lists for cluster balance computation
|
6162
|
+
// Object holds link lists for cluster balance computation.
|
5937
6163
|
x.push(MODEL.flowBalance(obj, t));
|
5938
6164
|
} else if(obj instanceof Expression) {
|
5939
6165
|
x.push(obj.result(t));
|
5940
6166
|
} else if(typeof obj === 'number') {
|
5941
|
-
// Object is a number
|
6167
|
+
// Object is a number.
|
5942
6168
|
x.push(obj);
|
5943
6169
|
} else {
|
5944
6170
|
console.log('ERROR: VMI_push_var object =', obj);
|
@@ -5947,17 +6173,17 @@ function VMI_push_var(x, args) {
|
|
5947
6173
|
}
|
5948
6174
|
|
5949
6175
|
function VMI_push_entity(x, args) {
|
5950
|
-
//
|
6176
|
+
// Push a special "entity reference" object based on `args`, being the
|
5951
6177
|
// list [obj, anchor1, offset1, anchor2, offset2] where `obj` has the
|
5952
|
-
// format {r: entity object, a: attribute}
|
5953
|
-
// The object that is pushed on the stack passes the entity, the
|
5954
|
-
// to use, and the time interval
|
6178
|
+
// format {r: entity object, a: attribute}.
|
6179
|
+
// The object that is pushed on the stack passes the entity, the
|
6180
|
+
// attribute to use, and the time interval.
|
5955
6181
|
const
|
5956
|
-
// NOTE:
|
6182
|
+
// NOTE: Use the "local" time step for expression `x`.
|
5957
6183
|
tot = twoOffsetTimeStep(x.step[x.step.length - 1],
|
5958
6184
|
args[1], args[2], args[3], args[4], 1, x),
|
5959
6185
|
er = {entity: args[0].r, attribute: args[0].a, t1: tot[0], t2: tot[1]};
|
5960
|
-
// Trace only now that time step t has been computed
|
6186
|
+
// Trace only now that time step t has been computed.
|
5961
6187
|
if(DEBUGGING) {
|
5962
6188
|
console.log(['push entity: ', er.entity.displayName, '|', er.attribute,
|
5963
6189
|
', t = ', er.t1, ' - ', er.t2].join(''));
|
@@ -5965,27 +6191,94 @@ function VMI_push_entity(x, args) {
|
|
5965
6191
|
x.push(er);
|
5966
6192
|
}
|
5967
6193
|
|
6194
|
+
function VMI_push_method(x, args) {
|
6195
|
+
// Push the result of the expression associated with the method (a
|
6196
|
+
// dataset modifier with a selector that starts with a colon).
|
6197
|
+
// The first element of the argument list specifies the method,
|
6198
|
+
// and possibly also the entity to be used as its object.
|
6199
|
+
// NOTE: Methods can only be called "as is" (without prefix) in a
|
6200
|
+
// method expression. The object of such "as is" method calls is
|
6201
|
+
// the object of the calling method expression `x`.
|
6202
|
+
const
|
6203
|
+
method = args[0].meq,
|
6204
|
+
mex = method.expression,
|
6205
|
+
// NOTE: If method object prefix is not specified in the first
|
6206
|
+
// argument, use the MOP of the calling method (if specified).
|
6207
|
+
mo_prefix = args[0].mo || x.method_object_prefix,
|
6208
|
+
// NOTE: Use the "local" time step for expression `x`.
|
6209
|
+
tot = twoOffsetTimeStep(x.step[x.step.length - 1],
|
6210
|
+
args[1], args[2], args[3], args[4], 1, x);
|
6211
|
+
if(!x.method_object && !mo_prefix) {
|
6212
|
+
console.log('ERROR: Undefined method object', x);
|
6213
|
+
x.push(VM.BAD_REF);
|
6214
|
+
return;
|
6215
|
+
}
|
6216
|
+
if(x.method_object) {
|
6217
|
+
// Set the method object to be used in VMI_push_wildcard_entity.
|
6218
|
+
mex.method_object = x.method_object;
|
6219
|
+
} else if(mex.isEligible(mo_prefix)) {
|
6220
|
+
mex.method_object_prefix = mo_prefix;
|
6221
|
+
} else {
|
6222
|
+
console.log('ERROR: ', mo_prefix, 'is not in eligible list of',
|
6223
|
+
method.selector, method.eligible_prefixes);
|
6224
|
+
x.push(VM.BAD_REF);
|
6225
|
+
return;
|
6226
|
+
}
|
6227
|
+
const
|
6228
|
+
t = tot[0],
|
6229
|
+
v = mex.result(t);
|
6230
|
+
// Clear the method object & prefix -- just to be neat.
|
6231
|
+
mex.method_object = null;
|
6232
|
+
mex.method_object_prefix = '';
|
6233
|
+
// Trace only now that time step t has been computed.
|
6234
|
+
if(DEBUGGING) {
|
6235
|
+
console.log('push method:', obj.displayName, method.selector,
|
6236
|
+
tot[1] + (tot[2] ? ':' + tot[2] : ''), 'value =', VM.sig4Dig(v));
|
6237
|
+
}
|
6238
|
+
x.push(v);
|
6239
|
+
}
|
6240
|
+
|
5968
6241
|
function VMI_push_wildcard_entity(x, args) {
|
5969
|
-
//
|
6242
|
+
// Push the value of (or reference to) an entity attribute, based on
|
5970
6243
|
// `args`, being the list [obj, anchor1, offset1, anchor2, offset2]
|
5971
6244
|
// where `obj` has the format {ee: list of eligible entities,
|
5972
6245
|
// n: name (with wildcard #), a: attribute, br: by reference (boolean)}
|
5973
|
-
|
5974
|
-
|
6246
|
+
let obj = null,
|
6247
|
+
nn = args[0].n;
|
5975
6248
|
const el = args[0].ee;
|
5976
|
-
|
5977
|
-
|
5978
|
-
|
5979
|
-
|
5980
|
-
|
5981
|
-
|
5982
|
-
|
5983
|
-
|
5984
|
-
|
5985
|
-
|
6249
|
+
// NOTE: Variables in method expressions that reference the object of
|
6250
|
+
// the method also code with this VM instruction, but then pass the
|
6251
|
+
// string "MO" instead of the list of eligible entities. This indicates
|
6252
|
+
// that the `method_object` property of expression `x` should be used.
|
6253
|
+
if(el === 'MO') {
|
6254
|
+
obj = x.method_object;
|
6255
|
+
if(!obj && x.method_object_prefix) {
|
6256
|
+
// Try to identity the object by the prefixed name.
|
6257
|
+
obj = MODEL.objectByName(x.method_object_prefix +
|
6258
|
+
UI.PREFIXER + nn.substring(1));
|
6259
|
+
}
|
6260
|
+
if(!obj) {
|
6261
|
+
console.log(`ERROR: Undefined method object`, x);
|
6262
|
+
x.push(VM.BAD_REF);
|
6263
|
+
return;
|
6264
|
+
}
|
6265
|
+
} else {
|
6266
|
+
// Select the first entity in `ee` that matches the wildcard vector
|
6267
|
+
// index of the expression `x` being executed.
|
6268
|
+
nn = nn.replace('#', x.wildcard_vector_index);
|
6269
|
+
for(let i = 0; !obj && i < el.length; i++) {
|
6270
|
+
if(el[i].name === nn) obj = el[i];
|
6271
|
+
}
|
6272
|
+
// If no match, then this indicates a bad reference.
|
6273
|
+
if(!obj) {
|
6274
|
+
console.log(`ERROR: no match for "${nn}" in eligible entity list`, el);
|
6275
|
+
x.push(VM.BAD_REF);
|
6276
|
+
return;
|
6277
|
+
}
|
5986
6278
|
}
|
5987
|
-
//
|
5988
|
-
//
|
6279
|
+
// Now `obj` should be an existing model entity.
|
6280
|
+
// If args[0] indicates "by reference", then VMI_push_entity can be
|
6281
|
+
// called with the appropriate parameters.
|
5989
6282
|
const attr = args[0].a || obj.defaultAttribute;
|
5990
6283
|
if(args[0].br) {
|
5991
6284
|
VMI_push_entity(x, {r: obj, a: attr});
|
@@ -6395,7 +6688,7 @@ function VMI_push_statistic(x, args) {
|
|
6395
6688
|
x.push(VM.UNDEFINED);
|
6396
6689
|
}
|
6397
6690
|
|
6398
|
-
function VMI_replace_undefined(x
|
6691
|
+
function VMI_replace_undefined(x) {
|
6399
6692
|
// Replaces one of the two top numbers on the stack by the other if the one
|
6400
6693
|
// is undefined
|
6401
6694
|
const d = x.pop(true); // TRUE denotes that "undefined" should be ignored as issue
|
@@ -6408,7 +6701,7 @@ function VMI_replace_undefined(x, empty) {
|
|
6408
6701
|
// NOTE: when the VM computes logical OR, AND and NOT, any non-zero number
|
6409
6702
|
// is interpreted as TRUE
|
6410
6703
|
|
6411
|
-
function VMI_or(x
|
6704
|
+
function VMI_or(x) {
|
6412
6705
|
// Performs a logical OR on the two top numbers on the stack
|
6413
6706
|
const d = x.pop();
|
6414
6707
|
if(d !== false) {
|
@@ -6417,7 +6710,7 @@ function VMI_or(x, empty) {
|
|
6417
6710
|
}
|
6418
6711
|
}
|
6419
6712
|
|
6420
|
-
function VMI_and(x
|
6713
|
+
function VMI_and(x) {
|
6421
6714
|
// Performs a logical AND on the two top numbers on the stack
|
6422
6715
|
const d = x.pop();
|
6423
6716
|
if(d !== false) {
|
@@ -6426,7 +6719,7 @@ function VMI_and(x, empty) {
|
|
6426
6719
|
}
|
6427
6720
|
}
|
6428
6721
|
|
6429
|
-
function VMI_not(x
|
6722
|
+
function VMI_not(x) {
|
6430
6723
|
// Performs a logical NOT on the top number of the stack
|
6431
6724
|
const d = x.top();
|
6432
6725
|
if(d !== false) {
|
@@ -6435,7 +6728,7 @@ function VMI_not(x, empty) {
|
|
6435
6728
|
}
|
6436
6729
|
}
|
6437
6730
|
|
6438
|
-
function VMI_abs(x
|
6731
|
+
function VMI_abs(x) {
|
6439
6732
|
// Replaces the top number of the stack by its absolute value
|
6440
6733
|
const d = x.top();
|
6441
6734
|
if(d !== false) {
|
@@ -6444,7 +6737,7 @@ function VMI_abs(x, empty) {
|
|
6444
6737
|
}
|
6445
6738
|
}
|
6446
6739
|
|
6447
|
-
function VMI_eq(x
|
6740
|
+
function VMI_eq(x) {
|
6448
6741
|
// Tests equality of the two top numbers on the stack
|
6449
6742
|
const d = x.pop();
|
6450
6743
|
if(d !== false) {
|
@@ -6453,7 +6746,7 @@ function VMI_eq(x, empty) {
|
|
6453
6746
|
}
|
6454
6747
|
}
|
6455
6748
|
|
6456
|
-
function VMI_ne(x
|
6749
|
+
function VMI_ne(x) {
|
6457
6750
|
// Tests inequality of the two top numbers on the stack
|
6458
6751
|
const d = x.pop();
|
6459
6752
|
if(d !== false) {
|
@@ -6462,7 +6755,7 @@ function VMI_ne(x, empty) {
|
|
6462
6755
|
}
|
6463
6756
|
}
|
6464
6757
|
|
6465
|
-
function VMI_lt(x
|
6758
|
+
function VMI_lt(x) {
|
6466
6759
|
// Tests whether second number on the stack is less than the top number
|
6467
6760
|
const d = x.pop();
|
6468
6761
|
if(d !== false) {
|
@@ -6471,7 +6764,7 @@ function VMI_lt(x, empty) {
|
|
6471
6764
|
}
|
6472
6765
|
}
|
6473
6766
|
|
6474
|
-
function VMI_gt(x
|
6767
|
+
function VMI_gt(x) {
|
6475
6768
|
// Tests whether second number on the stack is greater than the top number
|
6476
6769
|
const d = x.pop();
|
6477
6770
|
if(d !== false) {
|
@@ -6480,7 +6773,7 @@ function VMI_gt(x, empty) {
|
|
6480
6773
|
}
|
6481
6774
|
}
|
6482
6775
|
|
6483
|
-
function VMI_le(x
|
6776
|
+
function VMI_le(x) {
|
6484
6777
|
// Tests whether second number on the stack is less than, or equal to,
|
6485
6778
|
// the top number
|
6486
6779
|
const d = x.pop();
|
@@ -6490,7 +6783,7 @@ function VMI_le(x, empty) {
|
|
6490
6783
|
}
|
6491
6784
|
}
|
6492
6785
|
|
6493
|
-
function VMI_ge(x
|
6786
|
+
function VMI_ge(x) {
|
6494
6787
|
// Tests whether second number on the stack is greater than, or equal to,
|
6495
6788
|
// the top number
|
6496
6789
|
const d = x.pop();
|
@@ -6500,7 +6793,7 @@ function VMI_ge(x, empty) {
|
|
6500
6793
|
}
|
6501
6794
|
}
|
6502
6795
|
|
6503
|
-
function VMI_add(x
|
6796
|
+
function VMI_add(x) {
|
6504
6797
|
// Pops the top number on the stack and adds it to the new top number
|
6505
6798
|
const d = x.pop();
|
6506
6799
|
if(d !== false) {
|
@@ -6509,7 +6802,7 @@ function VMI_add(x, empty) {
|
|
6509
6802
|
}
|
6510
6803
|
}
|
6511
6804
|
|
6512
|
-
function VMI_sub(x
|
6805
|
+
function VMI_sub(x) {
|
6513
6806
|
// Pops the top number on the stack and subtracts it from the new
|
6514
6807
|
// top number
|
6515
6808
|
const d = x.pop();
|
@@ -6519,7 +6812,7 @@ function VMI_sub(x, empty) {
|
|
6519
6812
|
}
|
6520
6813
|
}
|
6521
6814
|
|
6522
|
-
function VMI_mul(x
|
6815
|
+
function VMI_mul(x) {
|
6523
6816
|
// Pops the top number on the stack and multiplies it with the new
|
6524
6817
|
// top number
|
6525
6818
|
const d = x.pop();
|
@@ -6529,7 +6822,7 @@ function VMI_mul(x, empty) {
|
|
6529
6822
|
}
|
6530
6823
|
}
|
6531
6824
|
|
6532
|
-
function VMI_div(x
|
6825
|
+
function VMI_div(x) {
|
6533
6826
|
// Pops the top number on the stack and divides the new top number
|
6534
6827
|
// by it. In case of division by zero, the top is replaced by #DIV0!
|
6535
6828
|
const d = x.pop();
|
@@ -6543,7 +6836,7 @@ function VMI_div(x, empty) {
|
|
6543
6836
|
}
|
6544
6837
|
}
|
6545
6838
|
|
6546
|
-
function VMI_mod(x
|
6839
|
+
function VMI_mod(x) {
|
6547
6840
|
// Pops the top number on the stack, divides the new top number by it
|
6548
6841
|
// (if non-zero, or it pushes error code #DIV0!), takes the fraction
|
6549
6842
|
// part, and multiplies this with the divider; in other words, it
|
@@ -6559,7 +6852,7 @@ function VMI_mod(x, empty) {
|
|
6559
6852
|
}
|
6560
6853
|
}
|
6561
6854
|
|
6562
|
-
function VMI_negate(x
|
6855
|
+
function VMI_negate(x) {
|
6563
6856
|
// Performs a negation on the top number of the stack
|
6564
6857
|
const d = x.top();
|
6565
6858
|
if(d !== false) {
|
@@ -6568,7 +6861,7 @@ function VMI_negate(x, empty) {
|
|
6568
6861
|
}
|
6569
6862
|
}
|
6570
6863
|
|
6571
|
-
function VMI_power(x
|
6864
|
+
function VMI_power(x) {
|
6572
6865
|
// Pops the top number on the stack and raises the new top number
|
6573
6866
|
// to its power
|
6574
6867
|
const d = x.pop();
|
@@ -6578,7 +6871,7 @@ function VMI_power(x, empty) {
|
|
6578
6871
|
}
|
6579
6872
|
}
|
6580
6873
|
|
6581
|
-
function VMI_sqrt(x
|
6874
|
+
function VMI_sqrt(x) {
|
6582
6875
|
// Replaces the top number of the stack by its square root, or by
|
6583
6876
|
// error code #VALUE! if the top number is negative
|
6584
6877
|
const d = x.top();
|
@@ -6592,7 +6885,7 @@ function VMI_sqrt(x, empty) {
|
|
6592
6885
|
}
|
6593
6886
|
}
|
6594
6887
|
|
6595
|
-
function VMI_sin(x
|
6888
|
+
function VMI_sin(x) {
|
6596
6889
|
// Replaces the top number X of the stack by sin(X)
|
6597
6890
|
const d = x.top();
|
6598
6891
|
if(d !== false) {
|
@@ -6601,7 +6894,7 @@ function VMI_sin(x, empty) {
|
|
6601
6894
|
}
|
6602
6895
|
}
|
6603
6896
|
|
6604
|
-
function VMI_cos(x
|
6897
|
+
function VMI_cos(x) {
|
6605
6898
|
// Replaces the top number X of the stack by cos(X)
|
6606
6899
|
const d = x.top();
|
6607
6900
|
if(d !== false) {
|
@@ -6610,7 +6903,7 @@ function VMI_cos(x, empty) {
|
|
6610
6903
|
}
|
6611
6904
|
}
|
6612
6905
|
|
6613
|
-
function VMI_atan(x
|
6906
|
+
function VMI_atan(x) {
|
6614
6907
|
// Replaces the top number X of the stack by atan(X)
|
6615
6908
|
const d = x.top();
|
6616
6909
|
if(d !== false) {
|
@@ -6619,7 +6912,7 @@ function VMI_atan(x, empty) {
|
|
6619
6912
|
}
|
6620
6913
|
}
|
6621
6914
|
|
6622
|
-
function VMI_ln(x
|
6915
|
+
function VMI_ln(x) {
|
6623
6916
|
// Replaces the top number X of the stack by ln(X), or by error
|
6624
6917
|
// code #VALUE! if X is negative
|
6625
6918
|
const d = x.top();
|
@@ -6633,7 +6926,7 @@ function VMI_ln(x, empty) {
|
|
6633
6926
|
}
|
6634
6927
|
}
|
6635
6928
|
|
6636
|
-
function VMI_exp(x
|
6929
|
+
function VMI_exp(x) {
|
6637
6930
|
// Replaces the top number X of the stack by exp(X)
|
6638
6931
|
const d = x.top();
|
6639
6932
|
if(d !== false) {
|
@@ -6642,7 +6935,7 @@ function VMI_exp(x, empty) {
|
|
6642
6935
|
}
|
6643
6936
|
}
|
6644
6937
|
|
6645
|
-
function VMI_log(x
|
6938
|
+
function VMI_log(x) {
|
6646
6939
|
// Pops the top number B from the stack and replaces the new top
|
6647
6940
|
// number A by A log B. NOTE: x = A log B <=> x = ln(B) / ln(A)
|
6648
6941
|
let d = x.pop();
|
@@ -6657,7 +6950,7 @@ function VMI_log(x, empty) {
|
|
6657
6950
|
}
|
6658
6951
|
}
|
6659
6952
|
|
6660
|
-
function VMI_round(x
|
6953
|
+
function VMI_round(x) {
|
6661
6954
|
// Replaces the top number X of the stack by round(X)
|
6662
6955
|
const d = x.top();
|
6663
6956
|
if(d !== false) {
|
@@ -6666,7 +6959,7 @@ function VMI_round(x, empty) {
|
|
6666
6959
|
}
|
6667
6960
|
}
|
6668
6961
|
|
6669
|
-
function VMI_int(x
|
6962
|
+
function VMI_int(x) {
|
6670
6963
|
// Replaces the top number X of the stack by its integer part
|
6671
6964
|
const d = x.top();
|
6672
6965
|
if(d !== false) {
|
@@ -6675,7 +6968,7 @@ function VMI_int(x, empty) {
|
|
6675
6968
|
}
|
6676
6969
|
}
|
6677
6970
|
|
6678
|
-
function VMI_fract(x
|
6971
|
+
function VMI_fract(x) {
|
6679
6972
|
// Replaces the top number X of the stack by its fraction part
|
6680
6973
|
const d = x.top();
|
6681
6974
|
if(d !== false) {
|
@@ -6684,7 +6977,7 @@ function VMI_fract(x, empty) {
|
|
6684
6977
|
}
|
6685
6978
|
}
|
6686
6979
|
|
6687
|
-
function VMI_exponential(x
|
6980
|
+
function VMI_exponential(x) {
|
6688
6981
|
// Replaces the top number X of the stack by a random number from the
|
6689
6982
|
// negative exponential distribution with parameter X (so X is the lambda,
|
6690
6983
|
// and the mean will be 1/X)
|
@@ -6696,7 +6989,7 @@ function VMI_exponential(x, empty) {
|
|
6696
6989
|
}
|
6697
6990
|
}
|
6698
6991
|
|
6699
|
-
function VMI_poisson(x
|
6992
|
+
function VMI_poisson(x) {
|
6700
6993
|
// Replaces the top number X of the stack by a random number from the
|
6701
6994
|
// poisson distribution with parameter X (so X is the mean value lambda)
|
6702
6995
|
const d = x.top();
|
@@ -6707,7 +7000,7 @@ function VMI_poisson(x, empty) {
|
|
6707
7000
|
}
|
6708
7001
|
}
|
6709
7002
|
|
6710
|
-
function VMI_binomial(x
|
7003
|
+
function VMI_binomial(x) {
|
6711
7004
|
// Replaces the top list (!) A of the stack by Bin(A[0], A[1]), i.e., a random
|
6712
7005
|
// number from the binomial distribution with n = A[0] and p = A[1]
|
6713
7006
|
const d = x.top();
|
@@ -6723,7 +7016,7 @@ function VMI_binomial(x, empty) {
|
|
6723
7016
|
}
|
6724
7017
|
}
|
6725
7018
|
|
6726
|
-
function VMI_normal(x
|
7019
|
+
function VMI_normal(x) {
|
6727
7020
|
// Replaces the top list (!) A of the stack by N(A[0], A[1]), i.e., a random
|
6728
7021
|
// number from the normal distribution with mu = A[0] and sigma = A[1]
|
6729
7022
|
const d = x.top();
|
@@ -6739,7 +7032,7 @@ function VMI_normal(x, empty) {
|
|
6739
7032
|
}
|
6740
7033
|
}
|
6741
7034
|
|
6742
|
-
function VMI_weibull(x
|
7035
|
+
function VMI_weibull(x) {
|
6743
7036
|
// Replaces the top list (!) A of the stack by Weibull(A[0], A[1]), i.e., a
|
6744
7037
|
// random number from the Weibull distribution with lambda = A[0] and k = A[1]
|
6745
7038
|
const d = x.top();
|
@@ -6755,7 +7048,7 @@ function VMI_weibull(x, empty) {
|
|
6755
7048
|
}
|
6756
7049
|
}
|
6757
7050
|
|
6758
|
-
function VMI_triangular(x
|
7051
|
+
function VMI_triangular(x) {
|
6759
7052
|
// Replaces the top list (!) A of the stack by Tri(A[0], A[1]), A[2]), i.e.,
|
6760
7053
|
// a random number from the triangular distribution with a = A[0], b = A[1],
|
6761
7054
|
// and c = A[2]. NOTE: if only 2 parameters are passed, c is assumed to equal
|
@@ -6773,7 +7066,7 @@ function VMI_triangular(x, empty) {
|
|
6773
7066
|
}
|
6774
7067
|
}
|
6775
7068
|
|
6776
|
-
function VMI_npv(x
|
7069
|
+
function VMI_npv(x) {
|
6777
7070
|
// Replaces the top list (!) A of the stack by the net present value (NPV)
|
6778
7071
|
// of the arguments in A. A[0] is the interest rate r, A[1] is the number of
|
6779
7072
|
// time periods n. If A has only 1 or 2 elements, the NPV is 0. If A has 3
|
@@ -6813,7 +7106,7 @@ function VMI_npv(x, empty) {
|
|
6813
7106
|
}
|
6814
7107
|
}
|
6815
7108
|
|
6816
|
-
function VMI_min(x
|
7109
|
+
function VMI_min(x) {
|
6817
7110
|
// Replaces the top list (!) A of the stack by the lowest value in this list
|
6818
7111
|
// NOTE: if A is not a list, A is left on the stack
|
6819
7112
|
const d = x.top();
|
@@ -6825,7 +7118,7 @@ function VMI_min(x, empty) {
|
|
6825
7118
|
}
|
6826
7119
|
}
|
6827
7120
|
|
6828
|
-
function VMI_max(x
|
7121
|
+
function VMI_max(x) {
|
6829
7122
|
// Replaces the top list (!) A of the stack by the highest value in this list
|
6830
7123
|
// NOTE: if A is not a list, A is left on the stack
|
6831
7124
|
const d = x.top();
|
@@ -6837,7 +7130,7 @@ function VMI_max(x, empty) {
|
|
6837
7130
|
}
|
6838
7131
|
}
|
6839
7132
|
|
6840
|
-
function VMI_concat(x
|
7133
|
+
function VMI_concat(x) {
|
6841
7134
|
// Pops the top number B from the stack, and then replaces the new top
|
6842
7135
|
// element A by [A, B] if A is a number, or adds B to A is A is a list
|
6843
7136
|
// of numbers (!) or
|
@@ -6882,20 +7175,20 @@ function VMI_jump_if_false(x, index) {
|
|
6882
7175
|
}
|
6883
7176
|
}
|
6884
7177
|
|
6885
|
-
function VMI_pop_false(x
|
7178
|
+
function VMI_pop_false(x) {
|
6886
7179
|
// Removes the top value from the stack, which should be 0 or
|
6887
7180
|
// VM.UNDEFINED (but this is not checked)
|
6888
7181
|
const r = x.stack.pop();
|
6889
7182
|
if(DEBUGGING) console.log(`POP-FALSE (${r})`);
|
6890
7183
|
}
|
6891
7184
|
|
6892
|
-
function VMI_if_then(x
|
7185
|
+
function VMI_if_then(x) {
|
6893
7186
|
// NO operation -- as of version 1.0.14, this function only serves as
|
6894
7187
|
// operator symbol, and its executions would indicate an error
|
6895
7188
|
console.log('WARNING: this IF-THEN instruction is obsolete!');
|
6896
7189
|
}
|
6897
7190
|
|
6898
|
-
function VMI_if_else(x
|
7191
|
+
function VMI_if_else(x) {
|
6899
7192
|
// NO operation -- as of version 1.0.14, this function only serves as
|
6900
7193
|
// operator symbol, and its executions would indicate an error
|
6901
7194
|
console.log('WARNING: this IF-THEN instruction is obsolete!');
|
@@ -7824,7 +8117,7 @@ const
|
|
7824
8117
|
|
7825
8118
|
// Each custom operator must have its own Virtual Machine instruction
|
7826
8119
|
|
7827
|
-
function VMI_profitable_units(x
|
8120
|
+
function VMI_profitable_units(x) {
|
7828
8121
|
// Replaces the argument list that should be at the top of the stack by the
|
7829
8122
|
// number of profitable units having a standard capacity (number), given the
|
7830
8123
|
// level (vector) of the process that represents multiple such units, the
|
@@ -7958,7 +8251,7 @@ DYNAMIC_SYMBOLS.push('npu');
|
|
7958
8251
|
LEVEL_BASED_CODES.push(VMI_profitable_units);
|
7959
8252
|
|
7960
8253
|
|
7961
|
-
function VMI_highest_cumulative_consecutive_deviation(x
|
8254
|
+
function VMI_highest_cumulative_consecutive_deviation(x) {
|
7962
8255
|
// Replaces the argument list that should be at the top of the stack by
|
7963
8256
|
// the HCCD (as in the function name) of the vector V that is passed as
|
7964
8257
|
// the first argument of this function. The HCCD value is computed by
|