Skip to content

Commit 1a83c9f

Browse files
authored
Merge pull request #1026 from herbie-fp/zane-true-error
Concrete Values for Odyssey
2 parents 6cd3051 + 6ed1a1d commit 1a83c9f

File tree

5 files changed

+315
-118
lines changed

5 files changed

+315
-118
lines changed

infra/testApi.mjs

Lines changed: 105 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import { 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
65
const SAMPLE_SIZE = 8000
76
const FPCoreFormula = '(FPCore (x) (- (sqrt (+ x 1)) (sqrt x)))'
87
const 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)))))'
99
const eval_sample = [[[1], -1.4142135623730951]]
1010

1111
// improve endpoint
@@ -90,7 +90,7 @@ assert.deepEqual(sample.points[1], sample2.points[1])
9090
const explainBody = {
9191
method: 'POST',
9292
body: JSON.stringify({
93-
formula: FPCoreFormula, sample: sample2.points
93+
formula: FPCoreFormula, sample: sample.points
9494
})
9595
}
9696
const explain = await (await fetch(makeEndpoint("/api/explanations"), explainBody)).json()
@@ -144,7 +144,7 @@ assert.deepEqual(calculateAsyncResult.points, [[[1], -1.4142135623730951]])
144144
// Local error endpoint
145145
const localErrorBody = {
146146
method: 'POST', body: JSON.stringify({
147-
formula: FPCoreFormula, sample: sample2.points
147+
formula: FPCoreFormula, sample: sample.points
148148
})
149149
}
150150
const 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.
168168
assert.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
171272
const 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+
}

src/api/sandbox.rkt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@
143143

144144
(define-values (train-pcontext test-pcontext) (partition-pcontext pcontext))
145145
(*pcontext* test-pcontext)
146-
(local-error-as-tree test (*context*)))
146+
(local-error-as-tree (test-input test) (*context*)))
147147

148148
(define (get-explanations test pcontext)
149149
(unless pcontext

src/core/explain.rkt

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -40,16 +40,10 @@
4040
[else #t]))
4141

4242
(define (actual-errors expr pcontext)
43-
44-
(define errs
43+
(match-define (cons subexprs pt-errorss)
4544
(parameterize ([*pcontext* pcontext])
46-
(first (compute-local-errors (list (all-subexpressions expr)) (*context*)))))
47-
48-
(define pruned (make-hash))
49-
(for ([(k v) (in-hash errs)])
50-
(hash-set! pruned k (hash-ref v 'errs)))
51-
(define idk (flip-lists (hash->list pruned)))
52-
(match-define (cons subexprs pt-errorss) idk)
45+
(flip-lists (hash->list (first (compute-local-errors (list (all-subexpressions expr))
46+
(*context*)))))))
5347

5448
(define pt-worst-subexpr
5549
(append* (reap [sow]

0 commit comments

Comments
 (0)