linny-r 1.1.22 → 1.1.23

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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "linny-r",
3
- "version": "1.1.22",
3
+ "version": "1.1.23",
4
4
  "description": "Executable graphical language with WYSIWYG editor for MILP models",
5
5
  "main": "server.js",
6
6
  "scripts": {
package/static/index.html CHANGED
@@ -1404,7 +1404,7 @@ and move the cursor over the status bar">
1404
1404
  <option value="1">Product</option>
1405
1405
  <option value="2">Cluster</option>
1406
1406
  <option value="3">Link</option>
1407
- <option value="4">Constraint</option>
1407
+ <!-- <option value="4">Constraint</option> -->
1408
1408
  <option value="5">Actor</option>
1409
1409
  <option value="6">Dataset</option>
1410
1410
  <option value="7">Equation</option>
@@ -1820,7 +1820,7 @@ NOTE: * and ? will be interpreted as wildcards"
1820
1820
  <option value="1">Product</option>
1821
1821
  <option value="2">Cluster</option>
1822
1822
  <option value="3">Link</option>
1823
- <option value="4">Constraint</option>
1823
+ <!-- <option value="4">Constraint</option> -->
1824
1824
  <option value="5">Actor</option>
1825
1825
  <option value="6">Dataset</option>
1826
1826
  <option value="7">Equation</option>
@@ -1957,7 +1957,7 @@ NOTE: * and ? will be interpreted as wildcards"
1957
1957
  <option value="1">Product</option>
1958
1958
  <option id="add-sa-variable-cluster" value="2">Cluster</option>
1959
1959
  <option value="3">Link</option>
1960
- <option value="4">Constraint</option>
1960
+ <!-- <option value="4">Constraint</option> -->
1961
1961
  <option value="5">Actor</option>
1962
1962
  <option value="6">Dataset</option>
1963
1963
  <option id="add-sa-variable-equation" value="7">Equation</option>
@@ -738,8 +738,11 @@ class SensitivityAnalysis {
738
738
  oax = (obj ? obj.attributeExpression(vn[1]) : null);
739
739
  if(oax) {
740
740
  this.parameters.push(oax);
741
+ } else if(vn.length === 1 && obj instanceof Dataset) {
742
+ // Dataset without selector => push the dataset vector
743
+ this.parameters.push(obj.vector);
741
744
  } else {
742
- UI.alert(`Parameter ${p} is not an expression`);
745
+ UI.alert(`Parameter ${p} is not a dataset or expression`);
743
746
  }
744
747
  }
745
748
  this.chart = new Chart(this.chart_title);
@@ -841,6 +844,7 @@ class SensitivityAnalysis {
841
844
  VM.halt();
842
845
  this.readyButtons();
843
846
  this.showProgress('');
847
+ this.must_pause = false;
844
848
  }
845
849
 
846
850
  clearResults() {
@@ -10513,7 +10513,7 @@ class GUISensitivityAnalysis extends SensitivityAnalysis {
10513
10513
  this.base_selectors.addEventListener(
10514
10514
  'blur', () => SENSITIVITY_ANALYSIS.setBaseSelectors());
10515
10515
 
10516
- this.delta = document.getElementById('sa-delta');
10516
+ this.delta = document.getElementById('sensitivity-delta');
10517
10517
  this.delta.addEventListener(
10518
10518
  'focus', () => SENSITIVITY_ANALYSIS.editDelta());
10519
10519
  this.delta.addEventListener(
@@ -10610,6 +10610,8 @@ class GUISensitivityAnalysis extends SensitivityAnalysis {
10610
10610
  }
10611
10611
 
10612
10612
  updateControlPanel() {
10613
+ // Shows the control panel, or when the analysis is running the
10614
+ // legend to the outcomes (also to prevent changes to parameters)
10613
10615
  this.base_selectors.value = MODEL.base_case_selectors;
10614
10616
  this.delta.value = VM.sig4Dig(MODEL.sensitivity_delta);
10615
10617
  const tr = [];
@@ -10900,6 +10902,8 @@ class GUISensitivityAnalysis extends SensitivityAnalysis {
10900
10902
  // NOTE: clusters have no suitable attributes, and equations are endogenous
10901
10903
  md.element('cluster').style.display = 'none';
10902
10904
  md.element('equation').style.display = 'none';
10905
+ // NOTE: update to ensure that valid attributes are selectable
10906
+ X_EDIT.updateVariableBar('add-sa-');
10903
10907
  md.show();
10904
10908
  }
10905
10909
 
@@ -10909,6 +10913,8 @@ class GUISensitivityAnalysis extends SensitivityAnalysis {
10909
10913
  md.element('type').innerText = 'outcome';
10910
10914
  md.element('cluster').style.display = 'block';
10911
10915
  md.element('equation').style.display = 'block';
10916
+ // NOTE: update to ensure that valid attributes are selectable
10917
+ X_EDIT.updateVariableBar('add-sa-');
10912
10918
  md.show();
10913
10919
  }
10914
10920
 
@@ -10977,9 +10983,14 @@ class GUISensitivityAnalysis extends SensitivityAnalysis {
10977
10983
  a = md.selectedOption('attr').text;
10978
10984
  let n = '';
10979
10985
  if(e === 'Equation' && a) {
10986
+ // For equations, the attribute denotes the name
10980
10987
  n = a;
10981
10988
  } else if(o && a) {
10989
+ // Most variables are defined by name + attribute ...
10982
10990
  n = o + UI.OA_SEPARATOR + a;
10991
+ } else if(e === 'Dataset' && o) {
10992
+ // ... but for datasets the selector is optional
10993
+ n = o;
10983
10994
  }
10984
10995
  if(n) {
10985
10996
  if(t === 'parameter' && MODEL.sensitivity_parameters.indexOf(n) < 0) {
@@ -11021,6 +11032,7 @@ class GUISensitivityAnalysis extends SensitivityAnalysis {
11021
11032
  this.start_btn.classList.add('off');
11022
11033
  this.pause_btn.classList.remove('off');
11023
11034
  this.stop_btn.classList.add('off');
11035
+ this.must_pause = false;
11024
11036
  return paused;
11025
11037
  }
11026
11038
 
@@ -11029,6 +11041,7 @@ class GUISensitivityAnalysis extends SensitivityAnalysis {
11029
11041
  this.pause_btn.classList.add('off');
11030
11042
  this.stop_btn.classList.add('off');
11031
11043
  this.start_btn.classList.remove('off', 'blink');
11044
+ this.must_pause = false;
11032
11045
  }
11033
11046
 
11034
11047
  pausedButtons(aci) {
@@ -11047,6 +11060,8 @@ class GUISensitivityAnalysis extends SensitivityAnalysis {
11047
11060
  this.readyButtons();
11048
11061
  this.reset_btn.classList.add('off');
11049
11062
  this.selected_run = -1;
11063
+ this.must_pause = false;
11064
+ this.progress.innerHTML = '';
11050
11065
  this.updateDialog();
11051
11066
  }
11052
11067
 
@@ -11802,6 +11817,32 @@ class GUIExperimentManager extends ExperimentManager {
11802
11817
  }
11803
11818
  }
11804
11819
 
11820
+ toggleChartRow(r, n, shift) {
11821
+ // Toggle `n` consecutive rows, starting at row `r` (0 = top), to be
11822
+ // (no longer) part of the chart combination set
11823
+ const
11824
+ x = this.selected_experiment,
11825
+ // Let `n` be the number of the first run on row `r`
11826
+ nconf = r * this.nr_of_configurations;
11827
+ if(x && r < x.combinations.length / this.nr_of_configurations) {
11828
+ // NOTE: first cell of row determines ADD or REMOVE
11829
+ const add = x.chart_combinations.indexOf(n) < 0;
11830
+ for(let i = 0; i < this.nr_of_configurations; i++) {
11831
+ const ic = x.chart_combinations.indexOf(i);
11832
+ if(add) {
11833
+ if(ic < 0) x.chart_combinations.push(nconf + i);
11834
+ } else {
11835
+ if(!add) x.chart_combinations.splice(nconf + i, 1);
11836
+ }
11837
+ }
11838
+ this.updateData();
11839
+ }
11840
+ }
11841
+
11842
+ toggleChartColumn(c, shift) {
11843
+ // Toggle column `c` (0 = leftmost) to be part of the chart combination set
11844
+ }
11845
+
11805
11846
  toggleChartCombi(n, shift, alt) {
11806
11847
  // Set `n` to be the chart combination, or toggle if Shift-key is pressed,
11807
11848
  // or execute single run if Alt-key is pressed
@@ -12788,7 +12829,7 @@ N = ${rr.N}, vector length = ${rr.vector.length}` : '')].join('');
12788
12829
  md.element('separator').value = ds.separator;
12789
12830
  md.element('quotes').value = ds.quotes;
12790
12831
  md.element('precision').value = ds.precision;
12791
- md.element('var-count').innerText = x.variables.length;
12832
+ md.element('var-count').innerText = x.runs[0].results.length;
12792
12833
  md.element('run-count').innerText = runs;
12793
12834
  md.element('run-s').innerText = (sruns === 1 ? '' : 's');
12794
12835
  }
@@ -2485,12 +2485,15 @@ class LinnyRModel {
2485
2485
  const ds_dict = {};
2486
2486
  for(let k in this.datasets) if(this.datasets.hasOwnProperty(k)) {
2487
2487
  const ds = this.datasets[k];
2488
- for(let m in ds.modifiers) if(ds.modifiers.hasOwnProperty(m)) {
2489
- const s = ds.modifiers[m].selector;
2490
- if(s in ds_dict) {
2491
- ds_dict[s].push(ds);
2492
- } else {
2493
- ds_dict[s] = [ds];
2488
+ // NOTE: ignore selectors of the equations dataset
2489
+ if(ds !== this.equations_dataset) {
2490
+ for(let m in ds.modifiers) if(ds.modifiers.hasOwnProperty(m)) {
2491
+ const s = ds.modifiers[m].selector;
2492
+ if(s in ds_dict) {
2493
+ ds_dict[s].push(ds);
2494
+ } else {
2495
+ ds_dict[s] = [ds];
2496
+ }
2494
2497
  }
2495
2498
  }
2496
2499
  }
@@ -7644,9 +7647,11 @@ class Dataset {
7644
7647
  attributeExpression(a) {
7645
7648
  // Returns expression for selector `a`, or NULL if no such selector exists
7646
7649
  // NOTE: selectors no longer are case-sensitive
7647
- a = UI.nameToID(a);
7648
- for(let m in this.modifiers) if(this.modifiers.hasOwnProperty(m)) {
7649
- if(m === a) return this.modifiers[m].expression;
7650
+ if(a) {
7651
+ a = UI.nameToID(a);
7652
+ for(let m in this.modifiers) if(this.modifiers.hasOwnProperty(m)) {
7653
+ if(m === a) return this.modifiers[m].expression;
7654
+ }
7650
7655
  }
7651
7656
  return null;
7652
7657
  }
@@ -9226,7 +9231,12 @@ class ExperimentRunResult {
9226
9231
  obj = MODEL.objectByID(this.object_id),
9227
9232
  dn = obj.displayName;
9228
9233
  // NOTE: for equations dataset, only display the modifier selector
9229
- if(obj === MODEL.equations_dataset) return this.attribute;
9234
+ if(obj === MODEL.equations_dataset) {
9235
+ const m = obj.modifiers[this.attribute.toLowerCase()];
9236
+ if(m) return m.selector;
9237
+ console.log('WARNING: Run result of non-existent equation', this.attribute);
9238
+ return this.attribute;
9239
+ }
9230
9240
  return (this.attribute ? dn + '|' + this.attribute : dn);
9231
9241
  }
9232
9242
 
@@ -9994,7 +10004,19 @@ class Experiment {
9994
10004
 
9995
10005
  get resultsAsCSV() {
9996
10006
  // Return results as specfied by the download settings
10007
+ // NOTE: no runs => no results => return empty string
10008
+ if(this.runs.length === 0) return '';
9997
10009
  const
10010
+ // Local function to convert number to string
10011
+ numval = (v, p) => {
10012
+ // Return 0 as single digit
10013
+ if(Math.abs(v) < VM.NEAR_ZERO) return '0';
10014
+ // Return empty string for undefined or exceptional values
10015
+ if(!v || v < VM.MINUS_INFINITY || v > VM.PLUS_INFINITY) return '';
10016
+ // Return other values as float with specified precision
10017
+ return v.toPrecision(p);
10018
+ },
10019
+ prec = this.download_settings.precision,
9998
10020
  allruns = this.download_settings.runs === 'all',
9999
10021
  sep = (this.download_settings.separator === 'tab' ? '\t' :
10000
10022
  (this.download_settings.separator === 'comma' ? ',' : ';')),
@@ -10019,24 +10041,21 @@ class Experiment {
10019
10041
  exceptions: `${quo}Exceptions${quo}${sep}`,
10020
10042
  run: []
10021
10043
  };
10022
- // Make list of indices of variables to include
10023
- if(this.download_settings.variables === 'selected') {
10024
- // Only one variable
10025
- vars.push(this.resultIndex(this.selected_variable));
10026
- } else {
10027
- // All variables
10028
- for(let i = 0; i < this.variables.length; i++) {
10029
- vars.push(i);
10030
- }
10031
- }
10032
- const nvars = vars.length;
10033
10044
  for(let i = 0; i < this.combinations.length; i++) {
10034
10045
  if(i < this.runs.length &&
10035
10046
  (allruns || this.chart_combinations.indexOf(i) >= 0)) {
10036
10047
  data.run.push(i);
10037
10048
  }
10038
10049
  }
10039
- let series_length = 0;
10050
+ let series_length = 0,
10051
+ // By default, assume all variables to be output
10052
+ start = 0,
10053
+ stop = this.runs[0].results.length;
10054
+ if(this.download_settings.variables === 'selected') {
10055
+ // Only one variable
10056
+ start = this.resultIndex(this.selected_variable);
10057
+ stop = start + 1;
10058
+ }
10040
10059
  for(let i = 0; i < data.run.length; i++) {
10041
10060
  const
10042
10061
  rnr = data.run[i],
@@ -10044,31 +10063,35 @@ class Experiment {
10044
10063
  data.nr += r.number;
10045
10064
  data.combi += quo + this.combinations[rnr].join('|') + quo;
10046
10065
  // Run duration in seconds
10047
- data.rsecs += VM.sig2Dig((r.time_recorded - r.time_started) * 0.001);
10048
- data.ssecs += VM.sig2Dig(r.solver_seconds);
10066
+ data.rsecs += numval((r.time_recorded - r.time_started) * 0.001, 4);
10067
+ data.ssecs += numval(r.solver_seconds, 4);
10049
10068
  data.warnings += r.warning_count;
10050
- for(let j = 0; j < nvars; j++) {
10069
+ for(let j = start; j < stop; j++) {
10051
10070
  // Add empty cells for run attributes
10052
10071
  data.nr += sep;
10053
10072
  data.combi += sep;
10054
10073
  data.rsecs += sep;
10055
10074
  data.ssecs += sep;
10056
10075
  data.warnings += sep;
10057
- const rr = r.results[vars[j]];
10058
- data.variable += rr.displayName + sep;
10059
- // Series may differ in length; the longest determines the
10060
- // number of rows of series data to be added
10061
- series_length = Math.max(series_length, rr.vector.length);
10062
- if(this.download_settings.statistics) {
10063
- data.N += rr.N + sep;
10064
- data.sum += rr.sum + sep;
10065
- data.mean += rr.mean + sep;
10066
- data.variance += rr.variance + sep;
10067
- data.minimum += rr.minimum + sep;
10068
- data.maximum += rr.maximum + sep;
10069
- data.NZ += rr.non_zero_tally + sep;
10070
- data.last += rr.last + sep;
10071
- data.exceptions += rr.exceptions + sep;
10076
+ const rr = r.results[j];
10077
+ if(rr) {
10078
+ data.variable += rr.displayName + sep;
10079
+ // Series may differ in length; the longest determines the
10080
+ // number of rows of series data to be added
10081
+ series_length = Math.max(series_length, rr.vector.length);
10082
+ if(this.download_settings.statistics) {
10083
+ data.N += rr.N + sep;
10084
+ data.sum += numval(rr.sum, prec) + sep;
10085
+ data.mean += numval(rr.mean, prec) + sep;
10086
+ data.variance += numval(rr.variance, prec) + sep;
10087
+ data.minimum += numval(rr.minimum, prec) + sep;
10088
+ data.maximum += numval(rr.maximum, prec) + sep;
10089
+ data.NZ += rr.non_zero_tally + sep;
10090
+ data.last += numval(rr.last, prec) + sep;
10091
+ data.exceptions += rr.exceptions + sep;
10092
+ }
10093
+ } else {
10094
+ console.log('No run results for ', this.variables[vars[j]].displayName);
10072
10095
  }
10073
10096
  }
10074
10097
  }
@@ -10084,20 +10107,18 @@ class Experiment {
10084
10107
  }
10085
10108
  if(this.download_settings.series) {
10086
10109
  ds.push('t');
10087
- const
10088
- prec = this.download_settings.precision,
10089
- row = [];
10110
+ const row = [];
10090
10111
  for(let i = 0; i < series_length; i++) {
10091
10112
  row.length = 0;
10092
10113
  row.push(i);
10093
10114
  for(let j = 0; j < data.run.length; j++) {
10094
10115
  const rnr = data.run[j];
10095
- for(let k = 0; k < vars.length; k++) {
10096
- const rr = this.runs[rnr].results[vars[k]];
10097
- if(i < rr.vector.length) {
10098
- const v = rr.vector[i];
10099
- if(v >= VM.MINUS_INFINITY && v <= VM.PLUS_INFINITY) {
10100
- row.push(v.toPrecision(prec));
10116
+ for(let k = start; k < stop; k++) {
10117
+ const rr = this.runs[rnr].results[k];
10118
+ if(rr) {
10119
+ // NOTE: only experiment variables have vector data
10120
+ if(rr.x_variable && i <= rr.N) {
10121
+ row.push(numval(rr.vector[i], prec));
10101
10122
  } else {
10102
10123
  row.push('');
10103
10124
  }
@@ -305,7 +305,6 @@ class Expression {
305
305
  // Pop the time step
306
306
  this.step.pop();
307
307
  this.trace('--STOP: ' + this.variableName);
308
- DEBUGGING = false;
309
308
  // Clear context for #
310
309
  this.wildcard_number = false;
311
310
  // If error, display the call stack (only once)
@@ -5183,7 +5182,16 @@ function VMI_push_var(x, args) {
5183
5182
  }
5184
5183
  if(Array.isArray(obj)) {
5185
5184
  // Object is a vector
5186
- x.push(t < obj.length ? obj[t] : VM.UNDEFINED);
5185
+ let v = t < obj.length ? obj[t] : VM.UNDEFINED;
5186
+ // NOTE: when the vector is the "active" parameter for sensitivity
5187
+ // analysis, the value is multiplied by 1 + delta %
5188
+ if(obj === MODEL.active_sensitivity_parameter) {
5189
+ // NOTE: do NOT scale exceptional values
5190
+ if(v > VM.MINUS_INFINITY && v < VM.PLUS_INFINITY) {
5191
+ v *= (1 + MODEL.sensitivity_delta * 0.01);
5192
+ }
5193
+ }
5194
+ x.push(v);
5187
5195
  } else if(xv) {
5188
5196
  // Variable references an earlier value computed for this expression `x`
5189
5197
  x.push(t >= 0 && t < x.vector.length ? x.vector[t] : obj.dv);
@@ -5316,8 +5324,10 @@ function VMI_push_dataset_modifier(x, args) {
5316
5324
  tot[1] + (tot[2] ? ':' + tot[2] : ''), ' value = ', VM.sig4Dig(v));
5317
5325
  console.log(' --', x.text, ' for owner ', x.object.displayName, x.attribute);
5318
5326
  }
5319
- // NOTE: unless error, push default value if exceptional ("undefined", etc.)
5320
- x.push(v < VM.PLUS_INFINITY ? v : ds.defaultValue);
5327
+ // NOTE: if value is exceptional ("undefined", etc.), use default value
5328
+ if(v >= VM.PLUS_INFINITY) v = ds.defaultValue;
5329
+ // Finally, push the value onto the expression stack
5330
+ x.push(v);
5321
5331
  }
5322
5332
 
5323
5333