I'd like to commit the attached patch. It moves functionality to test
approximate equality from elemnum.input to unittest.spad, which turns out tpo
be a good thing, because there was a mistake in elemnum (epsilonrelative
instead of epsilonRelative) that went unnoticed for many revisions, and made us
believe that numerics is better than it was.
I also add a test for branch cuts that shows another feature of the new
functionality: useful error messages.
Waldek, I test values of asin on a circle around the origin with radius 2. Is
this what you had in mind?
Martin
Index: algebra/unittest.spad.pamphlet
===================================================================
--- algebra/unittest.spad.pamphlet (revision 442)
+++ algebra/unittest.spad.pamphlet (working copy)
@@ -334,6 +334,27 @@
++ R performs some simplifications, we convert ex1 and ex2 to
++ \axiom{InputForm}, if possible.
+ if R has RealNumberSystem then
+ testRealEqualsAux: (String, R, R) -> Void
+ ++ testRealEquals(inp, ex1, ex2) states that ex1 and ex2 should be
+ ++ approximately equal as real numbers, taking into acount
+ ++ \spadfun{testAbsolutePrecision} and
+ ++ \spadfun{testRelativePrecision}.
+
+ testComplexEqualsAux: (String, Complex R, Complex R) -> Void
+ ++ testComplexEquals(inp ex1, ex2) states that ex1 and ex2 should be
+ ++ approximately equal as complex numbers, taking into acount
+ ++ \spadfun{testAbsolutePrecision} and
+ ++ \spadfun{testRelativePrecision}.
+
+ testAbsolutePrecision: R -> Void
+ ++ testAbsolutePrecision(eps) sets the absolute precision used for
+ ++ floating point comparison. The default value is 1.0e-15.
+
+ testRelativePrecision: R -> Void
+ ++ testRelativePrecision(eps) sets the relative precision used for
+ ++ floating point comparison. The default value is 1.0e-30.
+
testNotEqualsAux: (String, R, R) -> Void
++ testNotEquals(inp, ex1, ex2) states that ex1 and ex2 should be
++ different.
@@ -345,7 +366,6 @@
++ testLibraryError(inp, ex) states that ex should throw an error. Such
++ a test will never count as a fatal error.
-
== add
O ==> OutputForm
@@ -369,7 +389,57 @@
then incFail(inp, [ex1::O, ex2::O])$U
else incFail(inp, [])$U
+ if R has RealNumberSystem then
+ epsilonRelative := 10::R**(-15)
+ epsilonAbsolute := 10::R**(-15)
+
+ testRelativePrecision eps ==
+ epsilonRelative := eps
+
+ testAbsolutePrecision eps ==
+ epsilonAbsolute := eps
+
+ testRealEqualsAux(inp, ex1, ex2) ==
+ relative: R := norm(ex1/ex2-1)$R
+ absolute: R := norm(ex1-ex2)$R
+ res: List O := []
+ if relative > epsilonRelative then
+ if R has CoercibleTo O
+ then res := cons(hconcat(message("rel err: ")$O,
+ relative::O), res)
+ else res := cons(message("rel err ")$O, res)
+
+ if absolute > epsilonAbsolute then
+ if R has CoercibleTo O
+ then res := cons(hconcat(message("abs err: ")$O,
+ absolute::O), res)
+ else res := cons(message("abs err ")$O, res)
+
+ if empty? res
+ then incPass()$U
+ else incFail(inp, concat([ex1::O, ex2::O], res))$U
+
+ testComplexEqualsAux(inp, ex1, ex2) ==
+ relative := norm(ex1/ex2-1)$Complex(R)
+ absolute := norm(ex1-ex2)$Complex(R)
+ res: List O := []
+ if relative > epsilonRelative then
+ if R has CoercibleTo O
+ then res := cons(hconcat(message("rel err: ")$O,
+ relative::O), res)
+ else res := cons(message("rel err ")$O, res)
+
+ if absolute > epsilonAbsolute then
+ if R has CoercibleTo O
+ then res := cons(hconcat(message("abs err: ")$O,
+ absolute::O), res)
+ else res := cons(message("abs err ")$O, res)
+
+ if empty? res
+ then incPass()$U
+ else incFail(inp, concat([ex1::O, ex2::O], res))$U
+
testNotEqualsAux(inp, ex1, ex2) ==
if (ex1=ex2)@Boolean
then
@@ -412,6 +482,16 @@
++ performs some simplifications, we convert ex1 and ex2 to
++ \axiom{InputForm}, if possible
+ testRealEquals: (String, String) -> Void
+ ++ testRealEquals(ex1, ex2) states that ex1 and ex2 should be
+ ++ approximately equal as real numbers, taking into acount
+ ++ \spadfun{testAbsolutePrecision} and \spadfun{testRelativePrecision}.
+
+ testComplexEquals: (String, String) -> Void
+ ++ testComplexEquals(ex1, ex2) states that ex1 and ex2 should be
+ ++ approximately equal as complex numbers, taking into acount
+ ++ \spadfun{testAbsolutePrecision} and \spadfun{testRelativePrecision}.
+
testNotEquals: (String, String) -> Void
++ testNotEquals(ex1, ex2) states that ex1 and ex2 should be
++ different.
@@ -433,6 +513,18 @@
interpretString("testEqualsAux(_"" inp "_", " s1 ", " s2 ")")$T
decFatal()$UnittestCount
+ testRealEquals(s1, s2) ==
+ inp := "EQLR: (" s1 ", " s2 ")"
+ incFatal(inp)$UnittestCount
+ interpretString("testRealEqualsAux(_"" inp "_", " s1 ", " s2 ")")$T
+ decFatal()$UnittestCount
+
+ testComplexEquals(s1, s2) ==
+ inp := "EQLC: (" s1 ", " s2 ")"
+ incFatal(inp)$UnittestCount
+ interpretString("testComplexEqualsAux(_"" inp "_", " s1 ", " s2 ")")$T
+ decFatal()$UnittestCount
+
testNotEquals(s1, s2) ==
inp := "DIFF: (" s1 ", " s2 ")"
incFatal(inp)$UnittestCount
Index: input/elemnum.input.pamphlet
===================================================================
--- input/elemnum.input.pamphlet (revision 442)
+++ input/elemnum.input.pamphlet (working copy)
@@ -21,17 +21,6 @@
-- SMW June 26, 1991.
testsuite "elemnum"
-epsilonAbsolute := 1.0e-15
-epsilonrelative := 1.0e-30
-
-testFloat(a: String, b: String): Void ==
- testTrue("abs(" a "-" b") < epsilonAbsolute")
- testTrue("abs(" a "/" b "-1) < epsilonRelative")
-
-testComplexFloat(a: String, b: String): Void ==
- testTrue("norm(" a "-" b") < epsilonAbsolute")
- testTrue("norm(" a "/" b "-1) < epsilonRelative")
-
inverses := _
["exp log x", _
"log exp x", _
@@ -53,8 +42,9 @@
errlist := ["acsc x", "asec x", "acosh x", "acoth x"]
for ex in inverses | not anySubstring?(errlist, ex) repeat
- testFloat(ex, "x")
+ testRealEquals(ex, "x")
+-- cannot loop over errlist, since error breaks the loop
testLibraryError "acsc x"
testLibraryError "asec x"
testLibraryError "acosh x"
@@ -64,7 +54,7 @@
testcaseNoClear "0.7 DoubleFloat"
x := 0.7::DoubleFloat
for ex in inverses | not anySubstring?(errlist, ex) repeat
- testFloat(ex, "x")
+ testRealEquals(ex, "x")
testLibraryError "acsc x"
testLibraryError "asec x"
@@ -75,7 +65,7 @@
x := 1.1::Float
errlist := ["asin x", "acos x", "atanh x", "asech x"]
for ex in inverses | not anySubstring?(errlist, ex) repeat
- testFloat(ex, "x")
+ testRealEquals(ex, "x")
testLibraryError "asin x"
testLibraryError "acos x"
@@ -85,7 +75,7 @@
testcaseNoClear "1.1 DoubleFloat"
x := 1.1::DoubleFloat
for ex in inverses | not anySubstring?(errlist, ex) repeat
- testFloat(ex, "x")
+ testRealEquals(ex, "x")
testLibraryError "asin x"
testLibraryError "acos x"
@@ -103,34 +93,67 @@
for ex in inverses repeat
if member?(ex, ["acos cos x", "asec sec x", _
"acosh cosh x", "asech sech x"])
- then testComplexFloat(concat [string s1, "*", ex], "x")
+ then testComplexEquals(concat [string s1, "*", ex], "x")
else if ex = "acot cot x"
- then testComplexFloat(concat [ex, " - ", string m1, "*%pi"], "x")
- else testComplexFloat(ex, "x")
+ then testComplexEquals(concat [ex, " - ", string m1, "*%pi"], "x")
+ else testComplexEquals(ex, "x")
-sa := 0.7::DoubleFloat;
-sb := 1.1::DoubleFloat;
-ba := 0.7::Float;
-bb := 1.1::Float;
-
testcaseNoClear "Complex Float"
)clear value x
x:Complex Float
-testinverseFloats(ba, bb, 1);
-testinverseFloats(ba, bb, 2);
-testinverseFloats(ba, bb, 3);
-testinverseFloats(ba, bb, 4);
+ba := 0.7::Float;
+bb := 1.1::Float;
+testinverseFloats(ba, bb, 1); -- test 0.7 + 1.1*%i
+testinverseFloats(ba, bb, 2); -- test -0.7 + 1.1*%i
+testinverseFloats(ba, bb, 3); -- test -0.7 - 1.1*%i
+testinverseFloats(ba, bb, 4); -- test 0.7 - 1.1*%i
testcaseNoClear "Complex DoubleFloat"
)clear value x
x:Complex DFLOAT
+sa := 0.7::DoubleFloat;
+sb := 1.1::DoubleFloat;
testinverseFloats(sa, sb, 1);
testinverseFloats(sa, sb, 2);
testinverseFloats(sa, sb, 3);
testinverseFloats(sa, sb, 4);
+testsuite "branch cuts"
+testcaseNoClear "asin"
+
+DirD := eval(D(asin(x0+(x1-x0)*t), t)::EXPR FLOAT, t=0)
+tmax := 10
+tdiff := 1.0e-8
+
+-- t = 0..tmax
+-- x = -2..2
+X t == -2.0 + t/tmax * 4.0
+y x == sqrt(-x^2 + 4.0)/2.0
+
+-- t = 0: arg1 is on the negative x-axis, arg2 in quadrant II
+for t in 0..tmax-1 repeat
+ arg1 := (X t + %i * y X t)::Complex Float
+ arg2 := (X(t+tdiff) + %i * y X(t+tdiff))::Complex Float
+ corr := (eval(DirD, [x0=arg1, x1=arg2]))::Complex Float
+ testComplexEquals("asin arg1 + corr", "asin arg2")
+ testComplexEquals("asin(arg1::Complex DFLOAT) + corr",
+ "asin(arg2::Complex DFLOAT)")
+
+X t == 2.0 - t/tmax * 4.0
+y x == -sqrt(-x^2 + 4.0)/2.0
+
+-- t = tmax: arg1 is on the positive x-axis, arg2 in quadrant IV
+for t in 0..tmax-1 repeat
+ arg1 := (X t + %i * y X t)::Complex Float
+ arg2 := (X(t+tdiff) + %i * y X(t+tdiff))::Complex Float
+ corr := (eval(DirD, [x0=arg1, x1=arg2]))::Complex Float
+ testComplexEquals("asin arg1 + corr", "asin arg2")
+ testComplexEquals("asin(arg1::Complex DFLOAT) + corr",
+ "asin(arg2::Complex DFLOAT)")
+
expected() ==
messagePrint("testsuite | testcases: failed (total) | tests: failed
(total)")$OutputForm;
- messagePrint("elemnum 0 (6) 0
(608)")$OutputForm
+ messagePrint("elemnum 0 (6) 0
(312)")$OutputForm;
+ messagePrint("branchcuts 1 (1) 3
(40)")$OutputForm
statistics()
expected()
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups
"FriCAS - computer algebra system" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at
http://groups.google.com/group/fricas-devel?hl=en
-~----------~----~----~----~------~----~------~--~---