fable 3.1.36 → 3.1.38
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/dist/fable.js +194 -179
- package/dist/fable.js.map +1 -1
- package/dist/fable.min.js +2 -2
- package/dist/fable.min.js.map +1 -1
- package/package.json +1 -1
- package/source/services/Fable-Service-ExpressionParser/Fable-Service-ExpressionParser-FunctionMap.json +9 -0
- package/source/services/Fable-Service-ExpressionParser.js +32 -18
- package/source/services/Fable-Service-Math.js +26 -5
- package/source/services/Fable-Service-Utility.js +14 -2
- package/test/ExpressionParser_tests.js +235 -1
- package/test/Math_test.js +20 -12
package/package.json
CHANGED
|
@@ -404,6 +404,11 @@
|
|
|
404
404
|
"Address": "fable.Dates.dateFromParts"
|
|
405
405
|
},
|
|
406
406
|
|
|
407
|
+
"slice": {
|
|
408
|
+
"Name": "Slice Array",
|
|
409
|
+
"Address": "fable.Utility.slice"
|
|
410
|
+
},
|
|
411
|
+
|
|
407
412
|
"createvalueobjectbyhashes": {
|
|
408
413
|
"Name": "Create Value Object by Hashes",
|
|
409
414
|
"Address": "fable.Utility.createValueObjectByHashes"
|
|
@@ -441,5 +446,9 @@
|
|
|
441
446
|
"gaussianelimination": {
|
|
442
447
|
"Name": "Solve a System of Linear Equations using Gaussian Elimination",
|
|
443
448
|
"Address": "fable.Math.gaussianElimination"
|
|
449
|
+
},
|
|
450
|
+
"predict": {
|
|
451
|
+
"Name": "Predict Y Values from X Values using a Regression Model",
|
|
452
|
+
"Address": "fable.Math.predictFromRegressionModel"
|
|
444
453
|
}
|
|
445
454
|
}
|
|
@@ -247,13 +247,14 @@ class FableServiceExpressionParser extends libFableServiceBase
|
|
|
247
247
|
this.lintTokenizedExpression(tmpResultsObject.RawTokens, tmpResultsObject);
|
|
248
248
|
this.buildPostfixedSolveList(tmpResultsObject.RawTokens, tmpResultsObject);
|
|
249
249
|
|
|
250
|
+
const tmpManifest = (typeof(pManifest) === 'object') ? pManifest : this.fable.newManyfest();
|
|
250
251
|
if (tmpResultsObject.SolverDirectives.Code == 'SERIES')
|
|
251
252
|
{
|
|
252
253
|
const [ tmpStep , tmpFrom, tmpTo] = this._prepareDirectiveParameters([
|
|
253
254
|
tmpResultsObject.SolverDirectives.Step,
|
|
254
255
|
tmpResultsObject.SolverDirectives.From,
|
|
255
256
|
tmpResultsObject.SolverDirectives.To,
|
|
256
|
-
], [ '1' ], tmpResultsObject, tmpDataSourceObject,
|
|
257
|
+
], [ '1' ], tmpResultsObject, tmpDataSourceObject, tmpManifest);
|
|
257
258
|
|
|
258
259
|
if (isNaN(tmpFrom) || isNaN(tmpTo))
|
|
259
260
|
{
|
|
@@ -305,17 +306,19 @@ class FableServiceExpressionParser extends libFableServiceBase
|
|
|
305
306
|
|
|
306
307
|
for (let i = 0; i <= tmpIterations; i++)
|
|
307
308
|
{
|
|
308
|
-
|
|
309
|
+
const tmpCurrentValueOfN = this.fable.Math.addPrecise(tmpFrom, this.fable.Math.multiplyPrecise(tmpStep, i.toString()));
|
|
310
|
+
const tmpPreviousValueOfN = (i == 0) ? 'false' : this.fable.Math.addPrecise(tmpFrom, this.fable.Math.multiplyPrecise(tmpStep, (i - 1).toString()));
|
|
309
311
|
|
|
310
312
|
// Jimmy up the data source with the current N value, stepIndex and all the other data from the source object
|
|
311
313
|
// This generates a data source object every time on purpose so we can remarshal in values that changed in the destination
|
|
312
314
|
let tmpSeriesStepDataSourceObject = Object.assign({}, tmpDataSourceObject);
|
|
313
315
|
tmpSeriesStepDataSourceObject.n = tmpCurrentValueOfN;
|
|
316
|
+
tmpSeriesStepDataSourceObject.prev_n = tmpPreviousValueOfN;
|
|
314
317
|
tmpSeriesStepDataSourceObject.stepIndex = i;
|
|
315
318
|
|
|
316
|
-
let tmpMutatedValues = this.substituteValuesInTokenizedObjects(tmpResultsObject.PostfixTokenObjects, tmpSeriesStepDataSourceObject, tmpResultsObject,
|
|
319
|
+
let tmpMutatedValues = this.substituteValuesInTokenizedObjects(tmpResultsObject.PostfixTokenObjects, tmpSeriesStepDataSourceObject, tmpResultsObject, tmpManifest);
|
|
317
320
|
|
|
318
|
-
tmpValueArray.push( this.solvePostfixedExpression( tmpResultsObject.PostfixSolveList, tmpDataDestinationObject, tmpResultsObject,
|
|
321
|
+
tmpValueArray.push( this.solvePostfixedExpression( tmpResultsObject.PostfixSolveList, tmpDataDestinationObject, tmpResultsObject, tmpManifest) );
|
|
319
322
|
|
|
320
323
|
for (let j = 0; j < tmpMutatedValues.length; j++)
|
|
321
324
|
{
|
|
@@ -330,7 +333,6 @@ class FableServiceExpressionParser extends libFableServiceBase
|
|
|
330
333
|
tmpAssignmentManifestHash = tmpResultsObject.OriginalRawTokens[0];
|
|
331
334
|
}
|
|
332
335
|
|
|
333
|
-
let tmpManifest = (typeof(pManifest) === 'object') ? pManifest : this.fable.newManyfest();
|
|
334
336
|
tmpManifest.setValueByHash(tmpDataDestinationObject, tmpAssignmentManifestHash, tmpValueArray);
|
|
335
337
|
|
|
336
338
|
return tmpValueArray;
|
|
@@ -348,7 +350,7 @@ class FableServiceExpressionParser extends libFableServiceBase
|
|
|
348
350
|
const tmpVariableDescription = tmpDirectiveValues[tmpVariableKey];
|
|
349
351
|
|
|
350
352
|
// Get the actual value for this variable's address
|
|
351
|
-
tmpVariableDescription.Value =
|
|
353
|
+
tmpVariableDescription.Value = tmpManifest.getValueByHash(tmpDataSourceObject, tmpVariableDescription.Address);
|
|
352
354
|
}
|
|
353
355
|
|
|
354
356
|
// If the first value doesn't have keys, don't do the map.
|
|
@@ -366,11 +368,12 @@ class FableServiceExpressionParser extends libFableServiceBase
|
|
|
366
368
|
// Jimmy up the data source with the current N value, stepIndex and all the other data from the source object
|
|
367
369
|
// This generates a data source object every time on purpose so we can remarshal in values that changed in the destination
|
|
368
370
|
let tmpSeriesStepDataSourceObject = Object.assign({}, tmpDataSourceObject);
|
|
371
|
+
tmpSeriesStepDataSourceObject.stepIndex = i;
|
|
369
372
|
|
|
370
373
|
for (let j = 0; j < tmpDirectiveValueKeys.length; j++)
|
|
371
374
|
{
|
|
372
375
|
const tmpVariableKey = tmpDirectiveValueKeys[j];
|
|
373
|
-
if (!Array.isArray(tmpDirectiveValues[tmpVariableKey].Value) || (tmpDirectiveValues[tmpVariableKey].Value.length <=
|
|
376
|
+
if (!Array.isArray(tmpDirectiveValues[tmpVariableKey].Value) || (tmpDirectiveValues[tmpVariableKey].Value.length <= i))
|
|
374
377
|
{
|
|
375
378
|
tmpSeriesStepDataSourceObject[tmpVariableKey] = 0;
|
|
376
379
|
}
|
|
@@ -378,11 +381,24 @@ class FableServiceExpressionParser extends libFableServiceBase
|
|
|
378
381
|
{
|
|
379
382
|
tmpSeriesStepDataSourceObject[tmpVariableKey] = tmpDirectiveValues[tmpVariableKey].Value[i];
|
|
380
383
|
}
|
|
384
|
+
const tmpPreviousValueKey = `prev_${tmpVariableKey}`;
|
|
385
|
+
if (!Array.isArray(tmpDirectiveValues[tmpVariableKey].Value) || (tmpDirectiveValues[tmpVariableKey].Value.length <= i))
|
|
386
|
+
{
|
|
387
|
+
tmpSeriesStepDataSourceObject[tmpPreviousValueKey] = 0;
|
|
388
|
+
}
|
|
389
|
+
else if (i == 0)
|
|
390
|
+
{
|
|
391
|
+
tmpSeriesStepDataSourceObject[tmpPreviousValueKey] = 'false';
|
|
392
|
+
}
|
|
393
|
+
else
|
|
394
|
+
{
|
|
395
|
+
tmpSeriesStepDataSourceObject[tmpPreviousValueKey] = tmpDirectiveValues[tmpVariableKey].Value[i - 1];
|
|
396
|
+
}
|
|
381
397
|
}
|
|
382
398
|
|
|
383
|
-
let tmpMutatedValues = this.substituteValuesInTokenizedObjects(tmpResultsObject.PostfixTokenObjects, tmpSeriesStepDataSourceObject, tmpResultsObject,
|
|
399
|
+
let tmpMutatedValues = this.substituteValuesInTokenizedObjects(tmpResultsObject.PostfixTokenObjects, tmpSeriesStepDataSourceObject, tmpResultsObject, tmpManifest);
|
|
384
400
|
|
|
385
|
-
tmpValueArray.push( this.solvePostfixedExpression( tmpResultsObject.PostfixSolveList, tmpDataDestinationObject, tmpResultsObject,
|
|
401
|
+
tmpValueArray.push( this.solvePostfixedExpression( tmpResultsObject.PostfixSolveList, tmpDataDestinationObject, tmpResultsObject, tmpManifest) );
|
|
386
402
|
|
|
387
403
|
for (let j = 0; j < tmpMutatedValues.length; j++)
|
|
388
404
|
{
|
|
@@ -397,7 +413,6 @@ class FableServiceExpressionParser extends libFableServiceBase
|
|
|
397
413
|
tmpAssignmentManifestHash = tmpResultsObject.OriginalRawTokens[0];
|
|
398
414
|
}
|
|
399
415
|
|
|
400
|
-
let tmpManifest = (typeof(pManifest) === 'object') ? pManifest : this.fable.newManyfest();
|
|
401
416
|
tmpManifest.setValueByHash(tmpDataDestinationObject, tmpAssignmentManifestHash, tmpValueArray);
|
|
402
417
|
|
|
403
418
|
return tmpValueArray;
|
|
@@ -406,7 +421,7 @@ class FableServiceExpressionParser extends libFableServiceBase
|
|
|
406
421
|
{
|
|
407
422
|
const [ tmpSampleCount ] = this._prepareDirectiveParameters([
|
|
408
423
|
tmpResultsObject.SolverDirectives.SampleCount
|
|
409
|
-
], [ '1' ], tmpResultsObject, tmpDataSourceObject,
|
|
424
|
+
], [ '1' ], tmpResultsObject, tmpDataSourceObject, tmpManifest);
|
|
410
425
|
|
|
411
426
|
if (isNaN(tmpSampleCount))
|
|
412
427
|
{
|
|
@@ -424,7 +439,7 @@ class FableServiceExpressionParser extends libFableServiceBase
|
|
|
424
439
|
{
|
|
425
440
|
let tmpVariableKey = tmpVariableKeys[i];
|
|
426
441
|
let tmpVariableDescription = tmpMonteCarloOutput.Values[tmpVariableKey];
|
|
427
|
-
|
|
442
|
+
|
|
428
443
|
// For each variable, generate its array of sampled values
|
|
429
444
|
tmpVariableDescription.Distribution = {};
|
|
430
445
|
tmpVariableDescription.ValueSequence = [];
|
|
@@ -437,7 +452,7 @@ class FableServiceExpressionParser extends libFableServiceBase
|
|
|
437
452
|
let tmpPointValue = this.fable.Math.parsePrecise(tmpPointToken, NaN);
|
|
438
453
|
if (isNaN(tmpPointValue) && typeof tmpPointToken === 'string' && tmpPointToken.length > 0)
|
|
439
454
|
{
|
|
440
|
-
tmpPointValue =
|
|
455
|
+
tmpPointValue = tmpManifest.getValueByHash(tmpDataSourceObject, tmpPointToken);
|
|
441
456
|
if (!tmpPointValue || (tmpPointValue == null))
|
|
442
457
|
{
|
|
443
458
|
//TODO: Warn?
|
|
@@ -497,8 +512,8 @@ class FableServiceExpressionParser extends libFableServiceBase
|
|
|
497
512
|
// tmpPreviousDomainTranslationAmount = this.fable.Math.addPrecise(tmpPreviousDomainTranslationAmount, tmpVariableDescription.DomainLength);
|
|
498
513
|
// }
|
|
499
514
|
}
|
|
500
|
-
|
|
501
|
-
for (let i = 0; i <= tmpSampleCount - 1; i++)
|
|
515
|
+
|
|
516
|
+
for (let i = 0; i <= Number(tmpSampleCount) - 1; i++)
|
|
502
517
|
{
|
|
503
518
|
// Jimmy up the data source with the current N value, stepIndex and all the other data from the source object
|
|
504
519
|
// This generates a data source object every time on purpose so we can remarshal in values that changed in the destination
|
|
@@ -528,8 +543,8 @@ class FableServiceExpressionParser extends libFableServiceBase
|
|
|
528
543
|
tmpPointManifest.Distribution[tmpDistributionPointValue] = tmpPointManifest.Distribution[tmpDistributionPointValue] + 1;
|
|
529
544
|
}
|
|
530
545
|
|
|
531
|
-
let tmpMutatedValues = this.substituteValuesInTokenizedObjects(tmpResultsObject.PostfixTokenObjects, tmpSeriesStepDataSourceObject, tmpResultsObject,
|
|
532
|
-
tmpMonteCarloOutput.Samples.push( this.solvePostfixedExpression( tmpResultsObject.PostfixSolveList, tmpDataDestinationObject, tmpResultsObject,
|
|
546
|
+
let tmpMutatedValues = this.substituteValuesInTokenizedObjects(tmpResultsObject.PostfixTokenObjects, tmpSeriesStepDataSourceObject, tmpResultsObject, tmpManifest);
|
|
547
|
+
tmpMonteCarloOutput.Samples.push( this.solvePostfixedExpression( tmpResultsObject.PostfixSolveList, tmpDataDestinationObject, tmpResultsObject, tmpManifest ) );
|
|
533
548
|
|
|
534
549
|
for (let j = 0; j < tmpMutatedValues.length; j++)
|
|
535
550
|
{
|
|
@@ -544,7 +559,6 @@ class FableServiceExpressionParser extends libFableServiceBase
|
|
|
544
559
|
tmpAssignmentManifestHash = tmpResultsObject.OriginalRawTokens[0];
|
|
545
560
|
}
|
|
546
561
|
|
|
547
|
-
let tmpManifest = (typeof(pManifest) === 'object') ? pManifest : this.fable.newManyfest();
|
|
548
562
|
tmpManifest.setValueByHash(tmpDataDestinationObject, tmpAssignmentManifestHash, tmpMonteCarloOutput);
|
|
549
563
|
|
|
550
564
|
return tmpMonteCarloOutput;
|
|
@@ -173,8 +173,8 @@ class FableServiceMath extends libFableServiceBase
|
|
|
173
173
|
|
|
174
174
|
/**
|
|
175
175
|
* Adds two values precisely.
|
|
176
|
-
* @param {number} pLeftValue - The left value to be added.
|
|
177
|
-
* @param {number} pRightValue - The right value to be added.
|
|
176
|
+
* @param {number|string} pLeftValue - The left value to be added.
|
|
177
|
+
* @param {number|string} pRightValue - The right value to be added.
|
|
178
178
|
* @returns {string} - The result of adding the two values as a string.
|
|
179
179
|
*/
|
|
180
180
|
addPrecise(pLeftValue, pRightValue)
|
|
@@ -190,8 +190,8 @@ class FableServiceMath extends libFableServiceBase
|
|
|
190
190
|
/**
|
|
191
191
|
* Subtracts two values precisely.
|
|
192
192
|
*
|
|
193
|
-
* @param {number} pLeftValue - The left value to subtract.
|
|
194
|
-
* @param {number} pRightValue - The right value to subtract.
|
|
193
|
+
* @param {number|string} pLeftValue - The left value to subtract.
|
|
194
|
+
* @param {number|string} pRightValue - The right value to subtract.
|
|
195
195
|
* @returns {string} The result of the subtraction as a string.
|
|
196
196
|
*/
|
|
197
197
|
subtractPrecise(pLeftValue, pRightValue)
|
|
@@ -1556,7 +1556,7 @@ class FableServiceMath extends libFableServiceBase
|
|
|
1556
1556
|
*/
|
|
1557
1557
|
leastSquares(pIndependentVariableVectors, pDependentVariableVector)
|
|
1558
1558
|
{
|
|
1559
|
-
const tmpIndependentVariableVectors = Array.isArray(pIndependentVariableVectors[0]) ? pIndependentVariableVectors : pIndependentVariableVectors.map(value => [value]);
|
|
1559
|
+
const tmpIndependentVariableVectors = Array.isArray(pIndependentVariableVectors[0]) ? this.matrixTranspose(pIndependentVariableVectors) : pIndependentVariableVectors.map(value => [value]);
|
|
1560
1560
|
// Add bias term (intercept)
|
|
1561
1561
|
const tmpIndependentVariableMatrixWithBiasTerm = tmpIndependentVariableVectors.map(row => [1, ...row]);
|
|
1562
1562
|
|
|
@@ -1756,6 +1756,27 @@ class FableServiceMath extends libFableServiceBase
|
|
|
1756
1756
|
return this.addPrecise(pEasingConfiguration.DomainRangeStart, tmpScaledValue);
|
|
1757
1757
|
}
|
|
1758
1758
|
}
|
|
1759
|
+
|
|
1760
|
+
/**
|
|
1761
|
+
* Predicts the dependent variable using a regression model.
|
|
1762
|
+
*
|
|
1763
|
+
* @param {Array<number|string>} pRegressionCoefficients - The regression coefficients [b0, b1, ..., bn].
|
|
1764
|
+
* @param {Array<number|string>|number|string} pIndependentVariableVector - The independent variable values [x1, x2, ..., xn] or single value for single variable.
|
|
1765
|
+
*
|
|
1766
|
+
* @return {number|string} - The predicted dependent variable value.
|
|
1767
|
+
*/
|
|
1768
|
+
predictFromRegressionModel(pRegressionCoefficients, pIndependentVariableVector)
|
|
1769
|
+
{
|
|
1770
|
+
let tmpIndependentVariableVector = pIndependentVariableVector;
|
|
1771
|
+
if (!Array.isArray(pIndependentVariableVector))
|
|
1772
|
+
{
|
|
1773
|
+
tmpIndependentVariableVector = [ pIndependentVariableVector ];
|
|
1774
|
+
}
|
|
1775
|
+
return pRegressionCoefficients.slice(1).reduce((sum, b, i) =>
|
|
1776
|
+
{
|
|
1777
|
+
return this.addPrecise(sum, this.multiplyPrecise(b, pIndependentVariableVector[i]));
|
|
1778
|
+
}, pRegressionCoefficients[0]);
|
|
1779
|
+
}
|
|
1759
1780
|
}
|
|
1760
1781
|
|
|
1761
1782
|
module.exports = FableServiceMath;
|
|
@@ -190,9 +190,21 @@ class FableServiceUtility extends libFableServiceBase
|
|
|
190
190
|
return tmpValueArray;
|
|
191
191
|
}
|
|
192
192
|
|
|
193
|
+
slice(pValueArray, pStartIndex, pEndIndex)
|
|
194
|
+
{
|
|
195
|
+
if (!Array.isArray(pValueArray))
|
|
196
|
+
{
|
|
197
|
+
this.fable.log.error('Fable.Math.slice called with non-array value', { pValueArray });
|
|
198
|
+
return [];
|
|
199
|
+
}
|
|
200
|
+
const tmpStartIndex = Number(this.fable.Math.parsePrecise(pStartIndex, '0'));
|
|
201
|
+
const tmpEndIndex = Number(this.fable.Math.parsePrecise(pEndIndex, pValueArray.length.toString()));
|
|
202
|
+
return pValueArray.slice(tmpStartIndex, tmpEndIndex);
|
|
203
|
+
}
|
|
204
|
+
|
|
193
205
|
/**
|
|
194
206
|
* Get a value array by hash/address list from the internal fable/pict state
|
|
195
|
-
* @param {string}
|
|
207
|
+
* @param {string} pValueHashes - The manyfest hash/address of the value to get
|
|
196
208
|
* @param {object} [pManifest] - The manyfest object to use; constructs one inline if not provided
|
|
197
209
|
* @returns {Array} - The value array built from the hash list
|
|
198
210
|
*/
|
|
@@ -215,7 +227,7 @@ class FableServiceUtility extends libFableServiceBase
|
|
|
215
227
|
/**
|
|
216
228
|
* Get a value object from a list of hash/addressese
|
|
217
229
|
* @param {object} pObject - The object to get the value from
|
|
218
|
-
* @param {string}
|
|
230
|
+
* @param {string} pValueHashes - The manyfest hash/address of the value to get
|
|
219
231
|
* @param {object} [pManifest] - The manyfest object to use; constructs one inline if not provided
|
|
220
232
|
* @returns {object} - The value object built from the hash list
|
|
221
233
|
*/
|
|
@@ -188,6 +188,9 @@ suite
|
|
|
188
188
|
Expect(tmpDestinationObject.EGS).to.equal(tmpDestinationObject.EGSR);
|
|
189
189
|
testFable.ExpressionParser.Messaging.logFunctionSolve(tmpResultObject);
|
|
190
190
|
Expect(tmpDestinationObject.EGS).to.equal("0.0061");
|
|
191
|
+
_Parser.solve('MATH_DP=ROUND(1.2345, 5 - 2)',
|
|
192
|
+
tmpDataObject, tmpResultObject, false, tmpDestinationObject);
|
|
193
|
+
Expect(tmpDestinationObject.MATH_DP).to.equal("1.235");
|
|
191
194
|
|
|
192
195
|
tmpDataObject.EJBinderGb1 = '15.5';
|
|
193
196
|
tmpDataObject.EKPctBinderPb2 = '2.55555';
|
|
@@ -475,7 +478,7 @@ suite
|
|
|
475
478
|
|
|
476
479
|
let tmpManifest = testFable.newManyfest();
|
|
477
480
|
let tmpDataSourceObject = {};
|
|
478
|
-
tmpDataSourceObject.CitiesData = require('./data/
|
|
481
|
+
tmpDataSourceObject.CitiesData = require('./data/cities.json');
|
|
479
482
|
/* Records look like:
|
|
480
483
|
[
|
|
481
484
|
{
|
|
@@ -873,6 +876,9 @@ suite
|
|
|
873
876
|
|
|
874
877
|
_Parser.solve('PreciseEquals = If("1.0000000001", "===", "1", "yes", "no")', testFable, tmpResultsObject, false, tmpDestinationObject);
|
|
875
878
|
Expect(tmpDestinationObject.PreciseEquals).to.equal('no');
|
|
879
|
+
|
|
880
|
+
_Parser.solve('Computed = If(AppData.Cities[0].latitude, "<", "50", AppData.Cities[0].latitude - 25, AppData.Cities[0].latitude + 25)', testFable, tmpResultsObject, false, tmpDestinationObject);
|
|
881
|
+
Expect(tmpDestinationObject.Computed).to.equal(testFable.AppData.Cities[0].latitude - 25);
|
|
876
882
|
}
|
|
877
883
|
);
|
|
878
884
|
|
|
@@ -899,6 +905,234 @@ suite
|
|
|
899
905
|
testFable.log.info('Series From Coefficients Result:', testFable.AppData.SeriesFromCoefficients);
|
|
900
906
|
_Parser.solve('IntegratedSeries = SUM(FLATTEN(AppData.SeriesFromCoefficients))', testFable, testFable.AppData, false, testFable.AppData);
|
|
901
907
|
testFable.log.info('Integrated Series Result:', testFable.AppData.IntegratedSeries);
|
|
908
|
+
|
|
909
|
+
const tmpRawData =
|
|
910
|
+
{
|
|
911
|
+
"StandardSelector": "English",
|
|
912
|
+
"MaxDryDensityTable": [
|
|
913
|
+
{
|
|
914
|
+
"MaxDryDensityTable": {
|
|
915
|
+
"f": "4.19",
|
|
916
|
+
"g": "125.7",
|
|
917
|
+
"j": "0.19",
|
|
918
|
+
"l": "109.1",
|
|
919
|
+
"NTest": "",
|
|
920
|
+
"a": "",
|
|
921
|
+
"b": "",
|
|
922
|
+
"d": "13.51",
|
|
923
|
+
"e": "9.32",
|
|
924
|
+
"h": "1.44",
|
|
925
|
+
"i": "1.25",
|
|
926
|
+
"k": "15.2"
|
|
927
|
+
},
|
|
928
|
+
"MaxDryDensityTablec": "10.00"
|
|
929
|
+
},
|
|
930
|
+
{
|
|
931
|
+
"MaxDryDensityTable": {
|
|
932
|
+
"f": "4.01",
|
|
933
|
+
"g": "120.3",
|
|
934
|
+
"j": "0.16",
|
|
935
|
+
"l": "106.5",
|
|
936
|
+
"NTest": "",
|
|
937
|
+
"a": "",
|
|
938
|
+
"b": "",
|
|
939
|
+
"d": "13.34",
|
|
940
|
+
"e": "9.33",
|
|
941
|
+
"h": "1.39",
|
|
942
|
+
"i": "1.23",
|
|
943
|
+
"k": "13"
|
|
944
|
+
},
|
|
945
|
+
"MaxDryDensityTablec": "11.00"
|
|
946
|
+
},
|
|
947
|
+
{
|
|
948
|
+
"MaxDryDensityTable": {
|
|
949
|
+
"f": "4.19",
|
|
950
|
+
"g": "125.7",
|
|
951
|
+
"j": "0.22",
|
|
952
|
+
"l": "106",
|
|
953
|
+
"NTest": "",
|
|
954
|
+
"a": "",
|
|
955
|
+
"b": "",
|
|
956
|
+
"d": "13.51",
|
|
957
|
+
"e": "9.32",
|
|
958
|
+
"h": "1.40",
|
|
959
|
+
"i": "1.18",
|
|
960
|
+
"k": "18.6"
|
|
961
|
+
},
|
|
962
|
+
"MaxDryDensityTablec": "12.00"
|
|
963
|
+
}
|
|
964
|
+
],
|
|
965
|
+
"NuclearTable": [
|
|
966
|
+
{
|
|
967
|
+
"NuclearTable": {},
|
|
968
|
+
"NDD": "0",
|
|
969
|
+
"ADD": "0",
|
|
970
|
+
"NuclearPercentPR": "0"
|
|
971
|
+
}
|
|
972
|
+
],
|
|
973
|
+
"Pulverization": [
|
|
974
|
+
{
|
|
975
|
+
"PTestNo": "1"
|
|
976
|
+
}
|
|
977
|
+
],
|
|
978
|
+
"FMC": [
|
|
979
|
+
{
|
|
980
|
+
"MassOfWater": "0"
|
|
981
|
+
}
|
|
982
|
+
],
|
|
983
|
+
"MetaTemplate": {},
|
|
984
|
+
"Header": {},
|
|
985
|
+
"SM": {
|
|
986
|
+
"SF": "0",
|
|
987
|
+
"SI": "0",
|
|
988
|
+
"SJ": "0"
|
|
989
|
+
},
|
|
990
|
+
"NM": {},
|
|
991
|
+
"Chart": {
|
|
992
|
+
"Slope": "1.18181818181818181818",
|
|
993
|
+
"Intercept": "91.136363636363636363664",
|
|
994
|
+
"MCZeroAtDD": "21.830887491264849755",
|
|
995
|
+
"ShiftToParallel": "3.230887491264849755",
|
|
996
|
+
"WetSideY2": "0.84615384615384615385",
|
|
997
|
+
"WetSideY1": "-36.84746008708272859272",
|
|
998
|
+
"DomainBegin": "13",
|
|
999
|
+
"DomainEnd": "18.6",
|
|
1000
|
+
"WetSideY0": "-6240",
|
|
1001
|
+
"OptimalMoistureContent": "16.27122843513086489567",
|
|
1002
|
+
"DryCount": 16,
|
|
1003
|
+
"WetCount": "16",
|
|
1004
|
+
"TotalCount": "32",
|
|
1005
|
+
"StartPlot": "12",
|
|
1006
|
+
"XValues": [
|
|
1007
|
+
"13.2",
|
|
1008
|
+
"13.4",
|
|
1009
|
+
"13.6",
|
|
1010
|
+
"13.8",
|
|
1011
|
+
"14",
|
|
1012
|
+
"14.2",
|
|
1013
|
+
"14.4",
|
|
1014
|
+
"14.6",
|
|
1015
|
+
"14.8",
|
|
1016
|
+
"15",
|
|
1017
|
+
"15.2",
|
|
1018
|
+
"15.4",
|
|
1019
|
+
"15.6",
|
|
1020
|
+
"15.8",
|
|
1021
|
+
"16",
|
|
1022
|
+
"16.2",
|
|
1023
|
+
"16.4",
|
|
1024
|
+
"16.6",
|
|
1025
|
+
"16.8",
|
|
1026
|
+
"17",
|
|
1027
|
+
"17.2",
|
|
1028
|
+
"17.4",
|
|
1029
|
+
"17.6",
|
|
1030
|
+
"17.8",
|
|
1031
|
+
"18",
|
|
1032
|
+
"18.2",
|
|
1033
|
+
"18.4",
|
|
1034
|
+
"18.6",
|
|
1035
|
+
"18.8",
|
|
1036
|
+
"19",
|
|
1037
|
+
"19.2",
|
|
1038
|
+
"19.4",
|
|
1039
|
+
],
|
|
1040
|
+
"PrimaryRoot": "110.3659972415182948767",
|
|
1041
|
+
"WetCountIntermediate": "43",
|
|
1042
|
+
"ValueLimit": "28"
|
|
1043
|
+
},
|
|
1044
|
+
"Result": "",
|
|
1045
|
+
"FamilyOfCurvesZone": "0.99",
|
|
1046
|
+
"MaxDryDensityEnglish": "0",
|
|
1047
|
+
"MaxDryDensityMetric": "0",
|
|
1048
|
+
"DryX": "15.2",
|
|
1049
|
+
"DryY": "109.1",
|
|
1050
|
+
"AsIsX": "13",
|
|
1051
|
+
"AsIsY": "106.5",
|
|
1052
|
+
"WetX": "18.6",
|
|
1053
|
+
"WetY": "106",
|
|
1054
|
+
"AverageMaxDryDensity": "11",
|
|
1055
|
+
"OptimumMoistureOfTotalMaterial": "0.1",
|
|
1056
|
+
"om": "0.0",
|
|
1057
|
+
"pr": "0.0"
|
|
1058
|
+
};
|
|
1059
|
+
|
|
1060
|
+
const tmpBaseMoistures = tmpRawData.Chart.XValues;
|
|
1061
|
+
//TODO: compute this form above; 3 stages; dry, wet, combined
|
|
1062
|
+
const tmpCombinedDensities = [ '106.7363636','106.9727273','107.2090909','107.4454545','107.6818182','107.9181818',
|
|
1063
|
+
'108.1545455','108.3909091','108.6272727','108.8636364','109.1','109.3363636','109.5727273','109.8090909','110.0454545',
|
|
1064
|
+
'110.2818182','110.1152028','109.7279363','109.3433842','108.9615182','108.5823101','108.2057322','107.8317574',
|
|
1065
|
+
'107.4603587','107.0915096','106.7251839','106.3613559','106','105.6410912','105.2846046','104.9305159','104.5788009'
|
|
1066
|
+
];
|
|
1067
|
+
const tmpMatrix = [];
|
|
1068
|
+
for (let i = 1; i <= 10; ++i)
|
|
1069
|
+
{
|
|
1070
|
+
const tmpPowArray = [];
|
|
1071
|
+
for (let j = 0; j < tmpBaseMoistures.length; ++j)
|
|
1072
|
+
{
|
|
1073
|
+
tmpPowArray.push(testFable.Math.powerPrecise(tmpBaseMoistures[j], i));
|
|
1074
|
+
}
|
|
1075
|
+
tmpMatrix.push(tmpPowArray);
|
|
1076
|
+
}
|
|
1077
|
+
testFable.AppData.FitToDensities = tmpCombinedDensities;
|
|
1078
|
+
testFable.AppData.MoistureMatrix = tmpMatrix;
|
|
1079
|
+
testFable.log.info('Fit To Densities:', testFable.AppData.FitToDensities);
|
|
1080
|
+
testFable.log.info('Moisture Matrix:', testFable.AppData.MoistureMatrix);
|
|
1081
|
+
_Parser.solve('LinearRegressionHand = LINEST(AppData.MoistureMatrix, AppData.FitToDensities)', testFable, testFable.AppData, false, testFable.AppData);
|
|
1082
|
+
testFable.log.info('Density Regression Coefficients:', testFable.AppData.LinearRegressionHand);
|
|
1083
|
+
|
|
1084
|
+
const tmpSolverInstructions =
|
|
1085
|
+
[
|
|
1086
|
+
'ChartDomainBegin = MaxDryDensityTable[0].MaxDryDensityTable.k - 2',
|
|
1087
|
+
'ChartDomainEnd = ChartDomainBegin + (Chart.TotalCount - 1) * 0.2',
|
|
1088
|
+
'XValues = SERIES FROM ChartDomainBegin TO ChartDomainEnd STEP 0.2 : n + 0',
|
|
1089
|
+
'DryCount = MATCH(Chart.OptimalMoistureContent, XValues)',
|
|
1090
|
+
'WetCountIntermediate = 60 - DryCount',
|
|
1091
|
+
'WetCount = IF(DryCount, "<=", 30, DryCount, WetCountIntermediate)',
|
|
1092
|
+
'TotalCount = DryCount + WetCount',
|
|
1093
|
+
'StartPlot = DryCount - 5',
|
|
1094
|
+
'ChartValueLimit = Chart.XValues.length - 1',
|
|
1095
|
+
'DryValues = MAP VAR x FROM Chart.XValues : x * Chart.Slope + Chart.Intercept',
|
|
1096
|
+
'WetValues = MAP VAR x FROM Chart.XValues : 6240 / (x + 100 / 2.7 + Chart.ShiftToParallel)',
|
|
1097
|
+
'CombinedValues = MAP VAR dry FROM DryValues VAR wet FROM WetValues VAR x from XValues : IF(x, "LTE", Chart.OptimalMoistureContent, dry, wet)',
|
|
1098
|
+
'FilteredXValues = MAP VAR x FROM XValues : IF(ABS(Chart.OptimalMoistureContent - x), "LT", 1, x, 0)',
|
|
1099
|
+
'XValueMatrix = createarrayfromabsolutevalues(0, 0, 0, 0, 0, 0, 0, 0, 0, 0)', // Create Placeholders
|
|
1100
|
+
'XValueMatrix[0] = MAP VAR x FROM XValues : x + 0',
|
|
1101
|
+
'XValueMatrix[1] = MAP VAR x FROM XValues : x^2',
|
|
1102
|
+
'XValueMatrix[2] = MAP VAR x FROM XValues : x^3',
|
|
1103
|
+
'XValueMatrix[3] = MAP VAR x FROM XValues : x^4',
|
|
1104
|
+
'XValueMatrix[4] = MAP VAR x FROM XValues : x^5',
|
|
1105
|
+
'XValueMatrix[5] = MAP VAR x FROM XValues : x^6',
|
|
1106
|
+
'XValueMatrix[6] = MAP VAR x FROM XValues : x^7',
|
|
1107
|
+
'XValueMatrix[7] = MAP VAR x FROM XValues : x^8',
|
|
1108
|
+
'XValueMatrix[8] = MAP VAR x FROM XValues : x^9',
|
|
1109
|
+
'XValueMatrix[9] = MAP VAR x FROM XValues : x^10',
|
|
1110
|
+
'LinearRegression = LINEST(XValueMatrix, CombinedValues)',
|
|
1111
|
+
'FilteredXValueVectors = MatrixTranspose(XValueMatrix)',
|
|
1112
|
+
'FittedDensities = MAP VAR x FROM XValues VAR vector FROM FilteredXValueVectors : PREDICT(LinearRegression, vector)',
|
|
1113
|
+
'FilteredDensities = MAP VAR x FROM FilteredXValues VAR prediction FROM FittedDensities : IF(x, "==", 0, 0, prediction)',
|
|
1114
|
+
];
|
|
1115
|
+
const tmpResultsObject = {};
|
|
1116
|
+
for (const tmpInstruction of tmpSolverInstructions)
|
|
1117
|
+
{
|
|
1118
|
+
_Parser.solve(tmpInstruction, tmpRawData, tmpResultsObject, false, tmpRawData);
|
|
1119
|
+
testFable.log.info(`After instruction: ${tmpInstruction}`, tmpRawData[tmpInstruction.split('=')[0].trim()]);
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
Expect(tmpRawData.FilteredDensities[10]).to.equal('0');
|
|
1123
|
+
Expect(Number(tmpRawData.FilteredDensities[11])).to.be.closeTo(109.3, 0.1);
|
|
1124
|
+
Expect(Number(tmpRawData.FilteredDensities[20])).to.be.closeTo(108.6, 0.1);
|
|
1125
|
+
Expect(tmpRawData.FilteredDensities[21]).to.equal('0');
|
|
1126
|
+
|
|
1127
|
+
/*
|
|
1128
|
+
testFable.log.info('linest payload (hand):', { MoistureMatrix: testFable.AppData.MoistureMatrix, FitToDensities: testFable.AppData.FitToDensities });
|
|
1129
|
+
testFable.log.info('linest payload (solver):', { XValueMatrix: tmpRawData.XValueMatrix, CombinedValues: tmpRawData.CombinedValues });
|
|
1130
|
+
|
|
1131
|
+
testFable.log.info('coefficients (hand):', testFable.AppData.LinearRegressionHand);
|
|
1132
|
+
testFable.log.info('coefficients (solver):', tmpRawData.LinearRegression);
|
|
1133
|
+
|
|
1134
|
+
testFable.log.info('filtered densities (solver):', tmpRawData.FilteredDensities);
|
|
1135
|
+
*/
|
|
902
1136
|
}
|
|
903
1137
|
);
|
|
904
1138
|
}
|
package/test/Math_test.js
CHANGED
|
@@ -486,25 +486,33 @@ suite
|
|
|
486
486
|
testFable.log.info('Prediction for [2,1]:', predict(coeffs_1, [2.5]));
|
|
487
487
|
|
|
488
488
|
// Example: predict y from x1 and x2
|
|
489
|
+
/* Example from: https://mathforcollege.com/nm/mws/gen/06reg/mws_gen_reg_spe_multivariate.pdf
|
|
490
|
+
144 18 52
|
|
491
|
+
142 24 40
|
|
492
|
+
124 12 40
|
|
493
|
+
64 30 48
|
|
494
|
+
96 30 32
|
|
495
|
+
92 22 16
|
|
496
|
+
*/
|
|
489
497
|
const X = [
|
|
490
|
-
[
|
|
491
|
-
[
|
|
492
|
-
[3, 1],
|
|
493
|
-
[4, 3]
|
|
498
|
+
[ 18, 24, 12, 30, 30, 22 ],
|
|
499
|
+
[ 52, 40, 40, 48, 32, 16 ],
|
|
494
500
|
];
|
|
495
501
|
|
|
496
|
-
const y = [
|
|
502
|
+
const y = [ 144, 142, 124, 64, 96, 92 ];
|
|
497
503
|
|
|
498
504
|
const coeffs = testFable.Math.leastSquares(X, y);
|
|
499
505
|
testFable.log.info('Coefficients:', coeffs);
|
|
506
|
+
Expect(coeffs.length).to.equal(3); // intercept + 2 variables
|
|
507
|
+
Expect(Number(coeffs[0])).to.be.closeTo(150.166, 0.01);
|
|
508
|
+
Expect(Number(coeffs[1])).to.be.closeTo(-2.731, 0.01);
|
|
509
|
+
Expect(Number(coeffs[2])).to.be.closeTo(0.581, 0.01);
|
|
500
510
|
|
|
501
|
-
testFable.log.info('Prediction for [
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
Expect(Number(predict(coeffs, [4, 3]))).to.be.closeTo(5, 0.25);
|
|
507
|
-
testFable.log.info('Prediction for [2,1]:', predict(coeffs, [2, 1]));
|
|
511
|
+
testFable.log.info('Prediction for [18,52] (training value is 144):', predict(coeffs, [18, 52]));
|
|
512
|
+
Expect(Number(predict(coeffs, [18, 52]))).to.be.closeTo(144, 15);
|
|
513
|
+
|
|
514
|
+
testFable.log.info('Prediction for [22,16] (training value is 92):', predict(coeffs, [22, 16]));
|
|
515
|
+
Expect(Number(predict(coeffs, [22, 16]))).to.be.closeTo(92, 10);
|
|
508
516
|
}
|
|
509
517
|
);
|
|
510
518
|
|