Kirby wrote:
There may be a better way to write the factory function than I've shown below. I'd like to see other solutions:
def makepoly(A,B,C): """ Build a polynomial function from coefficients """ return eval("lambda x: %s*x**2 + %s*x + %s" % (A,B,C))
How about this? class poly: def __init__(self, A, B, C): self.A, self.B, self.C = A, B, C def __call__(self, x): return self.A * x**2 + self.B * x + self.C
f = poly(2,3,4) f(10) 234
Having to know about classes pushes you more into learning Python as a programming language rather than just using Python as a math exploration tool, but that could be a good thing. You could even expand on the idea by adding additional methods that relate to polynomials (deriv, integral, roots, etc.): class poly: ... def deriv(self, x): return 2*self.A*x + self.B
f.deriv(2) 11
Brent
Kirby wrote:
There may be a better way to write the factory function than I've shown below. I'd like to see other solutions:
def makepoly(A,B,C): """ Build a polynomial function from coefficients """ return eval("lambda x: %s*x**2 + %s*x + %s" % (A,B,C))
How about this?
class poly: def __init__(self, A, B, C): self.A, self.B, self.C = A, B, C def __call__(self, x): return self.A * x**2 + self.B * x + self.C
f = poly(2,3,4) f(10) 234
I like it! Kirby
I elaborated on Brent's excellent suggestion for a post to k12.ed.math (which you can check via http://www.mathforum.com/epigone/k12.ed.math/ ). The Poly objects are in play:
f = Poly([2,3,4]) f Polynomial: 2*x**2 + 3*x + 4 f(10) 234 f.degree 2 f.deriv() Polynomial: 4*x + 3 df = f.deriv() df(10) 43 g = Poly([-1,0,0,2]) g Polynomial: -1*x**3 + 2 g(-1) 3 dg = g.deriv() dg Polynomial: -3*x**2 dg(-1) -3
Kirby
Kirby Urner writes:
The Poly objects are in play:
f = Poly([2,3,4]) f Polynomial: 2*x**2 + 3*x + 4 f(10) 234 f.degree 2 f.deriv() Polynomial: 4*x + 3 df = f.deriv() df(10) 43 g = Poly([-1,0,0,2]) g Polynomial: -1*x**3 + 2 g(-1) 3 dg = g.deriv() dg Polynomial: -3*x**2 dg(-1) -3
Could this be extended to a symbolic math implementation? At least, could you implement compose, add, subtract, multiply, and divide methods so that f.compose(g) f.add(g) would work? -- Seth David Schoen <schoen@loyalty.org> | And do not say, I will study when I Temp. http://www.loyalty.org/~schoen/ | have leisure; for perhaps you will down: http://www.loyalty.org/ (CAF) | not have leisure. -- Pirke Avot 2:5
Could this be extended to a symbolic math implementation? At least, could you implement compose, add, subtract, multiply, and divide methods so that f.compose(g) f.add(g) would work? -- Seth David Schoen <schoen@loyalty.org> | And do not say, I will study when I Temp. http://www.loyalty.org/~schoen/ | have leisure; for perhaps you will down: http://www.loyalty.org/ (CAF) | not have leisure. -- Pirke Avot 2:5 ============ Sure. Compose was already implied in Brent's class, since f(10) returns a number, and therefore so does g(f(10)) and g(f(10)). Using operator overriding, we can actually use + - and * for addition, subtraction and multiplication (I haven't done divide): Definition and Representation:
f = Poly([1,2,3]) f x**2 + 2*x + 3 g = Poly([2,3,4,5]) g 2*x**3 + 3*x**2 + 4*x + 5
Negation:
-f -x**2 - 2*x - 3
Subtraction:
f-g -2*x**3 - 2*x**2 - 2*x - 2
Evaluation:
g(10) 2345 f(10) 123
Composition:
f(g(10)) 5503718 g(f(10)) 3767618
Multiplication:
f*g 2*x**5 + 7*x**4 + 16*x**3 + 22*x**2 + 22*x + 15 g*f 2*x**5 + 7*x**4 + 16*x**3 + 22*x**2 + 22*x + 15
Evaluation and Multiplication:
x = -1 eval(str(g*f)) 4 h=g*f h(-1) 4
Negation and Multiplication:
-f*g -2*x**5 - 7*x**4 - 16*x**3 - 22*x**2 - 22*x - 15
Derivative:
h 2*x**5 + 7*x**4 + 16*x**3 + 22*x**2 + 22*x + 15 h.deriv() 10*x**4 + 28*x**3 + 48*x**2 + 44*x + 22
I'm pretty sure all of the above is correct. I've put the code I have so far at: http://www.inetarena.com/~pdx4d/ocn/polynomial.py (Python source) http://www.inetarena.com/~pdx4d/ocn/polynomial.html (colorized HTML) Maybe we can improve/streamline the implementation somewhat -- and I wouldn't be surprised if someone, somewhere has implemented something very similar already. The hardest part was getting the algebraic strings to look pretty. Kirby
I've put the code I have so far at:
http://www.inetarena.com/~pdx4d/ocn/polynomial.py (Python source) http://www.inetarena.com/~pdx4d/ocn/polynomial.html (colorized HTML)
Sorry, goofed the URLs: http://www.inetarena.com/~pdx4d/ocn/python/polynomial.py (Python source) http://www.inetarena.com/~pdx4d/ocn/python/polynomial.html (colorized HTML) Kirby
One change I've already made to the Polynomial code (and uploaded): compute the string version only once, upon initialization, and then have __call__ simply evaluate that string, like this: def __init__(self, coeffs): self.coeffs = [] # ... skip some lines self.strview = self.express() # <-- new def __call__(self,x): return eval(self.strview) # short! def __repr__(self): return self.strview # no need to recompute def express(self): """ Represent self as an algebraic expression """ ... Kirby
Kirby Urner writes:
Could this be extended to a symbolic math implementation? At least, could you implement compose, add, subtract, multiply, and divide methods so that
f.compose(g) f.add(g)
would work?
-- Seth David Schoen <schoen@loyalty.org> | And do not say, I will study when I Temp. http://www.loyalty.org/~schoen/ | have leisure; for perhaps you will down: http://www.loyalty.org/ (CAF) | not have leisure. -- Pirke Avot 2:5
============
Sure.
Compose was already implied in Brent's class, since f(10) returns a number, and therefore so does g(f(10)) and g(f(10)).
I was interested in having a polynomial returned explicitly, not just making a function that would have the same effect as composition. You could say I was hoping for a closed form. To implement that, I guess you'd want an exponentiation operator for polynomials (implement __pow__).
Using operator overriding, we can actually use + - and * for addition, subtraction and multiplication (I haven't done divide):
Definition and Representation:
f = Poly([1,2,3]) f x**2 + 2*x + 3 g = Poly([2,3,4,5]) g 2*x**3 + 3*x**2 + 4*x + 5
Negation:
-f -x**2 - 2*x - 3
Subtraction:
f-g -2*x**3 - 2*x**2 - 2*x - 2
Evaluation:
g(10) 2345 f(10) 123
Composition:
f(g(10)) 5503718 g(f(10)) 3767618
Multiplication:
f*g 2*x**5 + 7*x**4 + 16*x**3 + 22*x**2 + 22*x + 15 g*f 2*x**5 + 7*x**4 + 16*x**3 + 22*x**2 + 22*x + 15
Evaluation and Multiplication:
x = -1 eval(str(g*f)) 4 h=g*f h(-1) 4
Negation and Multiplication:
-f*g -2*x**5 - 7*x**4 - 16*x**3 - 22*x**2 - 22*x - 15
Derivative:
h 2*x**5 + 7*x**4 + 16*x**3 + 22*x**2 + 22*x + 15 h.deriv() 10*x**4 + 28*x**3 + 48*x**2 + 44*x + 22
I'm pretty sure all of the above is correct.
I've put the code I have so far at:
http://www.inetarena.com/~pdx4d/ocn/polynomial.py (Python source) http://www.inetarena.com/~pdx4d/ocn/polynomial.html (colorized HTML)
Very nice. I need to upgrade to get -=, +=, and list comprehensions in my interepreter. (It was easy to use map instead of the list comprehension, but the list comprehension is easier to read.) I quickly noticed that you can't multiply a Poly by an integer, or add or subtract an integer. I think one approach would be to throw an if type(other)==type(3): other = Poly([other]) at the start of __add__ and __mul__. That worked for me, so that I could multiply a Poly by an integer or add or subtract an integer. You also might want to define __rmul__, __rsub__, and __radd__ in terms of __mul__, __sub__, and __add__ (polynomial multiplication and addition being commutative). -- Seth David Schoen <schoen@loyalty.org> | And do not say, I will study when I Temp. http://www.loyalty.org/~schoen/ | have leisure; for perhaps you will down: http://www.loyalty.org/ (CAF) | not have leisure. -- Pirke Avot 2:5
Seth David Schoen writes:
Kirby Urner writes:
Could this be extended to a symbolic math implementation? At least, could you implement compose, add, subtract, multiply, and divide methods so that
f.compose(g) f.add(g)
would work?
============
Sure.
Compose was already implied in Brent's class, since f(10) returns a number, and therefore so does g(f(10)) and g(f(10)).
I was interested in having a polynomial returned explicitly, not just making a function that would have the same effect as composition. You could say I was hoping for a closed form.
To implement that, I guess you'd want an exponentiation operator for polynomials (implement __pow__).
[...]
I quickly noticed that you can't multiply a Poly by an integer, or add or subtract an integer. I think one approach would be to throw an
if type(other)==type(3): other = Poly([other])
at the start of __add__ and __mul__. That worked for me, so that I could multiply a Poly by an integer or add or subtract an integer.
You also might want to define __rmul__, __rsub__, and __radd__ in terms of __mul__, __sub__, and __add__ (polynomial multiplication and addition being commutative).
OK, I implemented __pow__, compose, __rmul__, __rsub__, and __radd__. The implementation of __pow__ does not take advantage of the binomial theorem or its higher-order generalizations, mostly because I don't know the higher-order generalizations. I also did the type check thing so that integer constants are automatically converted to polynomials where appropriate. That means it would be possible to implement __neg__ as "return self * -1". (This version gets rid of list comprehensions, +=, and -=, so it will run under 1.5.2.) http://www.loyalty.org/~schoen/polynomial.py
q = Poly([1,1]) q x + 1 q ** 2 x**2 + 2*x + 1 q ** 3 x**3 + 3*x**2 + 3*x + 1 q - 1 x 1 - q -x q * 2 2*x + 2 2 * q 2*x + 2
It seems that it would be helpful to store the coefficients in reverse order. Then you could do for exponent in range(len(self.coeffs)): coeff = self.coeffs[exponent] # Some code that uses exponent and coeff goes here rather than counting down. -- Seth David Schoen <schoen@loyalty.org> | And do not say, I will study when I Temp. http://www.loyalty.org/~schoen/ | have leisure; for perhaps you will down: http://www.loyalty.org/ (CAF) | not have leisure. -- Pirke Avot 2:5
OK, I implemented __pow__, compose, __rmul__, __rsub__, and __radd__. The implementation of __pow__ does not take advantage of the binomial theorem or its higher-order generalizations, mostly because I don't know the higher-order generalizations.
Great. I'd done a __pow__ earlier, but I like yours better. I also make sure the exponent is non-negative, as we're not promising to handle that kind of algebra here.
I also did the type check thing so that integer constants are automatically converted to polynomials where appropriate. That means it would be possible to implement __neg__ as "return self * -1".
This is good -- I've incorporated it.
(This version gets rid of list comprehensions, +=, and -=, so it will run under 1.5.2.)
My most recent at: http://www.inetarena.com/~pdx4d/ocn/python/polynomial.html or same w/ .py extension for cut and paste source. Note that for commutative ops like __add__ and __mul__, you can just go __radd__ = __add__ and __rmul__ = __mul__ to create equivalent definitions. Only subtraction needs to be handled differently, because arg order matters. Also, once your enhancements are added, you really don't need a separate compose method (which is very cool). Just pass a polynomial as your "value of x" in the already-defined call method:
from polynomial import * f = Poly([1,-3,2]) g = Poly([1,3,0]) f x**2 - 3*x + 2 g x**2 + 3*x f(g) # <--- symbolic composition x**4 + 6*x**3 + 6*x**2 - 9*x + 2 g(f) # <--- symbolic composition x**4 - 6*x**3 + 16*x**2 - 21*x + 10 f(g(10)) # <--- or you can do numeric evaluation 16512 g(f(10)) 5400
I've put your name in lights in my program's marquee. Kirby
The Poly class in polynomial.py has been streamlined and enchanced a bit, if anyone following this thread is curious: http://www.inetarena.com/~pdx4d/ocn/python/polynomial.html or .py for cut and pastable source Thanks to Brian Harvey on k12.ed.math for sharing his good ideas ( http://www.mathforum.com/epigone/k12.ed.math/blunpharthah ) Kirby
participants (3)
-
Brent Burley
-
Kirby Urner
-
Seth David Schoen