Compare commits

..

No commits in common. "27e7dcba200b766d6001eadc8f3230898fc41eb9" and "fdf3b088f2ca807beae036cd2e97bf297f990bfa" have entirely different histories.

30 changed files with 222 additions and 1046 deletions

2
.gitignore vendored
View File

@ -4,7 +4,5 @@ dist/
build/ build/
*.egg-info/ *.egg-info/
documentation/build/ documentation/build/
documentation/source/_build/
cache/ cache/
venv/ venv/
.nox

View File

@ -1,9 +1,9 @@
#!/usr/bin/env python #!/usr/bin/env python
# encoding: utf-8 # encoding: utf-8
from .calculus import Expression, Integer, Decimal from .calculus import Expression#, Polynom, Fraction, random_str, txt, Equation
# Expression.set_render('tex') #Expression.set_render('tex')
from .stat import Dataset, WeightedDataset from .stat import Dataset, WeightedDataset
from .geometry import random_pythagore from .geometry import random_pythagore

View File

@ -84,20 +84,19 @@ x^7
>>> e = Expression.from_str("1+2x^2+3x+4+5x") >>> e = Expression.from_str("1+2x^2+3x+4+5x")
>>> e_simplified = e.simplify() >>> e_simplified = e.simplify()
>>> e_simplified >>> e_simplified
<Quadratic 2x^2 + 8x + 5> <Quadratic 5 + 2x^2 + 8x>
>>> for s in e_simplified.explain(): >>> for s in e_simplified.explain():
... print(s) ... print(s)
1 + 2x^2 + 3x + 4 + 5x 1 + 2x^2 + 3x + 4 + 5x
2x^2 + 3x + 1 + 4 + 5x 1 + 2x^2 + 3x + 4 + 5x
2x^2 + 3x + 5x + 1 + 4 1 + 4 + 2x^2 + 3x + 5x
2x^2 + (3 + 5) * x + 5 5 + 2x^2 + (3 + 5) * x
2x^2 + 8x + 5 5 + 2x^2 + 8x
>>> e = Expression.from_str("(2x+3)^2") >>> e = Expression.from_str("(2x+3)^2")
>>> e_simplified = e.simplify() >>> e_simplified = e.simplify()
>>> e_simplified >>> e_simplified
<Quadratic 4x^2 + 12x + 9> <Quadratic 12x + 4x^2 + 9>
>>> for s in e_simplified.explain(): >>> for s in e_simplified.explain():
... print(s) ... print(s)
(2x + 3)^2 (2x + 3)^2
@ -106,13 +105,11 @@ x^7
2 * 2 * x^(1 + 1) + 3 * 2 * x + 3 * 2 * x + 9 2 * 2 * x^(1 + 1) + 3 * 2 * x + 3 * 2 * x + 9
6x + 6x + 4x^2 + 9 6x + 6x + 4x^2 + 9
(6 + 6) * x + 4x^2 + 9 (6 + 6) * x + 4x^2 + 9
4x^2 + 12x + 9 12x + 4x^2 + 9
""" """
from .expression import Expression from .expression import Expression
from .tokens.number import Integer, Decimal
if __name__ == "__main__": if __name__ == "__main__":
e = Expression.from_str("1+2/3/4/5") e = Expression.from_str("1+2/3/4/5")

View File

@ -10,17 +10,10 @@
Expression Expression
""" """
from functools import partial
from ..core import AssocialTree, Tree, compute, typing, TypingError from ..core import AssocialTree, Tree, compute, typing, TypingError
from ..core.random import ( from ..core.random import extract_rdleaf, extract_rv, random_generator, compute_leafs, replace_rdleaf
extract_rdleaf,
extract_rv,
random_generator,
compute_leafs,
replace_rdleaf,
)
from ..core.MO import moify from ..core.MO import moify
from .tokens import factory from .tokens import factory, Token
from .renders import renders from .renders import renders
@ -74,14 +67,12 @@ class Expression(object):
2 + 3 \\times 4 2 + 3 \\times 4
>>> e = Expression.from_str("2+3/4") >>> e = Expression.from_str("2+3/4")
>>> print(e) >>> print(e)
2 + \\dfrac{3}{4} 2 + \\frac{3}{4}
>>> es = e.simplify() >>> es = e.simplify()
>>> print(es) >>> print(es)
\\dfrac{11}{4} \\frac{11}{4}
>>> Expression.set_render('txt') >>> Expression.set_render('txt')
""" """
from .tokens.token import Token
Token.set_render(render) Token.set_render(render)
cls.RENDER = render cls.RENDER = render
@ -104,14 +95,15 @@ class Expression(object):
<Linear 2x + 1> <Linear 2x + 1>
>>> e = Expression.from_str("2x + 1 + 5x^2") >>> e = Expression.from_str("2x + 1 + 5x^2")
>>> e >>> e
<Quadratic 5x^2 + 2x + 1> <Quadratic 2x + 1 + 5x^2>
>>> e = Expression.from_str("2x + 1 + 5x") >>> e = Expression.from_str("2x + 1 + 5x")
>>> e >>> e
<Exp: 2x + 1 + 5x> <Exp: 2x + 1 + 5x>
""" """
t = Tree.from_str(string) t = Tree.from_str(string)
if typing: if typing:
return cls._post_processing(t) return cls._post_precessing(t)
return cls(t) return cls(t)
@ -160,10 +152,10 @@ class Expression(object):
if shuffle: if shuffle:
raise NotImplemented("Can't suffle expression yet") raise NotImplemented("Can't suffle expression yet")
return cls._post_processing(t) return cls._post_precessing(t)
@classmethod @classmethod
def _post_processing(cls, t): def _post_precessing(cls, t):
""" Post process the tree by typing it """ """ Post process the tree by typing it """
tt = cls(t)._typing() tt = cls(t)._typing()
try: try:
@ -445,65 +437,6 @@ class Expression(object):
else: else:
yield self yield self
def __call__(self, value):
""" Call a Expression to evaluate itself on value
:param value: evaluate the Expression with this value
:return: Expression simplified if the value is not a string with a length greater than 1.
:examples:
>>> f = Expression.from_str("3*x^2 + 2x + 1")
>>> for s in f(2).explain():
... print(s)
3 * 2^2 + 2 * 2 + 1
3 * 4 + 4 + 1
12 + 5
17
>>> f(f(2))
<Integer 902>
>>> f(17)
<Integer 902>
>>> f("n")
<Quadratic 3n^2 + 2n + 1>
>>> f("u_n")
<Exp: 3u_n^2 + 2u_n + 1>
>>> f(f)
<Polynomial 27x^4 + 36x^3 + 36x^2 + 16x + 6>
"""
tree = self._tree
variable = (set(tree.get_leafs(extract_variable)) - {None}).pop()
try:
dest = value._mo
except AttributeError:
dest = moify(value)
replace_var = partial(replace, origin=variable, dest=dest)
tree = tree.map_on_leaf(replace_var)
if isinstance(value, str) and len(value) > 1:
return Expression(tree)
return Expression(tree).simplify()
def extract_variable(leaf):
try:
return leaf.variable
except AttributeError:
return None
def replace(leaf, origin, dest):
""" Recursively replace origin to dest in leaf """
try:
leaf.tree
except AttributeError:
if leaf == origin:
return dest
return leaf
replace_var = partial(replace, origin=origin, dest=dest)
return leaf.tree.map_on_leaf(replace_var)
# ----------------------------- # -----------------------------
# Reglages pour 'vim' # Reglages pour 'vim'

View File

@ -10,163 +10,22 @@
Tokens represents MathObject at API level Tokens represents MathObject at API level
""" """
from ...core.MO import MO, MOnumber, MOstr, moify from ...core.MO import MO, MOnumber, MOstr
from ...core.MO.fraction import MOFraction from ...core.MO.fraction import MOFraction
from ...core.MO.monomial import MOstrPower, MOMonomial from ...core.MO.monomial import MOstrPower, MOMonomial
from ...core.MO.polynomial import MOpolynomial from ...core.MO.polynomial import MOpolynomial
from decimal import Decimal as _Decimal from decimal import Decimal as _Decimal
from functools import wraps
from .number import Integer, Decimal, Fraction
from .polynomial import Polynomial, Linear, Quadratic
from .token import Token from .token import Token
__all__ = ["factory"] __all__ = ["factory"]
def tokenify(mo, name="", ancestor=None):
""" Transform a MO or a python builtin to the appropriate token
:param mo: the thing to turn into a Token
:param name: a virtual name of the toke
:param ancestor: its ancestor
:example:
>>> a = MOnumber(2)
>>> tokenify(a)
<Integer 2>
>>> tokenify(2)
<Integer 2>
>>> tokenify("x")
<Linear x>
>>> tokenify(_Decimal("2.2"))
<Decimal 2.2>
>>> tokenify("2.2")
<Decimal 2.2>
>>> tokenify(2.2)
<Decimal 2.20000000000000017763568394002504646778106689453125>
tokenify is idempotent on "mo" parameter
>>> a = MOnumber(2)
>>> ta = tokenify(a)
>>> ta == tokenify(ta)
True
"""
if isinstance(mo, MO):
return _tokenify(mo, name, ancestor)
if isinstance(mo, Token):
if name == "":
_name = mo.name
else:
_name = name
if ancestor is None:
_ancestor = mo._ancestor
else:
_ancestor = ancestor
return _tokenify(mo._mo, _name, _ancestor)
return _tokenify(moify(mo), name, ancestor)
def to_be_token(func):
""" Decorator to ensure that the return value is a Token """
@wraps(func)
def wrapped(*args, **kwds):
ans = func(*args, **kwds)
try:
return [tokenify(t) for t in ans]
except TypeError:
return tokenify(ans)
return wrapped
def _tokenify(mo, name="", ancestor=None):
""" Transform a MO (from core) to the appropriate token (from API)
:example:
>>> a = MOnumber(2)
>>> _tokenify(a)
<Integer 2>
>>> a = MOnumber(2.5)
>>> _tokenify(a)
<Decimal 2.5>
>>> a = MOFraction(2, 5)
>>> _tokenify(a)
<Fraction 2 / 5>
>>> a = MOstr('x')
>>> _tokenify(a)
<Linear x>
>>> a = MOstrPower('x', 2)
>>> _tokenify(a)
<Quadratic x^2>
>>> a = MOstrPower('x', 3)
>>> _tokenify(a)
<Polynomial x^3>
>>> a = MOMonomial(3, 'x', 1)
>>> _tokenify(a)
<Linear 3x>
>>> a = MOMonomial(3, 'x', 2)
>>> _tokenify(a)
<Quadratic 3x^2>
>>> a = MOMonomial(3, 'x', 3)
>>> _tokenify(a)
<Polynomial 3x^3>
"""
if isinstance(mo, MOnumber):
if isinstance(mo.value, int):
from .number import Integer
return Integer.from_mo(mo, name, ancestor)
elif isinstance(mo.value, _Decimal):
from .number import Decimal
return Decimal.from_mo(mo, name, ancestor)
raise TypeError(f"Can't build from MOnumber ({mo}) neither int nor decimal")
if isinstance(mo, MOFraction):
if isinstance(mo._denominator, MOnumber) and isinstance(
mo._numerator, MOnumber
):
from .number import Fraction
return Fraction.from_mo(mo, name, ancestor)
raise TypeError(
f"Can't build from MOFraction ({mo}) numerator and denominator are not MOnumber"
)
if isinstance(mo, (MOstr, MOstrPower, MOMonomial, MOpolynomial)):
if not isinstance(mo._variable, (MOstr, str)):
raise TypeError(
f"Can't build Polynom over something else than a letter (got {mo._variable})"
)
if (
isinstance(mo, MOstr)
or (isinstance(mo, MOMonomial) and mo.power.value == 1)
or (isinstance(mo, MOpolynomial) and mo.power.value == 1)
):
from .polynomial import Linear
return Linear.from_mo(mo, name, ancestor)
elif (
(isinstance(mo, MOstrPower) and mo.power.value == 2)
or (isinstance(mo, MOMonomial) and mo.power.value == 2)
or (isinstance(mo, MOpolynomial) and mo.power.value == 2)
):
from .polynomial import Quadratic
return Quadratic.from_mo(mo, name, ancestor)
else:
from .polynomial import Polynomial
return Polynomial.from_mo(mo, name, ancestor)
raise TypeError(f"{type(mo)} is unknown MathObject")
def factory(exp, name="", ancestor=None): def factory(exp, name="", ancestor=None):
""" Transform a Expression with on single MathObject (from core) to a appropriate token (from API) """ Transform a Expression with on MathObject (from core) to a appropriate token (from API)
:example: :example:
>>> from ..expression import Expression >>> from ..expression import Expression
@ -202,7 +61,46 @@ def factory(exp, name="", ancestor=None):
if not isinstance(mo, MO): if not isinstance(mo, MO):
raise TypeError(f"Can't build Token from not computed Expression (got {mo})") raise TypeError(f"Can't build Token from not computed Expression (got {mo})")
return _tokenify(mo, name, ancestor) if isinstance(mo, MOnumber):
if isinstance(mo.value, int):
return Integer.from_mo(mo, name, ancestor)
elif isinstance(mo.value, _Decimal):
return Decimal.from_mo(mo, name, ancestor)
raise TypeError(f"Can't build from MOnumber ({mo}) neither int nor decimal")
elif isinstance(mo, MOFraction):
if isinstance(mo._denominator, MOnumber) and isinstance(
mo._numerator, MOnumber
):
return Fraction.from_mo(mo, name, ancestor)
raise TypeError(
f"Can't build from MOFraction ({mo}) numerator and denominator are not MOnumber"
)
elif isinstance(mo, (MOstr, MOstrPower, MOMonomial, MOpolynomial)):
if not isinstance(mo._variable, (MOstr, str)):
raise TypeError(
f"Can't build Polynom over something else than a letter (got {mo._variable})"
)
if (
isinstance(mo, MOstr)
or (isinstance(mo, MOMonomial) and mo.power.value == 1)
or (isinstance(mo, MOpolynomial) and mo.power.value == 1)
):
return Linear.from_mo(mo, name, ancestor)
elif (
(isinstance(mo, MOstrPower) and mo.power.value == 2)
or (isinstance(mo, MOMonomial) and mo.power.value == 2)
or (isinstance(mo, MOpolynomial) and mo.power.value == 2)
):
return Quadratic.from_mo(mo, name, ancestor)
else:
return Polynomial.from_mo(mo, name, ancestor)
else:
raise TypeError(f"{type(mo)} is unknown MathObject")
# ----------------------------- # -----------------------------

View File

@ -16,7 +16,6 @@ from ...core.arithmetic import gcd
from ...core.random.int_gene import filter_random from ...core.random.int_gene import filter_random
from ...core.MO import MO, MOnumber from ...core.MO import MO, MOnumber
from ...core.MO.fraction import MOFraction from ...core.MO.fraction import MOFraction
from random import random
__all__ = ["Integer", "Decimal"] __all__ = ["Integer", "Decimal"]
@ -243,20 +242,6 @@ class Fraction(Token):
def denominator(self): def denominator(self):
return self._mo.denominator return self._mo.denominator
@property
def decimal(self):
""" return decimal approximation of the fraction
:example:
>>> f = Fraction("3/4")
>>> f.decimal
<Decimal 0.75>
>>> f = Fraction("1/3")
>>> f.decimal
<Decimal 0.3333333333333333333333333333>
"""
return Decimal(_Decimal(self._mo.numerator._value) / _Decimal(self._mo.denominator._value))
# ----------------------------- # -----------------------------
# Reglages pour 'vim' # Reglages pour 'vim'

View File

@ -10,32 +10,26 @@
Tokens representing polynomials functions Tokens representing polynomials functions
""" """
from ..expression import Expression
from .token import Token from .token import Token
from . import to_be_token
from ...core.MO import MO from ...core.MO import MO
from ...core.MO.atoms import moify
__all__ = ["Polynomial", "Quadratic", "Linear"] __all__ = ["Polynomial", "Quadratic", "Linear"]
class Polynomial(Token): class Polynomial(Token):
""" Token representing a polynomial """ Token representing a polynomial """
:examples:
>>> from ...core.MO.polynomial import MOpolynomial
>>> P = Polynomial(MOpolynomial('x', [1, 2, 3]))
>>> P
<Polynomial 3x^2 + 2x + 1>
"""
def __init__(self, a, name="", ancestor=None): def __init__(self, a, name="", ancestor=None):
""" Initiate Polynomial with a MO"""
if not isinstance(a, MO): if not isinstance(a, MO):
raise TypeError if isinstance(a, str):
raise TypeError
else:
raise TypeError
else:
mo = a
Token.__init__(self, a, name, ancestor) Token.__init__(self, mo, name, ancestor)
self._mathtype = "polynome" self._mathtype = "polynome"
@classmethod @classmethod
@ -43,257 +37,49 @@ class Polynomial(Token):
return cls(mo, name, ancestor) return cls(mo, name, ancestor)
@classmethod
def from_coefficients(cls, coefficients):
""" Initiate polynomial from list of coefficients """
pass
@classmethod @classmethod
def random(cls): def random(cls):
raise NotImplementedError raise NotImplemented
@property
def raw(self):
raise NotImplementedError("Polynomial does not exists in python")
def __setitem__(self, key, item): def __setitem__(self, key, item):
""" Use Polynomial like if they were a dictionnary to set coefficients """ """ Use Polynomial like if they were a dictionnary to set coefficients """
raise NotImplementedError("Can't set coefficient of a polynomial") pass
@to_be_token
def __getitem__(self, key): def __getitem__(self, key):
""" Use Polynomial like if they were a dictionnary to get coefficients """ Use Polynomial like if they were a dictionnary to get coefficients """
pass
:examples:
>>> from ...core.MO.polynomial import MOpolynomial
>>> P = Polynomial(MOpolynomial('x', [1, 2, 3]))
>>> P[0]
<Integer 1>
>>> P[1]
<Integer 2>
>>> P[2]
<Integer 3>
>>> P[3]
Traceback (most recent call last):
...
KeyError: 3
"""
return self._mo.coefficients[key]
def __call__(self, value): def __call__(self, value):
""" Call a Polynomial to evaluate itself on value """ Call a Polynomial to evaluate itself on value """
pass
:examples:
>>> from ...core.MO.polynomial import MOpolynomial
>>> P = Polynomial(MOpolynomial('x', [1, 2, 3]))
>>> for s in P(2).explain():
... print(s)
3 * 2^2 + 2 * 2 + 1
3 * 4 + 4 + 1
12 + 5
17
"""
return Expression(self._mo.tree)(value)
def differentiate(self):
""" Differentiate a polynome
:example:
>>> from ...core.MO.polynomial import MOpolynomial
>>> P = Polynomial(MOpolynomial('x', [1, 2, 3]))
>>> P
<Polynomial 3x^2 + 2x + 1>
>>> P.differentiate()
<Linear 6x + 2>
>>> for s in P.differentiate().explain():
... print(s)
0 + 2 + 3 * 2x
2 + 3 * 2 * x
6x + 2
"""
return Expression(self._mo.differentiate()).simplify()
@property
def roots(self):
""" Get roots of the Polynomial """
raise NotImplementedError("Can't compute roots not specific polynomial")
class Linear(Polynomial): class Linear(Polynomial):
""" Token representing a linear ax + b """ Token representing a linear """
:examples:
>>> from ...core.MO.polynomial import MOpolynomial, MOMonomial
>>> P = Linear(MOpolynomial('x', [1, 2]))
>>> P
<Linear 2x + 1>
>>> P.a
<Integer 2>
>>> P.b
<Integer 1>
>>> P.differentiate()
<Integer 2>
>>> P.roots
[<Fraction - 2 / 1>]
"""
def __init__(self, mo, name="", ancestor=None): def __init__(self, mo, name="", ancestor=None):
""" Initiate Linear with MO
:examples:
>>> from ...core.MO.polynomial import MOpolynomial, MOMonomial
>>> P = Linear(MOpolynomial('x', [1, 2]))
>>> P
<Linear 2x + 1>
>>> Q = Linear(MOMonomial(3, 'x', 1))
>>> Q
<Linear 3x>
"""
Polynomial.__init__(self, mo, name, ancestor) Polynomial.__init__(self, mo, name, ancestor)
self._mathtype = "affine" self._mathtype = "affine"
@classmethod @classmethod
def random(cls): def random(cls):
raise NotImplementedError raise NotImplemented
@property
@to_be_token
def a(self):
return self[1]
@property
@to_be_token
def b(self):
return self[0]
@property
@to_be_token
def roots(self):
""" Get the root of the polynomial
:examples:
>>> from ...core.MO.polynomial import MOpolynomial, MOMonomial
>>> P = Linear(MOpolynomial('x', [1, 2]))
>>> P.roots
[<Fraction - 2 / 1>]
>>> #P = Linear(MOpolynomial('x', [1, -2]))
>>> #P.roots
"""
try:
return [Expression.from_str(f"-{self.a}/{self.b}").simplify()]
except AttributeError:
return [Expression.from_str(f"-{self.a}/{self.b}")]
class Quadratic(Polynomial): class Quadratic(Polynomial):
""" Token representing a quadratic ax^2 + bx + c """ Token representing a quadratic """
:examples:
>>> from ...core.MO.polynomial import MOpolynomial
>>> P = Quadratic(MOpolynomial('x', [1, 2, 3]))
>>> P
<Quadratic 3x^2 + 2x + 1>
>>> P.a
<Integer 3>
>>> P.b
<Integer 2>
>>> P.c
<Integer 1>
>>> P.delta
<Integer - 8>
>>> for s in P.delta.explain():
... print(s)
2^2 - 4 * 3 * 1
4 - 12 * 1
4 - 12
- 8
>>> P.differentiate()
<Linear 6x + 2>
>>> P.roots
[]
"""
def __init__(self, mo, name="", ancestor=None): def __init__(self, mo, name="", ancestor=None):
""" Initiate Quadratic from MO
>>> from ...core.MO.polynomial import MOpolynomial
>>> P = Quadratic(MOpolynomial('x', [1, 2, 3]))
>>> P
<Quadratic 3x^2 + 2x + 1>
"""
Polynomial.__init__(self, mo, name, ancestor) Polynomial.__init__(self, mo, name, ancestor)
self._mathtype = "polynome du 2nd degré" self._mathtype = "polynome du 2nd degré"
@classmethod @classmethod
def random(cls): def random(cls):
raise NotImplementedError raise NotImplemented
@property
@to_be_token
def a(self):
try:
return self[2]
except KeyError:
return 0
@property
@to_be_token
def b(self):
try:
return self[1]
except KeyError:
return 0
@property
@to_be_token
def c(self):
try:
return self[0]
except KeyError:
return 0
@property
@to_be_token
def delta(self):
return Expression.from_str(f"{self.b}^2-4*{self.a}*{self.c}").simplify()
@property
@to_be_token
def roots(self):
""" Roots of the polynom
:example:
>>> from ...core.MO.polynomial import MOpolynomial
>>> P = Quadratic(MOpolynomial('x', [1, 0, 1]))
>>> P.roots
[]
>>> P = Quadratic(MOpolynomial('x', [4, -4, 1]))
>>> P.roots
[<Integer 2>]
>>> P = Quadratic(MOpolynomial('x', [1, 0, -1]))
>>> P.roots
[<Integer - 1>, <Integer 1>]
"""
if self.delta._mo < 0:
return []
elif self.delta._mo == 0:
# return [Expression.from_str(f"-{self.b}/(2*{self.a})").simplify()]
return [round(eval(f"-{self.b}/(2*{self.a})"), 2)]
else:
from math import sqrt
roots = [
str(eval(f"(-{self.b}-sqrt({self.delta}))/(2*{self.a})")),
str(eval(f"(-{self.b}+sqrt({self.delta}))/(2*{self.a})")),
]
roots.sort()
return roots
# ----------------------------- # -----------------------------

View File

@ -11,7 +11,6 @@ Tokens: practical envelop of math object
""" """
from ..renders import renders from ..renders import renders
from ...core.MO.atoms import moify
class Token(object): class Token(object):
@ -27,11 +26,7 @@ class Token(object):
self._ancestor = ancestor self._ancestor = ancestor
@classmethod @classmethod
def random( def random(cls):
cls,
family="integer",
**kwds
):
raise NotImplemented raise NotImplemented
@classmethod @classmethod
@ -74,7 +69,7 @@ class Token(object):
else: else:
raise ValueError(f"Unknow render {self.RENDER}") raise ValueError(f"Unknow render {self.RENDER}")
# return renders[self.RENDER](self._mo) #return renders[self.RENDER](self._mo)
@property @property
def __txt__(self): def __txt__(self):
@ -84,29 +79,20 @@ class Token(object):
def __tex__(self): def __tex__(self):
return self._mo.__tex__ return self._mo.__tex__
@property
def raw(self):
""" Get python's raw forme of the token """
return self._mo.content
def _operate(self, other, operation): def _operate(self, other, operation):
""" Make a operation between 2 Tokens """ """ Make a operation between 2 Tokens,
a Token and en Expression ora
a Token an a builtin type
"""
from ..expression import Expression from ..expression import Expression
from ...core import Tree from ...core import Tree
from . import factory from . import factory
if not isinstance(other, Token): if not isinstance(other, Token):
try: _other = factory(other)
_other = factory(other)
except AttributeError:
_other = factory(Expression(moify(other)))
else: else:
_other = other _other = other
tree = Tree(operation, self._mo, _other._mo)
if operation == '-':
tree = Tree("+", self._mo, Tree("-", None, _other._mo))
else:
tree = Tree(operation, self._mo, _other._mo)
return Expression(tree).simplify() return Expression(tree).simplify()
def __add__(self, other): def __add__(self, other):
@ -123,18 +109,6 @@ class Token(object):
... print(i) ... print(i)
3 + 7 3 + 7
10 10
>>> a = Integer(3)
>>> c = a + 7
>>> c
<Integer 10>
>>> for i in c.explain():
... print(i)
3 + 7
10
>>> a = Integer(3)
>>> c = a + "x"
>>> c
<Linear x + 3>
>>> from .number import Fraction >>> from .number import Fraction
>>> a = Fraction("4/3") >>> a = Fraction("4/3")
>>> b = Integer(7) >>> b = Integer(7)
@ -152,32 +126,6 @@ class Token(object):
""" """
return self._operate(other, "+") return self._operate(other, "+")
def __sub__(self, other):
""" Subing 2 Tokens or a Token and a Expression
:example:
>>> from .number import Integer
>>> a = Integer(3)
>>> b = Integer(7)
>>> c = a - b
>>> c
<Integer - 4>
>>> for i in c.explain():
... print(i)
3 - 7
3 - 7
- 4
>>> a = Integer(3)
>>> c = a - 7
>>> c
<Integer - 4>
>>> a = Integer(3)
>>> c = a - "x"
>>> c
<Linear - x + 3>
"""
return self._operate(other, "-")
def __mul__(self, other): def __mul__(self, other):
""" Multiply 2 Tokens or a Token and a Expression """ Multiply 2 Tokens or a Token and a Expression
@ -192,12 +140,6 @@ class Token(object):
... print(i) ... print(i)
3 * 7 3 * 7
21 21
>>> c = a * 7
>>> c
<Integer 21>
>>> c = a * "x"
>>> c
<Linear 3x>
>>> from .number import Fraction >>> from .number import Fraction
>>> a = Fraction("4/3") >>> a = Fraction("4/3")
>>> b = Integer(7) >>> b = Integer(7)
@ -225,9 +167,6 @@ class Token(object):
>>> for i in c.explain(): >>> for i in c.explain():
... print(i) ... print(i)
3 / 7 3 / 7
>>> c = a / 7
>>> c
<Fraction 3 / 7>
>>> from .number import Fraction >>> from .number import Fraction
>>> a = Fraction("4/3") >>> a = Fraction("4/3")
>>> b = Integer(7) >>> b = Integer(7)
@ -243,129 +182,6 @@ class Token(object):
""" """
return self._operate(other, "/") return self._operate(other, "/")
def __pow__(self, other):
""" Token powered by an other
:example:
>>> from .number import Integer
>>> a = Integer(3)
>>> b = Integer(7)
>>> c = a ** b
>>> c
<Integer 2187>
>>> c = a ** 7
>>> c
<Integer 2187>
>>> from .number import Decimal
>>> a = Decimal('2.3')
>>> c = a ** 2
>>> c
<Decimal 5.29>
"""
return self._operate(other, "^")
def _roperate(self, other, operation):
""" Make a operation between 2 Tokens """
from ..expression import Expression
from ...core import Tree
from . import factory
if not isinstance(other, Token):
try:
_other = factory(other)
except AttributeError:
_other = factory(Expression(moify(other)))
else:
_other = other
if operation == '-':
tree = Tree("+", _other._mo, Tree("-", None, self._mo))
else:
tree = Tree(operation, _other._mo, self._mo)
return Expression(tree).simplify()
def __radd__(self, other):
""" Adding 2 Tokens or a Token and a Expression
:example:
>>> from .number import Integer
>>> a = Integer(3)
>>> c = 7 + a
>>> c
<Integer 10>
>>> c = "x" + a
>>> c
<Linear x + 3>
"""
return self._roperate(other, "+")
def __rsub__(self, other):
""" Subing 2 Tokens or a Token and a Expression
:example:
>>> from .number import Integer
>>> a = Integer(3)
>>> c = 7 - a
>>> c
<Integer 4>
>>> a = Integer(3)
>>> c = "x" - a
>>> c
<Linear x - 3>
"""
return self._roperate(other, "-")
def __rmul__(self, other):
""" Multiply 2 Tokens or a Token and a Expression
:example:
>>> from .number import Integer
>>> a = Integer(3)
>>> c = 7 * a
>>> c
<Integer 21>
>>> c = "x" * a
>>> c
<Linear 3x>
"""
return self._roperate(other, "*")
def __rtruediv__(self, other):
""" Divising 2 Tokens or a Token and a Expression
:example:
>>> from .number import Integer
>>> a = Integer(3)
>>> c = 7 / a
>>> c
<Fraction 7 / 3>
"""
return self._roperate(other, "/")
def _get_soul(self, other=None):
""" Get the builtin soul of self or other """
if isinstance(other, Token):
return other._mo._value
elif not other is None:
return other
return self._mo._value
def __eq__(self, other):
return self._get_soul() == self._get_soul(other)
def __gt__(self, other):
return self._get_soul() > self._get_soul(other)
def __lt__(self, other):
return self._get_soul() < self._get_soul(other)
def __ge__(self, other):
return self._get_soul() >= self._get_soul(other)
def __le__(self, other):
return self._get_soul() <= self._get_soul(other)
# ----------------------------- # -----------------------------
# Reglages pour 'vim' # Reglages pour 'vim'

View File

@ -30,10 +30,7 @@ Expression is the classe wich handle all calculus. It can randomly generate or i
""" """
from .API import Expression, Integer, Decimal from .API import Expression
from decimal import getcontext
#getcontext().prec = 2
__all__ = ["Expression"] __all__ = ["Expression"]

View File

@ -26,7 +26,6 @@ def moify(token):
except MOError: except MOError:
return token return token
@coroutine @coroutine
def moify_cor(target): def moify_cor(target):
""" Coroutine which try to convert a parsed token into an MO """ Coroutine which try to convert a parsed token into an MO
@ -92,12 +91,6 @@ class MOnumber(Atom):
>>> MOnumber(Decimal("-23.3")) >>> MOnumber(Decimal("-23.3"))
<MOnumber - 23.3> <MOnumber - 23.3>
Or directly passe a decimal string
>>> MOnumber("23.3")
<MOnumber 23.3>
>>> MOnumber("-23.3")
<MOnumber - 23.3>
MOnumber initialisation is idempotent MOnumber initialisation is idempotent
>>> a = MOnumber(23) >>> a = MOnumber(23)
@ -107,7 +100,7 @@ class MOnumber(Atom):
>>> MOnumber("a") >>> MOnumber("a")
Traceback (most recent call last): Traceback (most recent call last):
... ...
mapytex.calculus.core.MO.exceptions.MOError: ('The value of an MOnumber need to be a int, a float, a Decimal or a decimal string', "(got <class 'str'>)") mapytex.calculus.core.MO.exceptions.MOError: ('The value of an MOnumber need to be a int, a float or a Decimal', "(got <class 'str'>)")
Atoms specific property and methods Atoms specific property and methods
@ -124,26 +117,15 @@ class MOnumber(Atom):
""" """
if isinstance(value, Atom) and isinstance(value.value, (int, Decimal, float)): if isinstance(value, Atom) and isinstance(value.value, (int, Decimal, float)):
Atom.__init__(self, value.value) Atom.__init__(self, value.value)
elif isinstance(value, (float, Decimal)): elif isinstance(value, (int, Decimal)):
if int(value) == value:
Atom.__init__(self, int(value))
else:
Atom.__init__(self, Decimal(value))
elif isinstance(value, int):
Atom.__init__(self, value) Atom.__init__(self, value)
elif isinstance(value, float):
Atom.__init__(self, Decimal(value))
else: else:
try: raise MOError(
v = float(value) "The value of an MOnumber need to be a int, a float or a Decimal",
except (ValueError, TypeError): f"(got {type(value)})",
raise MOError( )
"The value of an MOnumber need to be a int, a float, a Decimal or a decimal string",
f"(got {type(value)})",
)
else:
if int(v) == v:
Atom.__init__(self, int(v))
else:
Atom.__init__(self, Decimal(value))
self._signature = "scalar" self._signature = "scalar"
@ -172,7 +154,7 @@ class MOnumber(Atom):
>>> MOnumber(-3).__tex__ >>> MOnumber(-3).__tex__
'- 3' '- 3'
""" """
if self.value >= 0: if self.value > 0:
return str(self.value) return str(self.value)
return f"- {abs(self.value)}" return f"- {abs(self.value)}"
@ -184,16 +166,6 @@ class MOnumber(Atom):
except AttributeError: except AttributeError:
return self.value < other return self.value < other
def differentiate(self):
""" differentiate a number and get 0
:example:
>>> a = MOnumber(3)
>>> a.differentiate()
<MOnumber 0>
"""
return MOnumber(0)
class MOstr(Atom): class MOstr(Atom):
@ -288,16 +260,6 @@ class MOstr(Atom):
def degree(self): def degree(self):
return 1 return 1
def differentiate(self):
""" differentiate a variable and get 1
:example:
>>> a = MOstr("x")
>>> a.differentiate()
<MOnumber 1>
"""
return MOnumber(1)
# ----------------------------- # -----------------------------
# Reglages pour 'vim' # Reglages pour 'vim'

View File

@ -8,7 +8,6 @@
from mapytex.calculus.core.tree import Tree from mapytex.calculus.core.tree import Tree
from .mo import Molecule, MO from .mo import Molecule, MO
from .atoms import MOnumber
__all__ = ["MOFraction"] __all__ = ["MOFraction"]
@ -35,7 +34,7 @@ class MOFraction(Molecule):
>>> print(f.__txt__) >>> print(f.__txt__)
2 / 3 2 / 3
>>> print(f.__tex__) >>> print(f.__tex__)
\\dfrac{2}{3} \\frac{2}{3}
>>> print(f) >>> print(f)
2 / 3 2 / 3
>>> f = MOFraction(2, 3, negative = True) >>> f = MOFraction(2, 3, negative = True)
@ -72,22 +71,6 @@ class MOFraction(Molecule):
""" return the inverse fraction """ """ return the inverse fraction """
return MOFraction(self._denominator, self._numerator, self.negative) return MOFraction(self._denominator, self._numerator, self.negative)
def differentiate(self):
""" differentiate a fraction and get something!
:example:
>>> a = MOFraction(2, 3)
>>> a.differentiate()
<MOnumber 0>
"""
d_num = self.numerator.differentiate()
d_denom = self.denominator.differentiate()
if d_num == 0 and d_denom == 0:
return MOnumber(0)
else:
raise NotImplementedError
# ----------------------------- # -----------------------------
# Reglages pour 'vim' # Reglages pour 'vim'

View File

@ -97,9 +97,6 @@ class MO(ABC):
""" """
return self._signature return self._signature
def differentiate(self):
raise NotImplementedError
class Atom(MO): class Atom(MO):

View File

@ -111,22 +111,6 @@ class MOstrPower(Molecule):
""" """
return f"monome{self.power}" return f"monome{self.power}"
def differentiate(self):
""" differentiate a MOstrPower and get a tree
:example:
>>> a = MOstrPower('x', 3)
>>> print(a.differentiate())
*
> 3
> x^2
"""
if self._power > 2:
return Tree(
"*", self.power, MOstrPower(self.variable, self._power._value - 1)
)
return Tree("*", self.power, MOstr(self.variable))
class MOMonomial(Molecule): class MOMonomial(Molecule):
@ -274,31 +258,6 @@ class MOMonomial(Molecule):
""" """
return f"monome{self.power}" return f"monome{self.power}"
def differentiate(self):
""" Differentiate a MOMonomial and get a tree
:example:
>>> x = MOstr('x')
>>> m = MOMonomial(4, x)
>>> m
<MOMonomial 4x>
>>> print(m.differentiate())
4
>>> m = MOMonomial(4, 'x', 2)
>>> m
<MOMonomial 4x^2>
>>> print(m.differentiate())
*
> 4
> *
| > 2
| > x
"""
if self.power == 1:
return self.coefficient
return Tree("*", self.coefficient, self.strpower.differentiate())
# ----------------------------- # -----------------------------
# Reglages pour 'vim' # Reglages pour 'vim'

View File

@ -6,7 +6,6 @@
# #
# Distributed under terms of the MIT license. # Distributed under terms of the MIT license.
from collections import OrderedDict
from mapytex.calculus.core.tree import Tree from mapytex.calculus.core.tree import Tree
from . import MO, MOstr from . import MO, MOstr
from .mo import Molecule from .mo import Molecule
@ -58,9 +57,8 @@ class MOpolynomial(Molecule):
raise TypeError("Coefs needs to be a dictionnary or a list") raise TypeError("Coefs needs to be a dictionnary or a list")
self._coefs = _coefs self._coefs = _coefs
monomials = OrderedDict() monomials = {}
for deg in sorted(self._coefs.keys()): for deg, coef in self._coefs.items():
coef = self._coefs[deg]
if deg == 0: if deg == 0:
monomials[deg] = coef monomials[deg] = coef
elif deg == 1 and coef == 1: elif deg == 1 and coef == 1:
@ -122,34 +120,12 @@ class MOpolynomial(Molecule):
:example: :example:
>>> p = MOpolynomial('x', [1, 2, 3]) >>> p = MOpolynomial('x', [1, 2, 3])
>>> p.monomials >>> p.monomials
OrderedDict([(<MOnumber 0>, <MOnumber 1>), (<MOnumber 1>, <MOMonomial 2x>), (<MOnumber 2>, <MOMonomial 3x^2>)]) {<MOnumber 0>: <MOnumber 1>, <MOnumber 1>: <MOMonomial 2x>, <MOnumber 2>: <MOMonomial 3x^2>}
>>> p.monomials.values() >>> p.monomials.values()
odict_values([<MOnumber 1>, <MOMonomial 2x>, <MOMonomial 3x^2>]) dict_values([<MOnumber 1>, <MOMonomial 2x>, <MOMonomial 3x^2>])
""" """
return self._monomials return self._monomials
def differentiate(self):
""" Differentiate a MOMonomial and get a tree
:example:
>>> p = MOpolynomial('x', [1, 2, 3])
>>> print(p)
3x^2 + 2x + 1
>>> print(p.differentiate())
+
> 0
> +
| > 2
| > *
| | > 3
| | > *
| | | > 2
| | | > x
"""
monomials_d = [m.differentiate() for m in self.monomials.values()]
return Tree.from_list("+", monomials_d)
# ----------------------------- # -----------------------------
# Reglages pour 'vim' # Reglages pour 'vim'

View File

@ -70,9 +70,6 @@ def monumber_monumber(left, right):
>>> b = MOnumber(6) >>> b = MOnumber(6)
>>> add(a, b) >>> add(a, b)
<MOnumber 10> <MOnumber 10>
>>> b = MOnumber('2.3')
>>> add(a, b)
<MOnumber 6.3>
""" """
return MO.factory(left.value + right.value) return MO.factory(left.value + right.value)

View File

@ -10,7 +10,7 @@
Tree renders Tree renders
""" """
__all__ = ["tree2txt", "tree2tex"] __all__ = ["tree2txt"]
from .tree2txt import tree2txt from .tree2txt import tree2txt
from .tree2tex import tree2tex from .tree2tex import tree2tex

View File

@ -117,17 +117,17 @@ def div2tex(left, right):
>>> from ..MO import MO >>> from ..MO import MO
>>> div2tex(MO.factory(2), MO.factory(3)) >>> div2tex(MO.factory(2), MO.factory(3))
'\\dfrac{2}{3}' '\\frac{2}{3}'
>>> from ..tree import Tree >>> from ..tree import Tree
>>> t = Tree.from_str("1/2") >>> t = Tree.from_str("1/2")
>>> div2tex(t, MO.factory(3)) >>> div2tex(t, MO.factory(3))
'\\dfrac{\\dfrac{1}{2}}{3}' '\\frac{\\frac{1}{2}}{3}'
>>> t = Tree.from_str("1+2") >>> t = Tree.from_str("1+2")
>>> div2tex(t, MO.factory(3)) >>> div2tex(t, MO.factory(3))
'\\dfrac{1 + 2}{3}' '\\frac{1 + 2}{3}'
>>> t = Tree.from_str("1*2") >>> t = Tree.from_str("1*2")
>>> div2tex(MO.factory(3), t) >>> div2tex(MO.factory(3), t)
'\\dfrac{3}{1 \\times 2}' '\\frac{3}{1 \\times 2}'
""" """
try: try:
left_ = tree2tex(left) left_ = tree2tex(left)
@ -138,7 +138,7 @@ def div2tex(left, right):
except (AttributeError, ValueError): except (AttributeError, ValueError):
right_ = right.__tex__ right_ = right.__tex__
return "\\dfrac{" + left_ + "}{" + right_ + "}" return "\\frac{" + left_ + "}{" + right_ + "}"
def pow2tex(left, right): def pow2tex(left, right):
@ -196,10 +196,7 @@ def render_with_parenthesis(subtree, operator):
subtree_need_parenthesis = True subtree_need_parenthesis = True
except (AttributeError, KeyError): except (AttributeError, KeyError):
pass pass
try: subtree_ = subtree.__tex__
subtree_ = subtree.__tex__
except AttributeError:
subtree_ = str(subtree)
else: else:
if OPERATORS[subtree.node]["precedence"] < OPERATORS[operator]["precedence"]: if OPERATORS[subtree.node]["precedence"] < OPERATORS[operator]["precedence"]:
subtree_need_parenthesis = True subtree_need_parenthesis = True

View File

@ -200,10 +200,7 @@ def render_with_parenthesis(subtree, operator):
subtree_need_parenthesis = True subtree_need_parenthesis = True
except (AttributeError, KeyError): except (AttributeError, KeyError):
pass pass
try: subtree_ = subtree.__txt__
subtree_ = subtree.__txt__
except AttributeError:
subtree_ = str(subtree)
else: else:
if OPERATORS[subtree.node]["precedence"] < OPERATORS[operator]["precedence"]: if OPERATORS[subtree.node]["precedence"] < OPERATORS[operator]["precedence"]:
subtree_need_parenthesis = True subtree_need_parenthesis = True

View File

@ -15,7 +15,7 @@ from decimal import Decimal, InvalidOperation
from .coroutine import * from .coroutine import *
from .operator import is_operator from .operator import is_operator
from .MO import moify_cor from .MO import moify_cor
from .random.leaf import look_for_rdleaf, RdLeaf from .random.leaf import look_for_rdleaf
__all__ = ["str2"] __all__ = ["str2"]
@ -395,10 +395,8 @@ def missing_times(target):
elif not is_operator(tok) and tok != ")": elif not is_operator(tok) and tok != ")":
target_.send("*") target_.send("*")
if ( if isinstance(tok, int) or (
isinstance(tok, int) isinstance(tok, str) and not is_operator(tok) and not tok == "("
or (isinstance(tok, str) and not is_operator(tok) and not tok == "(")
or (isinstance(tok, RdLeaf))
): ):
previous = tok previous = tok
@ -820,8 +818,31 @@ def rdstr2(sink):
>>> rdstr2list = rdstr2(list_sink) >>> rdstr2list = rdstr2(list_sink)
>>> rdstr2list("{a}+{a*b}-2") >>> rdstr2list("{a}+{a*b}-2")
[<RdLeaf a>, '+', <RdLeaf a*b>, '+', <MOnumber - 2>] [<RdLeaf a>, '+', <RdLeaf a*b>, '+', <MOnumber - 2>]
>>> rdstr2list("{a}({b}x+{c})") """
[<RdLeaf a>, '*', [<RdLeaf b>, '*', <MOstr x>, '+', <RdLeaf c>]] lfop = lookfor(is_operator)
operator_corout = partial(concurent_broadcast, lookfors=[lfop])
def pipeline(expression):
str2_corout = look_for_rdleaf(
lookforNumbers(operator_corout(missing_times(moify_cor(pparser(sink)))))
)
for i in expression.replace(" ", ""):
str2_corout.send(i)
a = str2_corout.throw(STOOOP)
return a
return pipeline
def rdstr2(sink):
""" Return a pipeline which parse random expression and with sink as endpoint
:example:
>>> rdstr2list = rdstr2(list_sink)
>>> rdstr2list("{a}+{a*b}-2")
[<RdLeaf a>, '+', <RdLeaf a*b>, '+', <MOnumber - 2>]
""" """
lfop = lookfor(is_operator) lfop = lookfor(is_operator)
operator_corout = partial(concurent_broadcast, lookfors=[lfop]) operator_corout = partial(concurent_broadcast, lookfors=[lfop])

View File

@ -77,22 +77,13 @@ class Tree:
> * > *
| > 3 | > 3
| > n | > n
>>> t = Tree.from_str("2+{n}x", random=True) >>> t = Tree.from_str("2+{n}*x", random=True)
>>> print(t) >>> print(t)
+ +
> 2 > 2
> * > *
| > {n} | > {n}
| > x | > x
>>> t = Tree.from_str("{a}({b}x+{c})", random=True)
>>> print(t)
*
> {a}
> +
| > *
| | > {b}
| | > x
| > {c}
""" """
@ -963,22 +954,7 @@ class MutableTree(Tree):
| | > 8 | | > 8
| | > 3 | | > 3
| > x | > x
>>> t = MutableTree.from_str("{b}*x+{c}", random=True)
>>> print(t)
+
> *
| > {b}
| > x
> {c}
>>> t = MutableTree.from_str("{a}*({b}*x+{c})", random=True)
>>> print(t)
*
> {a}
> +
| > *
| | > {b}
| | > x
| > {c}
""" """
if random: if random:
str_2_mut_tree = rdstr2(cls.sink) str_2_mut_tree = rdstr2(cls.sink)

View File

@ -95,10 +95,10 @@ def moscalar_momonomial(left, right):
>>> a = MOnumber(2) >>> a = MOnumber(2)
>>> b = MOMonomial(3, 'x', 4) >>> b = MOMonomial(3, 'x', 4)
>>> add(a, b) >>> add(a, b)
<MOpolynomial 3x^4 + 2> <MOpolynomial 2 + 3x^4>
>>> a = MOFraction(1, 5) >>> a = MOFraction(1, 5)
>>> add(a, b) >>> add(a, b)
<MOpolynomial 3x^4 + 1 / 5> <MOpolynomial 1 / 5 + 3x^4>
""" """
return MOpolynomial(right.variable, {right.power: right.coefficient, 0: left}) return MOpolynomial(right.variable, {right.power: right.coefficient, 0: left})
@ -126,10 +126,10 @@ def moscalar_mopolynomial(left, right):
>>> a = MOnumber(2) >>> a = MOnumber(2)
>>> b = MOpolynomial('x', [0, 2, 3]) >>> b = MOpolynomial('x', [0, 2, 3])
>>> add(a, b) >>> add(a, b)
<MOpolynomial 3x^2 + 2x + 2> <MOpolynomial 2 + 3x^2 + 2x>
>>> a = MOFraction(1, 5) >>> a = MOFraction(1, 5)
>>> add(a, b) >>> add(a, b)
<MOpolynomial 3x^2 + 2x + 1 / 5> <MOpolynomial 1 / 5 + 3x^2 + 2x>
""" """
if 0 in right.coefficients.keys(): if 0 in right.coefficients.keys():
raise NotImplementedError( raise NotImplementedError(
@ -237,7 +237,7 @@ def mostr_mopolynomial(left, right):
>>> a = MOstr("x") >>> a = MOstr("x")
>>> b = MOpolynomial('x', [1, 0, 3]) >>> b = MOpolynomial('x', [1, 0, 3])
>>> add(a, b) >>> add(a, b)
<MOpolynomial 3x^2 + x + 1> <MOpolynomial x + 3x^2 + 1>
""" """
if 1 in right.coefficients.keys(): if 1 in right.coefficients.keys():
raise NotImplementedError("Polynomial with no constant, calculus to do") raise NotImplementedError("Polynomial with no constant, calculus to do")
@ -255,7 +255,7 @@ def mopolynomial_mostr(left, right):
>>> a = MOpolynomial('x', [1, 0, 3]) >>> a = MOpolynomial('x', [1, 0, 3])
>>> b = MOstr("x") >>> b = MOstr("x")
>>> add(a, b) >>> add(a, b)
<MOpolynomial 3x^2 + x + 1> <MOpolynomial 3x^2 + 1 + x>
""" """
if 1 in left.coefficients.keys(): if 1 in left.coefficients.keys():
raise NotImplementedError("Polynomial with no constant, calculus to do") raise NotImplementedError("Polynomial with no constant, calculus to do")
@ -272,7 +272,7 @@ def mostrpower_mopolynomial(left, right):
>>> a = MOstrPower("x", 2) >>> a = MOstrPower("x", 2)
>>> b = MOpolynomial('x', [1, 2, 0, 4]) >>> b = MOpolynomial('x', [1, 2, 0, 4])
>>> add(a, b) >>> add(a, b)
<MOpolynomial 4x^3 + x^2 + 2x + 1> <MOpolynomial x^2 + 4x^3 + 2x + 1>
""" """
if left.power in right.coefficients.keys(): if left.power in right.coefficients.keys():
raise NotImplementedError("Degree in common, need to compute") raise NotImplementedError("Degree in common, need to compute")
@ -290,7 +290,7 @@ def mopolynomial_mostrpower(left, right):
>>> a = MOpolynomial('x', [1, 2, 0, 4]) >>> a = MOpolynomial('x', [1, 2, 0, 4])
>>> b = MOstrPower("x", 2) >>> b = MOstrPower("x", 2)
>>> add(a, b) >>> add(a, b)
<MOpolynomial 4x^3 + x^2 + 2x + 1> <MOpolynomial 4x^3 + 2x + 1 + x^2>
""" """
if right.power in left.coefficients.keys(): if right.power in left.coefficients.keys():
raise NotImplementedError("Degree in common, need to compute") raise NotImplementedError("Degree in common, need to compute")
@ -307,7 +307,7 @@ def momonomial_mopolynomial(left, right):
>>> a = MOMonomial(3, "x", 2) >>> a = MOMonomial(3, "x", 2)
>>> b = MOpolynomial('x', [1, 2, 0, 4]) >>> b = MOpolynomial('x', [1, 2, 0, 4])
>>> add(a, b) >>> add(a, b)
<MOpolynomial 4x^3 + 3x^2 + 2x + 1> <MOpolynomial 3x^2 + 4x^3 + 2x + 1>
""" """
if left.power in right.coefficients.keys(): if left.power in right.coefficients.keys():
raise NotImplementedError("Degree in common, need to compute") raise NotImplementedError("Degree in common, need to compute")
@ -325,7 +325,7 @@ def mopolynomial_momonomial(left, right):
>>> a = MOpolynomial('x', [1, 2, 0, 4]) >>> a = MOpolynomial('x', [1, 2, 0, 4])
>>> b = MOMonomial(3, "x", 2) >>> b = MOMonomial(3, "x", 2)
>>> add(a, b) >>> add(a, b)
<MOpolynomial 4x^3 + 3x^2 + 2x + 1> <MOpolynomial 4x^3 + 2x + 1 + 3x^2>
""" """
if right.power in left.coefficients.keys(): if right.power in left.coefficients.keys():
raise NotImplementedError("Degree in common, need to compute") raise NotImplementedError("Degree in common, need to compute")
@ -342,9 +342,9 @@ def mopolynomial_mopolynomial(left, right):
>>> a = MOpolynomial('x', [1, 0, 3]) >>> a = MOpolynomial('x', [1, 0, 3])
>>> b = MOpolynomial('x', [0, 2, 0, 4]) >>> b = MOpolynomial('x', [0, 2, 0, 4])
>>> add(a, b) >>> add(a, b)
<MOpolynomial 4x^3 + 3x^2 + 2x + 1> <MOpolynomial 3x^2 + 1 + 4x^3 + 2x>
>>> add(b, a) >>> add(b, a)
<MOpolynomial 4x^3 + 3x^2 + 2x + 1> <MOpolynomial 4x^3 + 2x + 3x^2 + 1>
""" """
common_degree = set(left.monomials.keys()).intersection(right.monomials.keys()) common_degree = set(left.monomials.keys()).intersection(right.monomials.keys())
if common_degree: if common_degree:
@ -361,7 +361,7 @@ def mostr_monomial(left, right):
>>> a = MOstr('x') >>> a = MOstr('x')
>>> b = MOMonomial(3, 'x', 4) >>> b = MOMonomial(3, 'x', 4)
>>> add(a, b) >>> add(a, b)
<MOpolynomial 3x^4 + x> <MOpolynomial x + 3x^4>
""" """
if right.power == 1: if right.power == 1:
raise NotImplementedError("Monomial is deg 1, need to compute") raise NotImplementedError("Monomial is deg 1, need to compute")
@ -391,7 +391,7 @@ def mostrpower_monomial(left, right):
>>> a = MOstrPower('x', 2) >>> a = MOstrPower('x', 2)
>>> b = MOMonomial(3, 'x', 4) >>> b = MOMonomial(3, 'x', 4)
>>> add(a, b) >>> add(a, b)
<MOpolynomial 3x^4 + x^2> <MOpolynomial x^2 + 3x^4>
""" """
if left.power == right.power: if left.power == right.power:
raise NotImplementedError( raise NotImplementedError(

View File

@ -15,7 +15,6 @@ from ..tree import Tree
from ..MO import MO, MOnumber, MOstr from ..MO import MO, MOnumber, MOstr
from ..MO.fraction import MOFraction from ..MO.fraction import MOFraction
from ..MO.monomial import MOstrPower, MOMonomial from ..MO.monomial import MOstrPower, MOMonomial
from ..compute.filters import special_case
multiply_doc = """ Multiply MOs multiply_doc = """ Multiply MOs
@ -28,38 +27,7 @@ multiply_doc = """ Multiply MOs
multiply = Dispatcher("multiply", doc=multiply_doc) multiply = Dispatcher("multiply", doc=multiply_doc)
def multiply_filter(left, right):
""" Automatic multiply on MO
:param left: MO
:param right: MO
:returns: MO if it is a special case, nothing other wise
"""
try:
if left == 0:
return left
except TypeError:
pass
try:
if right == 0:
return right
except TypeError:
pass
try:
if left == 1:
return right
except TypeError:
pass
try:
if right == 1:
return left
except TypeError:
pass
@multiply.register((MOnumber, MOFraction), MOstr) @multiply.register((MOnumber, MOFraction), MOstr)
@special_case(multiply_filter)
def moscalar_mostr(left, right): def moscalar_mostr(left, right):
""" Multiply a scalar with a letter to create a MOMonomial """ Multiply a scalar with a letter to create a MOMonomial
@ -75,7 +43,6 @@ def moscalar_mostr(left, right):
@multiply.register(MOstr, (MOnumber, MOFraction)) @multiply.register(MOstr, (MOnumber, MOFraction))
@special_case(multiply_filter)
def mostr_moscalar(left, right): def mostr_moscalar(left, right):
""" Multiply a scalar with a letter to create a MOMonomial """ Multiply a scalar with a letter to create a MOMonomial
@ -91,7 +58,6 @@ def mostr_moscalar(left, right):
@multiply.register((MOnumber, MOFraction), MOstrPower) @multiply.register((MOnumber, MOFraction), MOstrPower)
@special_case(multiply_filter)
def moscalar_mostrpower(left, right): def moscalar_mostrpower(left, right):
""" Multiply a scalar with a MOstrPower """ Multiply a scalar with a MOstrPower
@ -99,18 +65,12 @@ def moscalar_mostrpower(left, right):
>>> x = MOstrPower('x', 4) >>> x = MOstrPower('x', 4)
>>> multiply(a, x) >>> multiply(a, x)
<MOMonomial 4x^4> <MOMonomial 4x^4>
>>> a = MOnumber(1)
>>> x = MOstrPower('x', 4)
>>> multiply(a, x)
<MOstrPower x^4>
""" """
# if left == 1:
# return right
return MOMonomial(left, right) return MOMonomial(left, right)
@multiply.register(MOstrPower, (MOnumber, MOFraction)) @multiply.register(MOstrPower, (MOnumber, MOFraction))
@special_case(multiply_filter)
def mostrpower_moscalar(left, right): def mostrpower_moscalar(left, right):
""" Multiply a MOstrPower with a scalar """ Multiply a MOstrPower with a scalar

View File

@ -6,4 +6,4 @@ from .pythagore import random_pythagore
# ----------------------------- # -----------------------------
# Reglages pour 'vim' # Reglages pour 'vim'
# vim:set autoindent expandtab tabstop=4 shiftwidth=4: # vim:set autoindent expandtab tabstop=4 shiftwidth=4:
# cursor: 16 del # cursor: 16 del

View File

@ -5,7 +5,7 @@
from random import randint from random import randint
def random_pythagore(v_min=1, v_max=10, nbr_format=lambda x: x): def random_pythagore(v_min = 1, v_max = 10, nbr_format = lambda x : x) :
""" Generate a pythagore triplet """ Generate a pythagore triplet
:returns: (a,b,c) such that a^2 = b^2 + c^2 :returns: (a,b,c) such that a^2 = b^2 + c^2
@ -14,11 +14,10 @@ def random_pythagore(v_min=1, v_max=10, nbr_format=lambda x: x):
while u == v: while u == v:
u, v = randint(v_min, v_max), randint(v_min, v_max) u, v = randint(v_min, v_max), randint(v_min, v_max)
u, v = max(u, v), min(u, v) u, v = max(u, v), min(u, v)
triplet = (u ** 2 + v ** 2, 2 * u * v, u ** 2 - v ** 2) triplet = (u**2+v**2, 2*u*v, u**2-v**2)
formated_triplet = [nbr_format(i) for i in triplet] formated_triplet = [nbr_format(i) for i in triplet]
return formated_triplet return formated_triplet
# ----------------------------- # -----------------------------
# Reglages pour 'vim' # Reglages pour 'vim'
# vim:set autoindent expandtab tabstop=4 shiftwidth=4: # vim:set autoindent expandtab tabstop=4 shiftwidth=4:

View File

@ -1,4 +1,4 @@
# /usr/bin/env python #/usr/bin/env python
# -*- coding:Utf-8 -*- # -*- coding:Utf-8 -*-
# #
@ -32,17 +32,11 @@ class Dataset(list):
""" """
@classmethod @classmethod
def random( def random(cls, length, data_name="Valeurs",
cls, distrib="gauss", rd_args=(0, 1),
length, nbr_format=lambda x: round(x, 2),
data_name="Valeurs", v_min=None, v_max=None,
distrib="gauss", exact_mean=None):
rd_args=(0, 1),
nbr_format=lambda x: round(x, 2),
v_min=None,
v_max=None,
exact_mean=None,
):
""" Generate a random list of value """ Generate a random list of value
:param length: length of the dataset :param length: length of the dataset
@ -53,9 +47,11 @@ class Dataset(list):
:param v_max: maximum accepted value :param v_max: maximum accepted value
:param exact_mean: if set, the last generated number will be create in order that the computed mean is exacly equal to "exact_mean" :param exact_mean: if set, the last generated number will be create in order that the computed mean is exacly equal to "exact_mean"
""" """
data = random_generator( data = random_generator(length,
length, distrib, rd_args, nbr_format, v_min, v_max, exact_mean distrib, rd_args,
) nbr_format,
v_min, v_max,
exact_mean)
return cls(data, data_name=data_name) return cls(data, data_name=data_name)
@ -98,7 +94,7 @@ class Dataset(list):
def deviation(self): def deviation(self):
""" Compute the deviation (not normalized) """ """ Compute the deviation (not normalized) """
mean = self.mean() mean = self.mean()
return sum([(x - mean) ** 2 for x in self]) return sum([(x - mean)**2 for x in self])
@number_factory @number_factory
def variance(self): def variance(self):
@ -124,8 +120,7 @@ class Dataset(list):
self.quartile(1), self.quartile(1),
self.quartile(2), self.quartile(2),
self.quartile(3), self.quartile(3),
max(self), max(self))
)
@number_factory @number_factory
def quartile(self, quartile=1): def quartile(self, quartile=1):
@ -178,21 +173,18 @@ class Dataset(list):
""" Latex code to display dataset as a tabular """ """ Latex code to display dataset as a tabular """
d_per_line = self.effectif_total() // nbr_lines d_per_line = self.effectif_total() // nbr_lines
d_last_line = self.effectif_total() % d_per_line d_last_line = self.effectif_total() % d_per_line
splited_data = [ splited_data = [self[x:x + d_per_line]
self[x : x + d_per_line] for x in range(0, self.effectif_total(), d_per_line)]
for x in range(0, self.effectif_total(), d_per_line)
]
# On ajoute les éléments manquant pour la dernière line # On ajoute les éléments manquant pour la dernière line
if d_last_line: if d_last_line:
splited_data[-1] += [" "] * (d_per_line - d_last_line) splited_data[-1] += [' '] * (d_per_line - d_last_line)
# Construction du tableau # Construction du tableau
latex = "\\begin{{tabular}}{{|c|*{{{nbr_col}}}{{c|}}}} \n".format( latex = "\\begin{{tabular}}{{|c|*{{{nbr_col}}}{{c|}}}} \n".format(
nbr_col=d_per_line nbr_col=d_per_line)
)
latex += "\t\t \hline \n" latex += "\t\t \hline \n"
d_lines = [" & ".join(map(str, l)) for l in splited_data] d_lines = [' & '.join(map(str, l)) for l in splited_data]
latex += " \\\\ \n \\hline \n".join(d_lines) latex += " \\\\ \n \\hline \n".join(d_lines)
latex += " \\\\ \n \\hline \n" latex += " \\\\ \n \\hline \n"

View File

@ -1,4 +1,4 @@
# /usr/bin/env python #/usr/bin/env python
# -*- coding:Utf-8 -*- # -*- coding:Utf-8 -*-
from functools import wraps from functools import wraps
@ -6,7 +6,6 @@ from functools import wraps
def number_factory(fun): def number_factory(fun):
""" Decorator which format returned value """ """ Decorator which format returned value """
@wraps(fun) @wraps(fun)
def wrapper(*args, **kwargs): def wrapper(*args, **kwargs):
ans = fun(*args, **kwargs) ans = fun(*args, **kwargs)
@ -17,7 +16,6 @@ def number_factory(fun):
return round(ans, 2) return round(ans, 2)
except AttributeError: except AttributeError:
return ans return ans
return wrapper return wrapper

View File

@ -1,18 +1,14 @@
# /usr/bin/env python #/usr/bin/env python
# -*- coding:Utf-8 -*- # -*- coding:Utf-8 -*-
from random import randint, uniform, gauss, choice from random import randint, uniform, gauss, choice
def random_generator( def random_generator(length,
length, distrib=gauss, rd_args=(0, 1),
distrib=gauss, nbr_format=lambda x: round(x, 2),
rd_args=(0, 1), v_min=None, v_max=None,
nbr_format=lambda x: round(x, 2), exact_mean=None):
v_min=None,
v_max=None,
exact_mean=None,
):
""" Generate a random list of value """ Generate a random list of value
:param length: length of the dataset :param length: length of the dataset
@ -23,18 +19,12 @@ def random_generator(
:param v_max: maximum accepted value :param v_max: maximum accepted value
:param exact_mean: if set, the last generated number will be create in order that the computed mean is exacly equal to "exact_mean" :param exact_mean: if set, the last generated number will be create in order that the computed mean is exacly equal to "exact_mean"
>>> random_generator(10) # doctest: +SKIP >>> random_generator(10)
[-0.76, 0.46, 0.19, 0.08, -1.13, -0.5, 0.47, -2.11, 0.16, -1.05] >>> random_generator(10, distrib = uniform, rd_args = (5, 10))
>>> random_generator(10, distrib = uniform, rd_args = (5, 10)) # doctest: +SKIP >>> random_generator(10, distrib = "uniform", rd_args = (5, 10))
[9.01, 5.32, 5.59, 8.8, 7.36, 6.9, 6.05, 7.44, 9.47, 6.95] >>> random_generator(10, v_min = 0)
>>> random_generator(10, distrib = "uniform", rd_args = (5, 10)) # doctest: +SKIP >>> random_generator(10, exact_mean = 0)
[7.85, 9.01, 5.32, 5.59, 8.8, 7.36, 6.9, 6.05, 7.44, 9.47] >>> random_generator(10, distrib = gauss, rd_args = (50,20), nbr_format = int)
>>> random_generator(10, v_min = 0) # doctest: +SKIP
[0.46, 0.19, 0.08, 0.47, 0.16, 0.87, 0.17, 1.79, 0.19, 1.12]
>>> random_generator(10, exact_mean = 0) # doctest: +SKIP
[-0.76, 0.46, 0.19, 0.08, -1.13, -0.5, 0.47, -2.11, 0.16, 3.14]
>>> random_generator(10, distrib = gauss, rd_args = (50,20), nbr_format = int) # doctest: +SKIP
[34, 59, 53, 51, 27, 40, 59, 7, 53, 28]
""" """
# if exact_mean is set, we create automaticaly only length-1 value # if exact_mean is set, we create automaticaly only length-1 value
@ -57,8 +47,7 @@ def random_generator(
"gauss": gauss, "gauss": gauss,
"uniform": uniform, "uniform": uniform,
"randint": randint, "randint": randint,
"choice": choice, "choice": choice}
}
try: try:
distrib(*rd_args) distrib(*rd_args)
except TypeError: except TypeError:
@ -78,13 +67,11 @@ def random_generator(
last_v = nbr_format((length + 1) * exact_mean - sum(data)) last_v = nbr_format((length + 1) * exact_mean - sum(data))
if not validate(last_v): if not validate(last_v):
raise ValueError( raise ValueError(
"Can't build the last value. Conflict between v_min/v_max and exact_mean" "Can't build the last value. Conflict between v_min/v_max and exact_mean")
)
data.append(last_v) data.append(last_v)
return data return data
# ----------------------------- # -----------------------------
# Reglages pour 'vim' # Reglages pour 'vim'
# vim:set autoindent expandtab tabstop=4 shiftwidth=4: # vim:set autoindent expandtab tabstop=4 shiftwidth=4:

View File

@ -1,4 +1,4 @@
# /usr/bin/env python #/usr/bin/env python
# -*- coding:Utf-8 -*- # -*- coding:Utf-8 -*-
""" """
@ -12,11 +12,9 @@ from .dataset import Dataset
from itertools import chain from itertools import chain
from .number_tools import number_factory from .number_tools import number_factory
def flatten_list(l): def flatten_list(l):
return list(chain(*l)) return list(chain(*l))
class WeightedDataset(dict): class WeightedDataset(dict):
""" A weighted dataset with statistics and latex rendering methods """ A weighted dataset with statistics and latex rendering methods
@ -39,8 +37,11 @@ class WeightedDataset(dict):
""" """
def __init__( def __init__(
self, datas=[], data_name="Valeurs", weights=[], weight_name="Effectifs" self,
): datas=[],
data_name="Valeurs",
weights=[],
weight_name="Effectifs"):
""" """
Initiate the WeightedDataset Initiate the WeightedDataset
""" """
@ -83,7 +84,7 @@ class WeightedDataset(dict):
def deviation(self): def deviation(self):
""" Compute the deviation (not normalized) """ """ Compute the deviation (not normalized) """
mean = self.mean() mean = self.mean()
return sum([v * (k - mean) ** 2 for (k, v) in self.items()]) return sum([v * (k - mean)**2 for (k, v) in self.items()])
@number_factory @number_factory
def variance(self): def variance(self):
@ -108,13 +109,11 @@ class WeightedDataset(dict):
(1, 3, 4, 5, 5) (1, 3, 4, 5, 5)
""" """
return ( return (min(self.keys()),
min(self.keys()), self.quartile(1),
self.quartile(1), self.quartile(2),
self.quartile(2), self.quartile(3),
self.quartile(3), max(self.keys()))
max(self.keys()),
)
@number_factory @number_factory
def quartile(self, quartile=1): def quartile(self, quartile=1):
@ -147,9 +146,8 @@ class WeightedDataset(dict):
position = self.posi_quartile(quartile) - 1 position = self.posi_quartile(quartile) - 1
expanded_values = flatten_list([v * [k] for (k, v) in self.items()]) expanded_values = flatten_list([v * [k] for (k, v) in self.items()])
if position.is_integer(): if position.is_integer():
return ( return (expanded_values[int(position)] +
expanded_values[int(position)] + expanded_values[int(position) + 1] expanded_values[int(position) + 1]) / 2
) / 2
else: else:
return expanded_values[ceil(position)] return expanded_values[ceil(position)]
@ -169,8 +167,7 @@ class WeightedDataset(dict):
def tabular_latex(self): def tabular_latex(self):
""" Latex code to display dataset as a tabular """ """ Latex code to display dataset as a tabular """
latex = "\\begin{{tabular}}{{|c|*{{{nbr_col}}}{{c|}}}} \n".format( latex = "\\begin{{tabular}}{{|c|*{{{nbr_col}}}{{c|}}}} \n".format(
nbr_col=len(self.keys()) nbr_col=len(self.keys()))
)
latex += "\t \hline \n" latex += "\t \hline \n"
data_line = "\t {data_name} ".format(data_name=self.data_name) data_line = "\t {data_name} ".format(data_name=self.data_name)
weight_line = "\t {weight_name} ".format(weight_name=self.weight_name) weight_line = "\t {weight_name} ".format(weight_name=self.weight_name)

View File

@ -1,35 +0,0 @@
#!/usr/bin/env python
import nox
@nox.session
def lint(session):
session.install("black")
session.run("black", "mapytex", "noxfile.py", "setup.py")
@nox.session
def test(session):
session.install("-r", "requirements.txt")
session.install("pytest")
session.run("pytest", "mapytex")
@nox.session
def docs(session):
"""Build the documentation."""
session.run("rm", "-rf", "documentation/_build", external=True)
session.install("sphinx", "sphinx-autobuild", "sphinx_rtd_theme")
session.install(".")
session.cd("documentation/source")
sphinx_args = ["-b", "html", "-W", "-d", "_build/doctrees", ".", "_build/html"]
if not session.interactive:
sphinx_cmd = "sphinx-build"
else:
sphinx_cmd = "sphinx-autobuild"
sphinx_args.insert(0, "--open-browser")
session.run(sphinx_cmd, *sphinx_args)

View File

@ -6,14 +6,17 @@ except ImportError:
from distutils.core import setup from distutils.core import setup
setup( setup(
name="mapytex", name='mapytex',
version="2.1", version='2.1',
description="Computing like a student", description='Computing like a student',
author="Benjamin Bertrand", author='Benjamin Bertrand',
author_email="programming@opytex.org", author_email='programming@opytex.org',
url="http://git.opytex.org/lafrite/Mapytex", url='http://git.opytex.org/lafrite/Mapytex',
# packages=['mapytex'], #packages=['mapytex'],
packages=find_packages(), packages=find_packages(),
include_package_data=True, include_package_data = True,
install_requires=["multipledispatch", "tabulate"], install_requires=[
) 'multipledispatch',
'tabulate',
],
)