Python and Viv · Numerics
Newton's method
What to notice
- Python's
tol=1e-12, max_iter=50defaults become named constants in Viv, which has no default arguments. - The functions are passed as lambdas in both:
lambda x: x * x - 2.0isfn(x) -> x * x - 2.0. raise RuntimeError(...)isstop("..."): it ends the run witherror: Newton did not convergeand the line, and there is no way to catch it. Both programs converge, so neither line runs.cosandsinare builtins in Viv; Python takes them frommath. The values are printed to 12 decimals, which Viv's own libm and the platform's agree on.assert abs(a - b) < tolisassert_near(a, b, tol), which prints the gap when it fails.
Python newton.py · 26 lines
Viv newton.viv · 33 lines
py2viv translated from newton.py
What py2viv adds before the program
(nothing)
(nothing)
39 lines: a header and the guard functions its translation calls
1###
2Translated from newton.py by py2viv 0.2.0 (numeric Python -> Viv).
3Python source sha256: b039eebb050128259e4796e8a7e69fc5f698d1f166bcdbcbe90ee4e7bdf43eea
4Each print(...) is one print(...) line; fmt(x) writes a float as Python's repr does.
5A `raise`, a failed assert and every guard `stop` with Python's exception line.
6###
7
8#| An int result, as Python's int: a -0.0 becomes 0 (an int has no sign on zero), and the run
9#| stops at 2^53, past which a double no longer holds every integer (Python's int does).
10def py_int(x) {
11 if abs(x) >= 9007199254740992 {
12 stop("py2viv: an integer reached 2^53; Python's int is exact there and a double is not")
13 }
14 return x + 0.0
15}
16
17#| a / b with a float: ZeroDivisionError on a zero divisor.
18def py_div_f(a, b) {
19 if b == 0 {
20 stop("ZeroDivisionError: float division by zero")
21 }
22 return a / b
23}
24
25#| math.sqrt(x).
26def py_msqrt(x) {
27 if x < 0 {
28 stop("ValueError: math domain error")
29 }
30 return sqrt(x)
31}
32
33#| The argument of math.sin, cos or tan: ValueError for an infinity.
34def py_mtrig(x) {
35 if abs(x) == 1.0 / 0.0 {
36 stop("ValueError: math domain error")
37 }
38 return x
39}
1 imports and constants
4import math
2TOL = 1e-12
3MAX_ITER = 50
(nothing)
2 Newton's method with a given derivative
7def newton(f, df, x, tol=1e-12, max_iter=50):
8 for k in range(max_iter):
9 step = f(x) / df(x)
10 x -= step
11 print(f" step {k + 1}: x = {x:.12f}")
12 if abs(step) < tol:
13 return x
14 raise RuntimeError("Newton did not converge")
6def newton(f, df, x) {
7 for k to MAX_ITER {
8 step = f(x) / df(x)
9 x -= step
10 print(f" step {k + 1:d}: x = {x:.12f}")
11 if abs(step) < TOL {
12 return x
13 }
14 }
15 stop("Newton did not converge")
16}
45def newton(f, df, x, tol, max_iter) {
46 for k to max_iter {
47 step = py_div_f(f(x), df(x))
48 x -= step
49 print(" step ", fmt((py_int(k + 1)) + 0.0, "d"), ": x = ", fmt(x, ".12f"), sep="")
50 if abs(step) < tol {
51 return x
52 }
53 }
54 stop("RuntimeError: Newton did not converge")
55}
3 the square root of 2
17root2 = newton(lambda x: x * x - 2.0, lambda x: 2.0 * x, 1.0)
18print(f"sqrt(2) ~ {root2:.12f}")
19root2 = newton(fn(x) -> x * x - 2.0, fn(x) -> 2.0 * x, 1.0)
20print(f"sqrt(2) ~ {root2:.12f}")
57def _lam17(x) {
58 return x * x - 2.0
59}
60def _lam17_2(x) {
61 return 2.0 * x
62}
63root2 = newton(_lam17, _lam17_2, 1.0, 1e-12, 50)
64print("sqrt(2) ~", fmt(root2, ".12f"))
4 where cos(x) = x
21dottie = newton(lambda x: math.cos(x) - x, lambda x: -math.sin(x) - 1.0, 1.0)
22print(f"cos(x) = x at {dottie:.12f}")
23dottie = newton(fn(x) -> cos(x) - x, fn(x) -> -sin(x) - 1.0, 1.0)
24print(f"cos(x) = x at {dottie:.12f}")
66def _lam21(x) {
67 return cos(py_mtrig(x)) - x
68}
69def _lam21_2(x) {
70 return -sin(py_mtrig(x)) - 1.0
71}
72dottie = newton(_lam21, _lam21_2, 1.0, 1e-12, 50)
73print("cos(x) = x at", fmt(dottie, ".12f"))
5 checks
25assert abs(root2 - math.sqrt(2.0)) < 1e-12
26assert abs(math.cos(dottie) - dottie) < 1e-12
27test "roots" {
28 assert_near(root2, sqrt(2.0), 1e-12)
29 assert_near(cos(dottie), dottie, 1e-12)
30}
75if not (abs(root2 - py_msqrt(2.0)) < 1e-12) {
76 stop("AssertionError")
77}
78if not (abs(cos(py_mtrig(dottie)) - dottie) < 1e-12) {
79 stop("AssertionError")
80}
What they print
identical — the two programs print the same text.
Dimmed lines are left out of the comparison. Each Viv test block prints one PASS line; the Python checks the same facts with assert, which prints nothing.
$ python3 newton.py
step 1: x = 1.500000000000step 2: x = 1.416666666667step 3: x = 1.414215686275step 4: x = 1.414213562375step 5: x = 1.414213562373step 6: x = 1.414213562373sqrt(2) ~ 1.414213562373step 1: x = 0.750363867840step 2: x = 0.739112890911step 3: x = 0.739085133385step 4: x = 0.739085133215step 5: x = 0.739085133215cos(x) = x at 0.739085133215
$ viv run newton.viv
step 1: x = 1.500000000000step 2: x = 1.416666666667step 3: x = 1.414215686275step 4: x = 1.414213562375step 5: x = 1.414213562373step 6: x = 1.414213562373sqrt(2) ~ 1.414213562373step 1: x = 0.750363867840step 2: x = 0.739112890911step 3: x = 0.739085133385step 4: x = 0.739085133215step 5: x = 0.739085133215cos(x) = x at 0.739085133215PASS roots
$ viv run newton.py (py2viv’s translation)
The translation prints exactly what the Python prints.
step 1: x = 1.500000000000step 2: x = 1.416666666667step 3: x = 1.414215686275step 4: x = 1.414213562375step 5: x = 1.414213562373step 6: x = 1.414213562373sqrt(2) ~ 1.414213562373step 1: x = 0.750363867840step 2: x = 0.739112890911step 3: x = 0.739085133385step 4: x = 0.739085133215step 5: x = 0.739085133215cos(x) = x at 0.739085133215