fable 3.1.34 → 3.1.36
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 +231 -175
- 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-ExpressionTokenizer-DirectiveMutation.js +140 -3
- package/source/services/Fable-Service-ExpressionParser/Fable-Service-ExpressionParser-FunctionMap.json +5 -0
- package/source/services/Fable-Service-ExpressionParser/Fable-Service-ExpressionParser-Linter.js +1 -1
- package/source/services/Fable-Service-ExpressionParser/Fable-Service-ExpressionParser-Postfix.js +1 -1
- package/source/services/Fable-Service-ExpressionParser.js +214 -0
- package/source/services/Fable-Service-Math.js +22 -0
- package/source/services/Fable-Service-Utility.js +76 -0
- package/test/ExpressionParser_tests.js +132 -0
- package/test/Utility_tests.js +40 -1
|
@@ -461,6 +461,68 @@ suite
|
|
|
461
461
|
Expect(parseFloat(tmpResult)).to.be.at.most(13.78);
|
|
462
462
|
|
|
463
463
|
|
|
464
|
+
return fDone();
|
|
465
|
+
}
|
|
466
|
+
);
|
|
467
|
+
test
|
|
468
|
+
(
|
|
469
|
+
'Test map operation',
|
|
470
|
+
(fDone) =>
|
|
471
|
+
{
|
|
472
|
+
let testFable = new libFable();
|
|
473
|
+
let _Parser = testFable.instantiateServiceProviderIfNotExists('ExpressionParser');
|
|
474
|
+
|
|
475
|
+
|
|
476
|
+
let tmpManifest = testFable.newManyfest();
|
|
477
|
+
let tmpDataSourceObject = {};
|
|
478
|
+
tmpDataSourceObject.CitiesData = require('./data/Cities.json');
|
|
479
|
+
/* Records look like:
|
|
480
|
+
[
|
|
481
|
+
{
|
|
482
|
+
"city": "New York",
|
|
483
|
+
"growth_from_2000_to_2013": "4.8%",
|
|
484
|
+
"latitude": 40.7127837,
|
|
485
|
+
"longitude": -74.0059413,
|
|
486
|
+
"population": "8405837",
|
|
487
|
+
"rank": "1",
|
|
488
|
+
"state": "New York"
|
|
489
|
+
},
|
|
490
|
+
{
|
|
491
|
+
"city": "Los Angeles",
|
|
492
|
+
"growth_from_2000_to_2013": "4.8%",
|
|
493
|
+
"latitude": 34.0522342,
|
|
494
|
+
"longitude": -118.2436849,
|
|
495
|
+
"population": "3884307",
|
|
496
|
+
"rank": "2",
|
|
497
|
+
"state": "California"
|
|
498
|
+
},
|
|
499
|
+
*/
|
|
500
|
+
|
|
501
|
+
tmpDataSourceObject.MapTest = {};
|
|
502
|
+
tmpDataSourceObject.MapTest.Values = [ 1, 2, 3, 4, 5 ];
|
|
503
|
+
|
|
504
|
+
let tmpDataDestinationObject = {};
|
|
505
|
+
let tmpParserResultsObject = {};
|
|
506
|
+
|
|
507
|
+
let tmpResult = _Parser.solve('SimpleMapResult = MAP VAR x FROM MapTest.Values : x + 100', tmpDataSourceObject, tmpParserResultsObject, tmpManifest, tmpDataDestinationObject);
|
|
508
|
+
|
|
509
|
+
// Should expect 101 -> 105
|
|
510
|
+
Expect(tmpDataDestinationObject.SimpleMapResult.length).to.equal(5);
|
|
511
|
+
Expect(tmpDataDestinationObject.SimpleMapResult[0]).to.equal("101");
|
|
512
|
+
Expect(tmpDataDestinationObject.SimpleMapResult[1]).to.equal("102");
|
|
513
|
+
Expect(tmpDataDestinationObject.SimpleMapResult[2]).to.equal("103");
|
|
514
|
+
Expect(tmpDataDestinationObject.SimpleMapResult[3]).to.equal("104");
|
|
515
|
+
Expect(tmpDataDestinationObject.SimpleMapResult[4]).to.equal("105");
|
|
516
|
+
|
|
517
|
+
let tmpComplexMapResult = _Parser.solve('ComplexMapResult = MAP VAR cityRecord FROM CitiesData VAR x FROM MapTest.Values : cityRecord.population + (x * 1000000000000000)', tmpDataSourceObject, tmpParserResultsObject, tmpManifest, tmpDataDestinationObject);
|
|
518
|
+
|
|
519
|
+
Expect(tmpDataDestinationObject.ComplexMapResult.length).to.equal(1000);
|
|
520
|
+
Expect(tmpDataDestinationObject.ComplexMapResult[0]).to.equal("1000000008405837");
|
|
521
|
+
Expect(tmpDataDestinationObject.ComplexMapResult[4]).to.equal("5000000001553165");
|
|
522
|
+
// We ran out of values 1-5 so now it's just population
|
|
523
|
+
Expect(tmpDataDestinationObject.ComplexMapResult[10]).to.equal("885400");
|
|
524
|
+
Expect(tmpDataDestinationObject.ComplexMapResult[400]).to.equal("81050");
|
|
525
|
+
|
|
464
526
|
return fDone();
|
|
465
527
|
}
|
|
466
528
|
);
|
|
@@ -477,6 +539,13 @@ suite
|
|
|
477
539
|
let tmpDataDestinationObject = {};
|
|
478
540
|
let tmpParserResultsObject = {};
|
|
479
541
|
|
|
542
|
+
/* FIXME: This test is broken since `n` doesn't result in a valid postfix expression for the series generation.
|
|
543
|
+
let tmpPassthroughResult = _Parser.solve('IntegrationApproximationResult = SERIES FROM 13.2 TO 25 STEP 0.2 : n', tmpDataSourceObject, tmpParserResultsObject, tmpManifest, tmpDataDestinationObject);
|
|
544
|
+
Expect(tmpDataDestinationObject.IntegrationApproximationResult[0]).to.equal("13.2");
|
|
545
|
+
Expect(tmpDataDestinationObject.IntegrationApproximationResult[1]).to.equal("13.4");
|
|
546
|
+
Expect(tmpDataDestinationObject.IntegrationApproximationResult[59]).to.equal("25");
|
|
547
|
+
*/
|
|
548
|
+
|
|
480
549
|
// Approximate an integration of the function 1000 + (n / 2) from 13.2 and 25 every 0.2 values
|
|
481
550
|
let tmpResult = _Parser.solve('IntegrationApproximationResult = SERIES FROM 13.2 TO 25 STEP 0.2 : 1000 + (n / 2)', tmpDataSourceObject, tmpParserResultsObject, tmpManifest, tmpDataDestinationObject);
|
|
482
551
|
Expect(tmpDataDestinationObject.IntegrationApproximationResult[0]).to.equal("1006.6");
|
|
@@ -506,6 +575,69 @@ suite
|
|
|
506
575
|
}
|
|
507
576
|
);
|
|
508
577
|
test
|
|
578
|
+
(
|
|
579
|
+
'Test monte carlo...',
|
|
580
|
+
(fDone) =>
|
|
581
|
+
{
|
|
582
|
+
let testFable = new libFable();
|
|
583
|
+
let _Parser = testFable.instantiateServiceProviderIfNotExists('ExpressionParser');
|
|
584
|
+
let tmpManifest = testFable.newManyfest();
|
|
585
|
+
|
|
586
|
+
let tmpDataSourceObject = {};
|
|
587
|
+
let tmpDataDestinationObject = {};
|
|
588
|
+
let tmpParserResultsObject = {};
|
|
589
|
+
|
|
590
|
+
// Approximate an integration of the function 1000 + (n / 2) from 13.2 and 25 every 0.2 values
|
|
591
|
+
let tmpResult = _Parser.solve('MonteCarloResult = MONTECARLO SAMPLECOUNT 1000 VAR x PT x 50 PT x 100 : 10000000 + x', tmpDataSourceObject, tmpParserResultsObject, tmpManifest, tmpDataDestinationObject);
|
|
592
|
+
|
|
593
|
+
// Generate 1000 samples between 50 and 100, added to 10 million
|
|
594
|
+
Expect(tmpResult).to.exist;
|
|
595
|
+
Expect(tmpResult.Samples.length).to.equal(1000);
|
|
596
|
+
|
|
597
|
+
// Rosenbrock function test (experimenting with non-deterministic test cases)
|
|
598
|
+
/* Interesting values:
|
|
599
|
+
|
|
600
|
+
| Purpose | (x) | (y) | (z) | Notes |
|
|
601
|
+
| ----------------------------------------- | ----------------- | ----------------- | ----------------- | ------------------------------------------------------------------------------- |
|
|
602
|
+
| **Exploration (broad view)** | ([-3, 3]) | ([-1, 5]) | ([-1, 5]) | Shows how the function grows large and steep outside the valley. |
|
|
603
|
+
| **Focused optimization testing** | ([0, 2]) | ([0, 2]) | ([0, 2]) | Focuses on region around the minimum — ideal for Monte Carlo convergence plots. |
|
|
604
|
+
| **High-resolution contour visualization** | ([-1.5, 1.5]) | ([0, 2]) | ([0, 2]) | Best for plotting slices and seeing the curvature of the valley. |
|
|
605
|
+
| **Stress testing** | ([-2.048, 2.048]) | ([-2.048, 2.048]) | ([-2.048, 2.048]) | Tests the function's behavior over a wider range, useful for robustness checks. |
|
|
606
|
+
*/
|
|
607
|
+
let tmpRosenbrockDataSourceObject = {};
|
|
608
|
+
let tmpRosenbrockDataDestinationObject = {};
|
|
609
|
+
let tmpRosenbrockParserResultsObject = {};
|
|
610
|
+
let tmpRosenbrockResult = _Parser.solve('RosenbrockResult = MONTECARLO SAMPLECOUNT 5000 VAR x PT x -3 PT x 3 VAR y PT y -1 PT y 5 VAR z PT z -1 PT z 5 : (1 - x)^2 + 100 * (y - x^2)^2 + (z - y)^2', tmpRosenbrockDataSourceObject, tmpRosenbrockParserResultsObject, tmpManifest, tmpRosenbrockDataDestinationObject);
|
|
611
|
+
|
|
612
|
+
Expect(tmpRosenbrockResult).to.exist;
|
|
613
|
+
Expect(tmpRosenbrockResult.Samples.length).to.equal(5000);
|
|
614
|
+
|
|
615
|
+
// Build a histogram of the results to see how many are near zero
|
|
616
|
+
let tmpHistogram = [];
|
|
617
|
+
for (let sampleIndex = 0; sampleIndex < tmpRosenbrockResult.Samples.length; sampleIndex++)
|
|
618
|
+
{
|
|
619
|
+
let tmpSample = tmpRosenbrockResult.Samples[sampleIndex];
|
|
620
|
+
let tmpValue = testFable.Math.roundPrecise(tmpSample, 0);
|
|
621
|
+
if (!tmpHistogram[tmpValue])
|
|
622
|
+
tmpHistogram[tmpValue] = 0;
|
|
623
|
+
tmpHistogram[tmpValue] = tmpHistogram[tmpValue] + 1;
|
|
624
|
+
// Log the output sample for visual inspection
|
|
625
|
+
testFable.log.info(`Rosenbrock Sample ${sampleIndex}: Value: ${tmpSample}, Rounded Value: ${tmpValue} --> Histogram Count: ${tmpHistogram[tmpValue]}`);
|
|
626
|
+
}
|
|
627
|
+
// Output the histogram for visual inspection
|
|
628
|
+
//testFable.ExpressionParser.Messaging.logMessage("Rosenbrock Histogram:");
|
|
629
|
+
let tmpHistogramKeys = Object.keys(tmpHistogram);
|
|
630
|
+
tmpHistogramKeys.sort((a, b) => parseFloat(a) - parseFloat(b));
|
|
631
|
+
for (let i = 0; i < tmpHistogramKeys.length; i++)
|
|
632
|
+
{
|
|
633
|
+
let key = tmpHistogramKeys[i];
|
|
634
|
+
testFable.log.info(`Value: ${key}, Count: ${tmpHistogram[key]}`);
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
return fDone();
|
|
638
|
+
}
|
|
639
|
+
);
|
|
640
|
+
test
|
|
509
641
|
(
|
|
510
642
|
'Complex Histogram Arithmatic',
|
|
511
643
|
(fDone) =>
|
package/test/Utility_tests.js
CHANGED
|
@@ -525,6 +525,45 @@ suite
|
|
|
525
525
|
}
|
|
526
526
|
);
|
|
527
527
|
test
|
|
528
|
+
(
|
|
529
|
+
'match returns an index into an array of values',
|
|
530
|
+
function()
|
|
531
|
+
{
|
|
532
|
+
testFable = new libFable();
|
|
533
|
+
|
|
534
|
+
let tmpState = [ '-1', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10' ];
|
|
535
|
+
let tmpResult = testFable.services.Utility.findIndexInternal('4.5', tmpState);
|
|
536
|
+
Expect(tmpResult).to.be.a('number');
|
|
537
|
+
Expect(tmpResult).to.equal(6);
|
|
538
|
+
|
|
539
|
+
tmpResult = testFable.services.Utility.findIndexInternal('11', tmpState);
|
|
540
|
+
Expect(tmpResult).to.be.a('number');
|
|
541
|
+
Expect(tmpResult).to.equal(-1);
|
|
542
|
+
|
|
543
|
+
tmpResult = testFable.services.Utility.findIndexInternal('4', tmpState);
|
|
544
|
+
Expect(tmpResult).to.be.a('number');
|
|
545
|
+
Expect(tmpResult).to.equal(5);
|
|
546
|
+
|
|
547
|
+
tmpResult = testFable.services.Utility.findIndexInternal('4.5', tmpState, '0');
|
|
548
|
+
Expect(tmpResult).to.be.a('number');
|
|
549
|
+
Expect(tmpResult).to.equal(-1);
|
|
550
|
+
|
|
551
|
+
tmpResult = testFable.services.Utility.findIndexInternal('4', tmpState, 0);
|
|
552
|
+
Expect(tmpResult).to.be.a('number');
|
|
553
|
+
Expect(tmpResult).to.equal(5);
|
|
554
|
+
|
|
555
|
+
tmpState = [ '10', '9', '8', '7', '6', '5', '4', '3', '2', '1', '0', '-1' ];
|
|
556
|
+
|
|
557
|
+
tmpResult = testFable.services.Utility.findIndexInternal('4.5', tmpState, '-1');
|
|
558
|
+
Expect(tmpResult).to.be.a('number');
|
|
559
|
+
Expect(tmpResult).to.equal(6);
|
|
560
|
+
|
|
561
|
+
tmpResult = testFable.services.Utility.findIndexInternal('4', tmpState, -1);
|
|
562
|
+
Expect(tmpResult).to.be.a('number');
|
|
563
|
+
Expect(tmpResult).to.equal(6);
|
|
564
|
+
}
|
|
565
|
+
);
|
|
566
|
+
test
|
|
528
567
|
(
|
|
529
568
|
'createArrayFromAbsoluteValues works as expected',
|
|
530
569
|
function(fDone)
|
|
@@ -544,4 +583,4 @@ suite
|
|
|
544
583
|
}
|
|
545
584
|
);
|
|
546
585
|
}
|
|
547
|
-
);
|
|
586
|
+
);
|