Well, you picked a particularly tricky example because it uses both
the main core and the polys.

So let's start with my_expresion (the core part).  You already know
that x is defined to be a Symbol object (because you did this
yourself).  You should also know that sin and cos are Function
objects, which you can think of as container objects that hold
arguments and which also have various mathematical relations defined
on them.

As you may know, Python let's you override the behavior of the
built-in operations *, +, /, -, etc. on your own objects.  So all
objects have method __mul__, __add__, etc. defined on them.  When you
call x + y, this reduces to x.__add__(y).  In SymPy, a.__add__(b) is
converted to Add(a, b).  The same is true for __mul__ and Mul.  So in
sin(x)**2 + 2*sin(x) + 1, sin(x) creates a sin object (when it is
called).  This reduces to

sin(x).__pow__(2) + sin(x).__rmul__(2) + 1
Pow(x, 2).__add__(Mul(2, sin(x))).__add__(1)
Add(Pow(x, 2), Mul(2, sin(x)), 1)

which is what you will get if you call srepr(my_expression).  Note
that 2*sin(x) actually calls __rmul__.  That's because 2 (type int)
doesn't know how to multiply sin(x) (type sin), so sin(x)'s __rmul__
method is called.  This brings up an important point.  All Python
types are converted in this process to SymPy types, through the
sympify() function.  So, for example, sin(x).__pow__(2) reduces to
sin(x).__pow__(sympify(2)), which results in
sin(x).__pow__(Integer(2)).

Now, Mul, Pow, and Add's __new__ methods contain logic to do automatic
simplifications like x + 2*x => 3*x or x*x => x**2.  In this case, no
such simplifications needed to be applied.

All the args for any SymPy expression are stored in expr.args.  So you
would have

>>> my_expression.args
(1, sin(x)**2, 2*sin(x))
>>> my_expression.args[1].args
(sin(x), 2)

If you want to see the code for all of this, you can look at the files
in sympy/core.  The __op__ stuff is mostly in basic.py and expr.py.
The flattening routines are in add.py, pow.py, and mul.py.  The code
for functions that sin is built off of is in function.py.

Now, to the factoring part.  my_expression.factor() is a shortcut to
factor(my_expression).  Because factoring is a polynomial algorithm,
the expression has to be converted to a Poly first.  Poly is able to
represent polynomials with arbitrary symbolic "generators".  In this
case, it determines that it should use sin(x) as a generator, so it
creates Poly(sin(x)**2 + 2*sin(x) + 1, sin(x)), which you can think of
as a wrapper around the polynomial y**2 + 2*y + 1, where y is set to
be sin(x) (for the purposes of Poly, it does not matter what the
generators are, other than that coefficients cannot contain symbols
from them, so you can think of it in this way).

Then, it calls the factorization algorithm on Poly.  If you are
interested in how this works, I suggest you read the code and the
papers referenced there.  In this case, it is able to factor the
polynomial using the squarefree factorization algorithm, which is
actually not too difficult to understand.  In the general case, it
uses a complicated multivariate factorization algorithm that factors
any multivariate polynomial into irreducibles.

Anyway, Poly.factorlist returns something like [(Poly(sin(x) + 1),
2)].  factor() converts this into a normal SymPy expression (also
called a Basic expression or Expr expression) by passing it to Mul and
Pow (something like Mul(*[Pow(b, e) for b, e in expr]) would do it, I
think).

All the Poly stuff lives in sympy/polys.  If you are interested, I can
explain a little how they work (internal representation, etc.).  The
code for the Poly class lives in polytools.py, though the actual
factorization algorithm lives in sqftools.py and factortools.py.

And one thing that I didn't mention (and maybe you didn't even think
of) is the printing.  You do not use pretty printing, so the printing
is rather simple (just recursively print the objects using the proper
operators).  If you are interested, you can look at the code in
sympy/printing.

Let me know if this made sense, and if there are bits that you still
would like to know about.  Also, remember that SymPy is written in a
fairly modular way, so it's completely unnecessary to know how a
module works unless you want to work on that module specifically
(e.g., you don't need to how the core works to write some
simplification algorithm, like in simplify.py).

Aaron Meurer

On Thu, Jun 23, 2011 at 10:42 PM, Jeff Pickhardt <[email protected]> wrote:
> Hey guys,
>
> I'm reading through the SymPy code and, well, it's somewhat overwhelming if
> you're new to the project because there's so much going on.  (That's a good
> thing too - it means its robust!)
>
> Can someone help explain how this works?
>
>>>> from sympy import *
>>>> x = Symbol("x")
>>>> my_expression = sin(x)**2 + 2*sin(x) + 1
>>>> my_expression.factor()
> (1 + sin(x))**2
>>>>
>
> For instance, what data structures happen when I create my_expression, what
> happens when I factor it, etc.  A high-level walk through would help.  I see
> there's stuff going on at polytools.py, and I think _symbolic_factor gets
> called.  It's just confusing to keep everything in my head when I don't yet
> have a high level understanding of how sympy expressions and what not
> actually work.
>
> Also, the new quantum mechanics stuff looks really cool.  Wish I had had
> that a few years ago!
>
> Thanks,
> Jeff
>
> --
> Jeff Pickhardt
> (650) 530-0036
> [email protected]
>
> --
> You received this message because you are subscribed to the Google Groups
> "sympy" 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/sympy?hl=en.
>

-- 
You received this message because you are subscribed to the Google Groups 
"sympy" 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/sympy?hl=en.

Reply via email to