11import { strict as assert } from 'node:assert' ; // use strict equality everywhere
22
33// Future TODO: before this API becomes set in stone/offered publicly, we should change the results of these methods to be just the output data rather than duplicating input values.
4-
54// Reusable testing data
65const SAMPLE_SIZE = 8000
76const FPCoreFormula = '(FPCore (x) (- (sqrt (+ x 1)) (sqrt x)))'
87const FPCoreFormula2 = '(FPCore (x) (- (sqrt (+ x 1))))'
8+ const FPCoreFormula3 = '(FPCore (x) (if (<= (- (sqrt (+ x 1.0)) (sqrt x)) 0.05) (* 0.5 (sqrt (/ 1.0 x))) (fma (fma (- 0.125) x 0.5) x (- 1.0 (sqrt x)))))'
99const eval_sample = [ [ [ 1 ] , - 1.4142135623730951 ] ]
1010
1111// improve endpoint
@@ -90,7 +90,7 @@ assert.deepEqual(sample.points[1], sample2.points[1])
9090const explainBody = {
9191 method : 'POST' ,
9292 body : JSON . stringify ( {
93- formula : FPCoreFormula , sample : sample2 . points
93+ formula : FPCoreFormula , sample : sample . points
9494 } )
9595}
9696const explain = await ( await fetch ( makeEndpoint ( "/api/explanations" ) , explainBody ) ) . json ( )
@@ -144,7 +144,7 @@ assert.deepEqual(calculateAsyncResult.points, [[[1], -1.4142135623730951]])
144144// Local error endpoint
145145const localErrorBody = {
146146 method : 'POST' , body : JSON . stringify ( {
147- formula : FPCoreFormula , sample : sample2 . points
147+ formula : FPCoreFormula , sample : sample . points
148148 } )
149149}
150150const localError = await ( await fetch ( makeEndpoint ( "/api/localerror" ) , localErrorBody ) ) . json ( )
@@ -166,6 +166,107 @@ const localError2 = await (await fetch(makeEndpoint("/api/localerror"), {
166166} ) ) . json ( )
167167// Test that different sample points produce different job ids ensuring that different results are served for these inputs.
168168assert . notEqual ( localError1 . job , localError2 . job )
169+ // Assert local error works for default example.
170+ const ignoredValue = 1e+308
171+ '(FPCore (1e-100) (- (sqrt (+ x 1)) (sqrt x)))'
172+ const localError5 = await ( await fetch ( makeEndpoint ( "/api/localerror" ) , {
173+ method : 'POST' , body : JSON . stringify ( {
174+ formula : FPCoreFormula , sample : [ [ [ 1e-100 ] , ignoredValue ] ] , seed : 5
175+ } )
176+ } ) ) . json ( )
177+
178+ // avg_error, actual_value, exact_value, absolute_difference, ulps_error
179+ // root node
180+ checkLocalErrorNode ( localError5 . tree , [ ] ,
181+ '-' , '0.0' , '1.0' , '1.0' , '1e-50' , '0.0' )
182+ // left sqrt
183+ checkLocalErrorNode ( localError5 . tree , [ 0 ] ,
184+ 'sqrt' , '0.0' , '1.0' , '1.0' , '5e-101' , '0.0' )
185+ // right sqrt
186+ checkLocalErrorNode ( localError5 . tree , [ 1 ] ,
187+ 'sqrt' , '0.0' , '1e-50' , '1e-50' , '2.379726195519099e-68' , '0.0' )
188+ // plus
189+ checkLocalErrorNode ( localError5 . tree , [ 0 , 0 ] ,
190+ '+' , '0.0' , '1.0' , '1.0' , '1e-100' , '0.0' )
191+ // var x
192+ checkLocalErrorNode ( localError5 . tree , [ 0 , 0 , 0 ] ,
193+ 'x' , '0.0' , '1e-100' , '1e-100' , 'equal' , '0.0' )
194+ // literal 1
195+ checkLocalErrorNode ( localError5 . tree , [ 0 , 0 , 1 ] ,
196+ '1.0' , '0.0' , '1.0' , '1.0' , 'equal' , '0.0' )
197+
198+ // '(FPCore (1e100) (- (sqrt (+ x 1)) (sqrt x)))'
199+ const localError6 = await ( await fetch ( makeEndpoint ( "/api/localerror" ) , {
200+ method : 'POST' , body : JSON . stringify ( {
201+ formula : FPCoreFormula , sample : [ [ [ 1e100 ] , ignoredValue ] ] , seed : 5
202+ } )
203+ } ) ) . json ( )
204+ // avg_error, actual_value, exact_value, absolute_error, ulps_error
205+ // root node
206+ checkLocalErrorNode ( localError6 . tree , [ ] ,
207+ '-' , '61.7' , '0.0' , '5e-51' , '5e-51' , '61.74124908607812' )
208+ // left sqrt
209+ checkLocalErrorNode ( localError6 . tree , [ 0 ] ,
210+ 'sqrt' , '0.0' , '1e+50' , '1e+50' , '6.834625285603891e+33' , '0.0' )
211+ // right sqrt
212+ checkLocalErrorNode ( localError6 . tree , [ 1 ] ,
213+ 'sqrt' , '0.0' , '1e+50' , '1e+50' , '6.834625285603891e+33' , '0.0' )
214+ // plus
215+ checkLocalErrorNode ( localError6 . tree , [ 0 , 0 ] ,
216+ '+' , '0.0' , '1e+100' , '1e+100' , '1.0' , '0.0' )
217+ // var x
218+ checkLocalErrorNode ( localError6 . tree , [ 0 , 0 , 0 ] ,
219+ 'x' , '0.0' , '1e+100' , '1e+100' , 'equal' , '0.0' )
220+ // literal 1
221+ checkLocalErrorNode ( localError6 . tree , [ 0 , 0 , 1 ] ,
222+ '1.0' , '0.0' , '1.0' , '1.0' , 'equal' , '0.0' )
223+
224+ // Test a large number `2e269` to trigger NaNs in local error
225+ const localError7 = await ( await fetch ( makeEndpoint ( "/api/localerror" ) , {
226+ method : 'POST' , body : JSON . stringify ( {
227+ formula : FPCoreFormula3 , sample : [ [ [ 2e269 ] , ignoredValue ] ] , seed : 5
228+ } )
229+ } ) ) . json ( )
230+ // Test against conditionals expressions
231+ checkLocalErrorNode ( localError7 . tree , [ 0 ] ,
232+ '<=' , '0.0' , 'true' , 'true' , 'invalid' , '0.0' )
233+ // TODO a bug in Rival
234+ // checkLocalErrorNode(localError7.tree, [0, 0],
235+ // '-', '61.2', '0.0', '1.1180339887498948e-135', '1.1180339887498948e-135', '61.16647760559045')
236+ checkLocalErrorNode ( localError7 . tree , [ 0 , 1 ] ,
237+ '0.05' , '0.0' , '0.05' , '0.05' , 'invalid' , '0.0' )
238+ checkLocalErrorNode ( localError7 . tree , [ 2 ] ,
239+ 'fma' , '0.0' , '-inf.0' , '-inf.0' , 'invalid' , '0.0' )
240+
241+ /// root: The root node of the local error tree.
242+ /// path: the path to get to the node you want to test.
243+ /// name: Name of the node you are testing
244+ /// avg_error: Average Error
245+ /// actual_value: Value of the node
246+ /// exact_value: The correct evaluation of the expression
247+ /// absolute_difference: The ABS of the error at the node |approx - exact|
248+ /// ulps_error: ulps of error at this node.
249+ function checkLocalErrorNode ( root , path , name ,
250+ avg_error , actual_value , exact_value , absolute_difference , ulps_error ) {
251+ const node = getNodeFromPath ( root , path )
252+ // console.log(node) // Helpful for seeing which node is failing a test
253+ assert . equal ( node [ 'e' ] , name )
254+ assert . equal ( node [ 'avg-error' ] , avg_error )
255+ assert . equal ( node [ 'actual-value' ] , actual_value )
256+ assert . equal ( node [ 'exact-value' ] , exact_value )
257+ assert . equal ( node [ 'abs-error-difference' ] , absolute_difference )
258+ assert . equal ( node [ 'ulps-error' ] , ulps_error )
259+ }
260+
261+ function getNodeFromPath ( node , path ) {
262+ if ( path . length > 0 ) {
263+ const index = path . shift ( )
264+ const child = node [ 'children' ] [ index ]
265+ return getNodeFromPath ( child , path )
266+ } else {
267+ return node
268+ }
269+ }
169270
170271// Alternatives endpoint
171272const altBody = {
@@ -257,4 +358,4 @@ async function callAsyncAndWaitJSONResult(endpoint, body) {
257358 }
258359 const result = await fetch ( makeEndpoint ( `/api/result/${ jobJSON . job } ` ) , { method : 'GET' } )
259360 return await result . json ( )
260- }
361+ }
0 commit comments