polynomials.py 6.07 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
# -*- coding: utf-8 -*-
"""
$Id$

Copyright 2008 Lode Leroy

This file is part of PyCAM.

PyCAM is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

PyCAM is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with PyCAM.  If not, see <http://www.gnu.org/licenses/>.
"""

sumpfralle's avatar
sumpfralle committed
23 24
import math
from math import sqrt
lode_leroy's avatar
lode_leroy committed
25 26 27

# see BRL-CAD/src/libbn/poly.c

sumpfralle's avatar
sumpfralle committed
28 29 30 31 32 33 34 35
EPSILON = 1e-4
SMALL = 1e-4
INV_2 = 0.5
INV_3 = 1.0 / 3.0
INV_4 = 0.25
INV_27 = 1.0 / 27.0
SQRT3 = sqrt(3.0)
PI_DIV_3 = math.pi / 3.0
lode_leroy's avatar
lode_leroy committed
36 37

def near_zero(x, epsilon=EPSILON):
sumpfralle's avatar
sumpfralle committed
38
    if abs(x) < epsilon:
lode_leroy's avatar
lode_leroy committed
39 40 41 42 43
        return True
    else:
        return False

def cuberoot(x):
sumpfralle's avatar
sumpfralle committed
44 45
    if x >= 0:
        return pow(x, INV_3)
lode_leroy's avatar
lode_leroy committed
46
    else:
sumpfralle's avatar
sumpfralle committed
47
        return -pow(-x, INV_3)
lode_leroy's avatar
lode_leroy committed
48

sumpfralle's avatar
sumpfralle committed
49
def poly1_roots(a, b):
lode_leroy's avatar
lode_leroy committed
50 51
    if near_zero(a):
        return None
sumpfralle's avatar
sumpfralle committed
52
    return (-b / a, )
lode_leroy's avatar
lode_leroy committed
53

sumpfralle's avatar
sumpfralle committed
54 55
def poly2_roots(a, b, c):
    d = b * b - 4 * a * c
lode_leroy's avatar
lode_leroy committed
56 57 58
    if d < 0:
        return None
    if near_zero(a):
sumpfralle's avatar
sumpfralle committed
59
        return poly1_roots(b, c)
lode_leroy's avatar
lode_leroy committed
60
    if d == 0:
sumpfralle's avatar
sumpfralle committed
61
        return (-b / (2 * a), )
lode_leroy's avatar
lode_leroy committed
62
    q = sqrt(d)
sumpfralle's avatar
sumpfralle committed
63 64
    if a < 0:
        return ((-b + q) / (2 * a), (-b - q) / (2 * a))
lode_leroy's avatar
lode_leroy committed
65
    else:
sumpfralle's avatar
sumpfralle committed
66
        return ((-b - q) / (2 * a), (-b + q) / (2 * a))
lode_leroy's avatar
lode_leroy committed
67

sumpfralle's avatar
sumpfralle committed
68
def poly3_roots(a, b, c, d):
lode_leroy's avatar
lode_leroy committed
69
    if near_zero(a):
sumpfralle's avatar
sumpfralle committed
70 71 72 73 74 75 76 77 78 79 80
        return poly2_roots(b, c, d)
    c1 = b / a
    c2 = c / a
    c3 = d / a

    c1_3 = c1 * INV_3
    a = c2 -c1 * c1_3
    b = (2 * c1 * c1 * c1 - 9 * c1 * c2 + 27 * c3) * INV_27
    delta = a * a
    delta = b * b * INV_4 + delta * a * INV_27
    if delta > 0:
lode_leroy's avatar
lode_leroy committed
81
        r_delta = sqrt(delta)
sumpfralle's avatar
sumpfralle committed
82 83
        A = -INV_2 * b + r_delta
        B = -INV_2 * b - r_delta
lode_leroy's avatar
lode_leroy committed
84 85
        A = cuberoot(A)
        B = cuberoot(B)
sumpfralle's avatar
sumpfralle committed
86 87 88
        return (A + B - c1_3, )
    elif delta == 0:
        b_2 = -b * INV_2
lode_leroy's avatar
lode_leroy committed
89
        s = cuberoot(b_2)
sumpfralle's avatar
sumpfralle committed
90
        return (2 * s - c1_3, -s - c1_3, -s - c1_3, )
lode_leroy's avatar
lode_leroy committed
91
    else:
sumpfralle's avatar
sumpfralle committed
92
        if a > 0:
lode_leroy's avatar
lode_leroy committed
93 94 95 96 97 98 99
            fact = 0
            phi = 0
            cs_phi = 1.0
            sn_phi_s3 = 0.0
        else:
            a *= -INV_3
            fact = sqrt(a)
sumpfralle's avatar
sumpfralle committed
100
            f = -b * INV_2 / (a * fact)
lode_leroy's avatar
lode_leroy committed
101 102 103 104 105 106
            if f >= 1.0:
                phi = 0
                cs_phi = 1.0
                sn_phi_s3 = 0.0
            elif f <= -1.0:
                phi = PI_DIV_3
sumpfralle's avatar
sumpfralle committed
107 108
                cs_phi = math.cos(phi)
                sn_phi_s3 = math.sin(phi) * SQRT3
lode_leroy's avatar
lode_leroy committed
109
            else:
sumpfralle's avatar
sumpfralle committed
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
                phi = math.acos(f) * INV_3
                cs_phi = math.cos(phi)
                sn_phi_s3 = math.sin(phi) * SQRT3
        r1 = 2 * fact * cs_phi
        r2 = fact * ( sn_phi_s3 - cs_phi)
        r3 = fact * (-sn_phi_s3 - cs_phi)
        return (r1 - c1_3, r2 - c1_3, r3 - c1_3)

def poly4_roots(a, b, c, d, e):
    if a == 0:
        return poly3_roots(b, c, d, e)
    c1 = float(b) / a
    c2 = float(c) / a
    c3 = float(d) / a
    c4 = float(e) / a
    roots3 = poly3_roots(1.0, -c2, c3 * c1 - 4 * c4, -c3 * c3 - c4 * c1 * c1 \
            + 4 * c4 * c2)
lode_leroy's avatar
lode_leroy committed
127 128
    if not roots3:
        return None
sumpfralle's avatar
sumpfralle committed
129
    if len(roots3) == 1:
lode_leroy's avatar
lode_leroy committed
130 131
        U = roots3[0]
    else:
sumpfralle's avatar
sumpfralle committed
132 133
        U = max(roots3[0], roots3[1], roots3[2])
    p = c1 * c1 * INV_4 + U - c2
lode_leroy's avatar
lode_leroy committed
134
    U *= INV_2
sumpfralle's avatar
sumpfralle committed
135 136 137
    q = U * U - c4
    if p < 0:
        if p < -SMALL:
lode_leroy's avatar
lode_leroy committed
138 139 140 141
            return None
        p = 0
    else:
        p = sqrt(p)
sumpfralle's avatar
sumpfralle committed
142 143
    if q < 0:
        if q < -SMALL:
lode_leroy's avatar
lode_leroy committed
144 145 146 147 148
            return None
        q = 0
    else:
        q = sqrt(q)

sumpfralle's avatar
sumpfralle committed
149 150
    quad1 = [1.0, c1 * INV_2 - p, 0]
    quad2 = [1.0, c1 * INV_2 + p, 0]
lode_leroy's avatar
lode_leroy committed
151 152 153

    q1 = U - q
    q2 = U + q
sumpfralle's avatar
sumpfralle committed
154
    p = quad1[1] * q2 + quad2[1] * q1 - c3
lode_leroy's avatar
lode_leroy committed
155 156 157 158
    if near_zero(p):
        quad1[2] = q1
        quad2[2] = q2
    else:
sumpfralle's avatar
sumpfralle committed
159
        q = quad1[1] * q1 + quad2[1] * q2 - c3
lode_leroy's avatar
lode_leroy committed
160 161 162 163 164 165 166 167 168 169
        if near_zero(q):
            quad1[2] = q2
            quad2[2] = q1
        else:
            return None
#    print "f1(x)=%g*x**2%+g*x%+g" % (quad1[0], quad1[1], quad1[2])
#    print "f2(x)=%g*x**2%+g*x%+g" % (quad2[0], quad2[1], quad2[2])
    roots1 = poly2_roots(quad1[0], quad1[1], quad1[2])
    roots2 = poly2_roots(quad2[0], quad2[1], quad2[2])
    if roots1 and roots2:
sumpfralle's avatar
sumpfralle committed
170
        return roots1 + roots2
lode_leroy's avatar
lode_leroy committed
171 172 173 174 175 176 177
    elif roots1:
        return roots1
    elif roots2:
        return roots2
    else:
        return None

sumpfralle's avatar
sumpfralle committed
178 179 180
def test_poly1(a, b):
    roots = poly1_roots(a, b)
    print a, "*x+", b, "=0 ", roots
lode_leroy's avatar
lode_leroy committed
181 182
    if roots:
        for r in roots:
sumpfralle's avatar
sumpfralle committed
183
            f = a * r + b
lode_leroy's avatar
lode_leroy committed
184 185
            if not near_zero(f):
                print "ERROR:",
sumpfralle's avatar
sumpfralle committed
186
            print "    f(%f)=%f" % (r, f)
lode_leroy's avatar
lode_leroy committed
187

sumpfralle's avatar
sumpfralle committed
188 189 190
def test_poly2(a, b, c):
    roots = poly2_roots(a, b, c)
    print a, "*x^2+", b, "*x+", c, "=0 ", roots
lode_leroy's avatar
lode_leroy committed
191 192
    if roots:
        for r in roots:
sumpfralle's avatar
sumpfralle committed
193
            f = a * r * r + b * r + c
lode_leroy's avatar
lode_leroy committed
194 195
            if not near_zero(f):
                print "ERROR:",
sumpfralle's avatar
sumpfralle committed
196
            print "    f(%f)=%f" % (r, f)
lode_leroy's avatar
lode_leroy committed
197

sumpfralle's avatar
sumpfralle committed
198 199 200
def test_poly3(a, b, c, d):
    roots = poly3_roots(a, b, c, d)
    print a, "*x^3+", b, "*x^2+", c, "*x+", d, "=0 ", roots
lode_leroy's avatar
lode_leroy committed
201 202
    if roots:
        for r in roots:
sumpfralle's avatar
sumpfralle committed
203
            f = a * r * r * r + b * r * r + c * r + d
lode_leroy's avatar
lode_leroy committed
204 205
            if not near_zero(f):
                print "ERROR:",
sumpfralle's avatar
sumpfralle committed
206
            print "    f(%f)=%f" % (r, f)
lode_leroy's avatar
lode_leroy committed
207

sumpfralle's avatar
sumpfralle committed
208 209 210
def test_poly4(a, b, c, d, e):
    roots = poly4_roots(a, b, c, d, e)
    print "f(x)=%g*x**4%+g*x**3%+g*x**2%+g*x%+g" % (a, b, c, d, e)
lode_leroy's avatar
lode_leroy committed
211 212 213
    print "roots:", roots
    if roots:
        for r in roots:
sumpfralle's avatar
sumpfralle committed
214
            f = a * r * r * r * r + b * r * r * r + c * r * r + d * r + e
lode_leroy's avatar
lode_leroy committed
215 216
            if not near_zero(f, epsilon=SMALL):
                print "ERROR:",
sumpfralle's avatar
sumpfralle committed
217
            print "    f(%f)=%f" % (r, f)
lode_leroy's avatar
lode_leroy committed
218 219 220
    return roots

if __name__ == "__main__":
sumpfralle's avatar
sumpfralle committed
221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238
    test_poly1(1, 2)

    test_poly2(1, 2, 0)
    test_poly2(1, 2, 1)
    test_poly2(1, 2, 2)

    test_poly3(1, 0, 0, 0)
    test_poly3(1, 0, 0, -1)
    test_poly3(1, -1, 0, 0)
    test_poly3(1, 0, -2, 0)
    test_poly3(1, 0, -2, 1)

    test_poly4(1, 0, 0, 0, 0)
    test_poly4(1, 0, 0, 0, -1)
    test_poly4(1, 0, -2, 0, 1)
    test_poly4(1, -10, 35, -50, +24)
    test_poly4(1, 0, 6, -60, 36)
    test_poly4(1, -25, 235.895, -995.565, 1585.25)
lode_leroy's avatar
lode_leroy committed
239