===============
Troubleshooting
===============


Common problems
===============


Symbol aliases
--------------

SymPy treats symbols with different assumptions as different symbols
even if they have the same name.  To reduce this confusion, Lcapy
assumes that symbol names are not aliased.  It achieves this by
maintaining a list of defined symbols for each circuit.  However, it
is unaware of symbols created by SymPy.


Symbol assumptions
------------------

There can be difficulties with symbol assumptions when working with
SymPy.  By default SymPy creates symbols with few assumptions, for example,

   >>> from sympy import Symbol
   >>> R1 = Symbol('R')
   >>> R1.assumptions0
   {'commutative': True}

On the other hand, by default, Lcapy assumes that symbols are
positive (unless explicitly defined otherwise).  For example,

   >>> from lcapy import symbol
   >>> R2 = symbol('R')
   >>> R2.assumptions0
   {'commutative': True,
   'complex': True,
   'hermitian': True,
   'imaginary': False,
   'negative': False,
   'nonnegative': True,
   'nonpositive': False,
   'nonzero': True,
   'positive': True,
   'real': True,
   'zero': False}


Since `R1` and `R2` have different assumptions, SymPy considers them different symbols even though they are both defined with the same name `R`.

Note, every real symbol is also considered complex although with no
imaginary part.  The proper way to test assumptions is to use the
attributes `is_complex`, `is_real`, etc.  For example,

   >>> t.is_real
   True
   >>> t.is_complex
   False


Zero substitution
-----------------

Be careful with zero substitutions.  For example, consider
    >>> x = symbol('x')
    >>> (x * (s + 1 / x)).subs(x, 0)
    0

In general it is safer (but slower) to evaluate a limit at zero.  

    >>> x = symbol('x')
    >>> (x * (s + 1 / x)).limit(x, 0)
    1
    
Another approach is expand the expression to avoid the division:

    >>> x = symbol('x')
    >>> (x * (s + 1 / x)).expand().subs(x, 0)
    1


Computation speed
-----------------

Lcapy can be slow for large problems due to the computational
complexity of the algorithms.  If speed is important, it is better to
substitute symbolic values with numerical values.

The results from slow computations are cached to improve the speed.

Some SymPy operations can take an unexpectedly long time, for example, `limit()`.



Debugging
=========


schtex
------

If `schtex` crashes, rerun it with the `-pdb` option.  This will enter the Python debugger when an unhandled exception is raised.


pdb method
----------

The Python debugger (pdb) can be entered using the `pdb()` method for many Lcapy classes.   For example, the inverse Laplace transform can be debugged for the expression `1 / (s + 2)` using:

   >>> (1 / (s + 2)).pdb().ILT()


debug method
------------

Expressions have a `debug()` method that prints the representation of the expresison, including symbol assumptions.  For example,

   >>> (1 / (s + 'a')).debug()                                                 
   sExpr(Pow(Add(s: {'nonpositive': False, 'nonzero': False, 'composite': False, 'real': False, 'negative': False, 'even': False, 'odd': False, 'prime': False, 'positive': False, 'nonnegative': False, 'integer': False, 'commutative': True, 'rational': False, 'zero': False, 'irrational': False},
              a: {'nonpositive': False, 'extended_nonpositive': False, 'hermitian': True, 'extended_positive': True, 'real': True, 'imaginary': False, 'negative': False, 'extended_real': True, 'infinite': False, 'extended_negative': False, 'extended_nonnegative': True, 'positive': True, 'nonnegative': True, 'extended_nonzero': True, 'finite': True, 'commutative': True, 'zero': False, 'complex': True, 'nonzero': True})
,
          -1)



   
