4 Commits

Author SHA1 Message Date
fdf3b088f2 Mise à jour de 'README.md'
Some checks failed
continuous-integration/drone/push Build is failing
2020-07-18 06:29:21 +00:00
c7bd77e341 Mise à jour de '.drone.yml'
Some checks failed
continuous-integration/drone/push Build is failing
2020-07-18 06:21:50 +00:00
37601be549 Feat: nothing
Some checks failed
continuous-integration/drone/push Build is failing
2020-07-17 20:37:51 +02:00
19cdddf27e Feat: add a drone for testing 2020-07-17 20:37:00 +02:00
39 changed files with 447 additions and 1536 deletions

View File

@@ -7,12 +7,3 @@ steps:
commands:
- pip install -r requirements.txt
- pytest --doctest-modules ./mapytex/
- name: Publish
image: plugins/pypi
settings:
username:
from_secret: pypi_username
password:
from_secret: pypi_password
when:
event: tag

2
.gitignore vendored
View File

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

View File

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

View File

@@ -84,20 +84,19 @@ x^7
>>> e = Expression.from_str("1+2x^2+3x+4+5x")
>>> e_simplified = e.simplify()
>>> e_simplified
<Quadratic 2x^2 + 8x + 5>
<Quadratic 5 + 2x^2 + 8x>
>>> for s in e_simplified.explain():
... print(s)
1 + 2x^2 + 3x + 4 + 5x
2x^2 + 3x + 1 + 4 + 5x
2x^2 + 3x + 5x + 1 + 4
2x^2 + (3 + 5) * x + 5
2x^2 + 8x + 5
1 + 2x^2 + 3x + 4 + 5x
1 + 4 + 2x^2 + 3x + 5x
5 + 2x^2 + (3 + 5) * x
5 + 2x^2 + 8x
>>> e = Expression.from_str("(2x+3)^2")
>>> e_simplified = e.simplify()
>>> e_simplified
<Quadratic 4x^2 + 12x + 9>
<Quadratic 12x + 4x^2 + 9>
>>> for s in e_simplified.explain():
... print(s)
(2x + 3)^2
@@ -106,37 +105,26 @@ x^7
2 * 2 * x^(1 + 1) + 3 * 2 * x + 3 * 2 * x + 9
6x + 6x + 4x^2 + 9
(6 + 6) * x + 4x^2 + 9
4x^2 + 12x + 9
12x + 4x^2 + 9
>>> e = Expression.from_str("(2x-3)(-x+2)")
>>> e_simplified = e.simplify()
>>> e_simplified
<Quadratic - 2x^2 + 7x - 6>
>>> for s in e_simplified.explain():
... print(s)
(2x - 3)(- x + 2)
(2x - 3)(- x + 2)
2x(- x) + 2x * 2 - 3(- x) - 3 * 2
2(- 1) * x^(1 + 1) + 2 * 2 * x - 3(- 1) * x - 6
4x + 3x - 2x^2 - 6
(4 + 3) * x - 2x^2 - 6
- 2x^2 + 7x - 6
"""
from .renders import render
from .expression import Expression
from .tokens import Token
from .tokens.polynomial import Polynomial
from .tokens.number import Integer, Decimal, Fraction
if __name__ == "__main__":
e = Expression.from_str("(2x-3)(-x+2)")
e_simplified = e.simplify()
e_simplified
for s in e_simplified.explain():
print(s._tree.map_on_leaf(lambda x: type(x)))
print(s)
e = Expression.from_str("1+2/3/4/5")
et = e._typing()
print("typing")
print(e._tree)
e = et._order()
print("order")
print(e._tree)
e = e._optimize()
print("then optimize")
print(e._tree)
e = et._optimize()
print("optimize without order")
print(e._tree)
# -----------------------------
# Reglages pour 'vim'

View File

@@ -10,18 +10,11 @@
Expression
"""
from functools import partial
from ..core import AssocialTree, Tree, compute, typing, TypingError
from ..core.random import (
extract_rdleaf,
extract_rv,
random_generator,
compute_leafs,
replace_rdleaf,
)
from ..core.random import extract_rdleaf, extract_rv, random_generator, compute_leafs, replace_rdleaf
from ..core.MO import moify
from .tokens import factory
from .renders import render
from .tokens import factory, Token
from .renders import renders
class Expression(object):
@@ -42,6 +35,7 @@ class Expression(object):
14
"""
RENDER = "txt"
def __init__(self, tree, ancestor=None):
"""
@@ -49,6 +43,39 @@ class Expression(object):
self._tree = tree
self._ancestor = ancestor
@classmethod
def set_render(cls, render):
""" Define default render function
:param render: render name (txt or tex)
:example:
>>> e = Expression.from_str("2+3*4")
>>> print(e)
2 + 3 * 4
>>> e = Expression.from_str("2+3/4")
>>> print(e)
2 + 3 / 4
>>> es = e.simplify()
>>> print(es)
11 / 4
>>> Expression.set_render('tex')
>>> Expression.RENDER
'tex'
>>> e = Expression.from_str("2+3*4")
>>> print(e)
2 + 3 \\times 4
>>> e = Expression.from_str("2+3/4")
>>> print(e)
2 + \\frac{3}{4}
>>> es = e.simplify()
>>> print(es)
\\frac{11}{4}
>>> Expression.set_render('txt')
"""
Token.set_render(render)
cls.RENDER = render
@classmethod
def from_str(cls, string, typing=True):
""" Initiate the expression from a string
@@ -68,14 +95,15 @@ class Expression(object):
<Linear 2x + 1>
>>> e = Expression.from_str("2x + 1 + 5x^2")
>>> e
<Quadratic 5x^2 + 2x + 1>
<Quadratic 2x + 1 + 5x^2>
>>> e = Expression.from_str("2x + 1 + 5x")
>>> e
<Exp: 2x + 1 + 5x>
"""
t = Tree.from_str(string)
if typing:
return cls._post_processing(t)
return cls._post_precessing(t)
return cls(t)
@@ -100,11 +128,9 @@ class Expression(object):
:returns: TODO
:example:
>>> e = Expression.random("{a}/{a*k}")
>>> e # doctest: +SKIP
>>> Expression.random("{a}/{a*k}") # doctest: +SKIP
<Exp: -3 / -15>
>>> e = Expression.random("{a}/{a*k} - 3*{b}", variables_scope={'a':{'min_max':(10, 30)}})
>>> e # doctest: +SKIP
>>> Expression.random("{a}/{a*k} - 3*{b}", variables_scope={'a':{'min_max':(10, 30)}}) # doctest: +SKIP
<Exp: 18 / 108 - 3 * 9>
>>> e = Expression.random("{a}*x + {b}*x + 3", ["a>b"], rejected=[0, 1])
>>> ee = e.simplify()
@@ -126,10 +152,10 @@ class Expression(object):
if shuffle:
raise NotImplemented("Can't suffle expression yet")
return cls._post_processing(t)
return cls._post_precessing(t)
@classmethod
def _post_processing(cls, t):
def _post_precessing(cls, t):
""" Post process the tree by typing it """
tt = cls(t)._typing()
try:
@@ -138,16 +164,16 @@ class Expression(object):
return cls(t)
def __str__(self):
return render(self._tree)
return renders[self.RENDER](self._tree)
def __repr__(self):
return f"<Exp: {render(self._tree, 'txt')}>"
return f"<Exp: {renders['txt'](self._tree)}>"
def _order(self, exclude_nodes=["*", "/", "**"]):
""" Order the expression base on types
:example:
>>> e = Expression.from_str("1 + 2x + 3 + 4x")
>>> print(e)
1 + 2x + 3 + 4x
@@ -168,8 +194,7 @@ class Expression(object):
return type(leaf)
else:
try:
typed_leaf = typing(
leaf.node, leaf.left_value, leaf.right_value)
typed_leaf = typing(leaf.node, leaf.left_value, leaf.right_value)
return typed_leaf.signature
except (AttributeError, NotImplementedError, TypingError):
return type(leaf)
@@ -186,7 +211,7 @@ class Expression(object):
def _optimize(self, exclude_nodes=["/", "**"]):
""" Return a copy of self with an optimize tree
:example:
>>> e = Expression.from_str("2x^2+2x+3x")
>>> print(e._tree)
@@ -227,7 +252,7 @@ class Expression(object):
def _typing(self):
""" Build a copy of self with as much typing as possible
:example:
>>> e = Expression.from_str("2x", typing=False)
>>> print(e._tree.map_on_leaf(lambda x: type(x).__name__))
@@ -272,7 +297,7 @@ class Expression(object):
def _compute(self):
"""" Compute one step of self
"""
try:
return Expression(self._tree.apply_on_last_level(compute))
@@ -309,15 +334,15 @@ class Expression(object):
comp_exp = opt_exp._compute()
if typed_exp != comp_exp:
if typed_exp == comp_exp:
typed_exp.set_ancestor(self._ancestor)
return typed_exp
else:
comp_exp.set_ancestor(self)
return comp_exp._simplify(optimize=optimize)
typed_exp.set_ancestor(self._ancestor)
return typed_exp
def simplify(self, optimize=True):
""" Simplify the expression, keep the history and factory child
""" Compute as much as possible the expression
:param optimize: bool to optimize tree when it's possible
:return: an expression
@@ -337,7 +362,7 @@ class Expression(object):
def explain(self):
""" Yield every calculus step which have lead to self
:example:
>>> e = Expression.from_str("2+3*4")
>>> f = e.simplify()
@@ -412,65 +437,6 @@ class Expression(object):
else:
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'

View File

@@ -12,59 +12,34 @@ Expression
"""
from ..core import tree2txt, tree2tex
class Render(object):
""" Object which render Expression or token """
def __init__(self, default="txt"):
self._default = default
self._render = default
self.renders = {}
def _txt(mo_tree):
""" txt render for MOs or Trees"""
try:
return tree2txt(mo_tree)
except ValueError:
pass
def register_render(self, name, func, attribute):
""" register a render """
try:
self.renders[name]
except KeyError:
self.renders[name] = {"name": name, "func": func, "attribute": attribute}
else:
raise ValueError("This render name already exists")
@property
def render_name(self):
return self._render
@property
def render(self):
return self.renders[self._render]
def __call__(self, mo_tree, tmp_render=''):
if tmp_render:
r = self.renders[tmp_render]
else:
r = self.render
try:
return r["func"](mo_tree)
except ValueError:
pass
try:
return getattr(mo_tree, r["attribute"])
except AttributeError:
return str(mo_tree)
def set_render(self, render):
""" Define the render """
if render in self.renders.keys():
self._render = render
else:
raise ValueError("This render does not exists")
try:
return mo_tree.__txt__
except AttributeError:
return str(mo_tree)
def _tex(mo_tree):
""" Tex render for MOs or Trees"""
try:
return tree2tex(mo_tree)
except ValueError:
pass
render = Render()
render.register_render("txt", tree2txt, "__txt__")
render.register_render("tex", tree2tex, "__tex__")
try:
return mo_tree.__tex__
except AttributeError:
return str(mo_tree)
renders = {"txt": _txt, "tex": _tex}
# -----------------------------
# Reglages pour 'vim'

View File

@@ -10,163 +10,22 @@
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.monomial import MOstrPower, MOMonomial
from ...core.MO.polynomial import MOpolynomial
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
__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):
""" 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:
>>> from ..expression import Expression
@@ -202,7 +61,46 @@ def factory(exp, name="", ancestor=None):
if not isinstance(mo, 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,9 +16,8 @@ from ...core.arithmetic import gcd
from ...core.random.int_gene import filter_random
from ...core.MO import MO, MOnumber
from ...core.MO.fraction import MOFraction
from random import random
__all__ = ["Integer", "Decimal", "Fraction"]
__all__ = ["Integer", "Decimal"]
class Integer(Token):
@@ -141,7 +140,7 @@ class Decimal(Token):
class Fraction(Token):
""" Token representing a fraction of numbers
""" Token representing a fraction
:example:
>>> Fraction("3/4")
@@ -237,90 +236,11 @@ class Fraction(Token):
@property
def numerator(self):
""" Get numerator of the fraction
:example:
>>> a = Fraction("3/4")
>>> a.numerator
<Integer 3>
"""
return Integer(self._mo.numerator)
return self._mo.numerator
@property
def denominator(self):
""" Get denominator of the fraction
:example:
>>> a = Fraction("3/4")
>>> a.denominator
<Integer 4>
"""
return Integer(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(self._mo._value)
@property
def simplified(self):
""" Get the irreductible version of self
:example:
>>> f = Fraction("3/4")
>>> f.simplified
<Fraction 3 / 4>
>>> f = Fraction("12/9")
>>> f.simplified
<Fraction 4 / 3>
>>> f = Fraction("12/4")
>>> f.simplified
<Integer 3>
"""
simplified = self._mo.simplified()
if isinstance(simplified, MOnumber):
return Integer(simplified)
return Fraction(simplified)
def simplify(self):
""" Itself or its simplified version
:example:
>>> f = Fraction("12/8")
>>> fs = f.simplify()
>>> for i in fs.explain():
... print(i)
12 / 8
3 / 2
>>> f = Fraction("5/8")
>>> fs = f.simplify()
>>> for i in fs.explain():
... print(i)
5 / 8
"""
simplified = self.simplified
try:
if self.numerator == simplified.numerator:
return self
except AttributeError:
pass
simplified._ancestor = self
return simplified
return self._mo.denominator
# -----------------------------

View File

@@ -10,33 +10,26 @@
Tokens representing polynomials functions
"""
from ..expression import Expression
from .token import Token
from . import to_be_token
from ...core.MO import MO
from ...core.MO.atoms import moify
from ...core.MO.polynomial import MOpolynomial
__all__ = ["Polynomial", "Quadratic", "Linear"]
class Polynomial(Token):
""" Token representing a polynomial
:examples:
>>> from ...core.MO.polynomial import MOpolynomial
>>> P = Polynomial(MOpolynomial('x', [1, 2, 3]))
>>> P
<Polynomial 3x^2 + 2x + 1>
"""
""" Token representing a polynomial """
def __init__(self, a, name="", ancestor=None):
""" Initiate Polynomial with 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"
@classmethod
@@ -44,284 +37,49 @@ class Polynomial(Token):
return cls(mo, name, ancestor)
@classmethod
def from_coefficients(cls, coefficients, variable_name="x", name=""):
""" Initiate polynomial from list of coefficients
:examples:
>>> P = Polynomial.from_coefficients([1, 2, 3])
>>> P
<Polynomial 3x^2 + 2x + 1>
>>> P = Polynomial.from_coefficients([1, 2, -3])
>>> P
<Polynomial - 3x^2 + 2x + 1>
>>> P = Polynomial.from_coefficients([1, 2, 3], "y")
>>> P
<Polynomial 3y^2 + 2y + 1>
"""
return cls(MOpolynomial(variable_name, coefficients), name)
@classmethod
def random(cls):
raise NotImplementedError
@property
def raw(self):
raise NotImplementedError("Polynomial does not exists in python")
raise NotImplemented
def __setitem__(self, key, item):
""" 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):
""" Use Polynomial like if they were a dictionnary to get coefficients
: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]
""" Use Polynomial like if they were a dictionnary to get coefficients """
pass
def __call__(self, value):
""" Call a Polynomial to evaluate itself on value
: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")
""" Call a Polynomial to evaluate itself on value """
pass
class Linear(Polynomial):
""" Token representing a linear ax + b
: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
[<Integer - 2>]
>>> for i in P.roots[0].explain():
... print(i)
- 2 / 1
- 2
"""
""" Token representing a linear """
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)
self._mathtype = "affine"
@classmethod
def random(cls):
raise NotImplementedError
@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
[<Integer - 2>]
>>> P = Linear(MOpolynomial('x', [2, 1]))
>>> P.roots
[<Fraction - 1 / 2>]
>>> for i in P.roots[0].explain():
... print(i)
- 1 / 2
>>> P = Linear(MOpolynomial('x', [10, 6]))
>>> P.roots
[<Fraction - 3 / 5>]
>>> for i in P.roots[0].explain():
... print(i)
- 6 / 10
- 3 / 5
"""
try:
return [Expression.from_str(f"-{self.a}/{self.b}").simplify()]
except AttributeError:
return [Expression.from_str(f"-{self.a}/{self.b}")]
raise NotImplemented
class Quadratic(Polynomial):
""" Token representing a quadratic ax^2 + bx + c
: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
[]
"""
""" Token representing a quadratic """
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)
self._mathtype = "polynome du 2nd degré"
@classmethod
def random(cls):
raise NotImplementedError
@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
raise NotImplemented
# -----------------------------

View File

@@ -10,14 +10,15 @@
Tokens: practical envelop of math object
"""
from ..renders import render
from ...core.MO.atoms import moify
from ..renders import renders
class Token(object):
""" Token: practical envelop of an math object """
RENDER = "txt"
def __init__(self, mo, name="", ancestor=None):
self._mo = mo
self.name = name
@@ -25,16 +26,20 @@ class Token(object):
self._ancestor = ancestor
@classmethod
def random(
cls,
family="integer",
**kwds
):
def random(cls):
raise NotImplemented
@classmethod
def set_render(cls, render):
""" Define default render function
:param render: render name (txt or tex)
"""
cls.RENDER = render
def explain(self):
""" Yield every calculus step which have lead to self
:example:
>>> from mapytex.calculus.API import Expression
>>> e = Expression.from_str("2+3*4")
@@ -54,40 +59,40 @@ class Token(object):
yield self
def __repr__(self):
try:
return f"<{self.__class__.__name__} {render(self._mo._tree, 'txt')}>"
except AttributeError:
return f"<{self.__class__.__name__} {render(self._mo, 'txt')}>"
return f"<{self.__class__.__name__} {self.__txt__}>"
def __str__(self):
try:
return render(self._mo._tree)
except AttributeError:
return render(self._mo)
if self.RENDER == "tex":
return self.__tex__
elif self.RENDER == "txt":
return self.__txt__
else:
raise ValueError(f"Unknow render {self.RENDER}")
#return renders[self.RENDER](self._mo)
@property
def raw(self):
""" Get python's raw forme of the token """
return self._mo.content
def __txt__(self):
return self._mo.__txt__
@property
def __tex__(self):
return self._mo.__tex__
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 ...core import Tree
from . import factory
if not isinstance(other, Token):
try:
_other = factory(other)
except AttributeError:
_other = factory(Expression(moify(other)))
_other = factory(other)
else:
_other = other
if operation == '-':
tree = Tree("+", self._mo, Tree("-", None, _other._mo))
else:
tree = Tree(operation, self._mo, _other._mo)
tree = Tree(operation, self._mo, _other._mo)
return Expression(tree).simplify()
def __add__(self, other):
@@ -104,18 +109,6 @@ class Token(object):
... print(i)
3 + 7
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
>>> a = Fraction("4/3")
>>> b = Integer(7)
@@ -133,32 +126,6 @@ class Token(object):
"""
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):
""" Multiply 2 Tokens or a Token and a Expression
@@ -173,12 +140,6 @@ class Token(object):
... print(i)
3 * 7
21
>>> c = a * 7
>>> c
<Integer 21>
>>> c = a * "x"
>>> c
<Linear 3x>
>>> from .number import Fraction
>>> a = Fraction("4/3")
>>> b = Integer(7)
@@ -206,9 +167,6 @@ class Token(object):
>>> for i in c.explain():
... print(i)
3 / 7
>>> c = a / 7
>>> c
<Fraction 3 / 7>
>>> from .number import Fraction
>>> a = Fraction("4/3")
>>> b = Integer(7)
@@ -224,128 +182,6 @@ class Token(object):
"""
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'

View File

@@ -13,7 +13,7 @@ Make calculus as a student
Expression is the classe wich handle all calculus. It can randomly generate or import calculus, simplify them and explain them as a student would do.
>>> from mapytex.calculus import Expression
>>> render.set_render("txt")
>>> Expression.set_render("txt")
>>> e = Expression.from_str("2x + 6 - 3x")
>>> print(e)
2x + 6 - 3x
@@ -30,11 +30,7 @@ Expression is the classe wich handle all calculus. It can randomly generate or i
"""
from .API import Expression, Integer, Decimal, render, Polynomial, Fraction
from .core import random_list
from decimal import getcontext
#getcontext().prec = 2
from .API import Expression
__all__ = ["Expression"]

View File

@@ -26,7 +26,6 @@ def moify(token):
except MOError:
return token
@coroutine
def moify_cor(target):
""" Coroutine which try to convert a parsed token into an MO
@@ -37,7 +36,7 @@ def moify_cor(target):
>>> for i in [-2, "+", "x", "*", Decimal("3.3")]:
... list2molist.send(i)
>>> list2molist.throw(STOOOP)
[<MOnumber -2>, '+', <MOstr x>, '*', <MOnumber 3.3>]
[<MOnumber - 2>, '+', <MOstr x>, '*', <MOnumber 3.3>]
"""
try:
@@ -78,7 +77,7 @@ class MOnumber(Atom):
>>> MOnumber(23)
<MOnumber 23>
>>> MOnumber(-23)
<MOnumber -23>
<MOnumber - 23>
As expected there will be trouble with float
@@ -90,13 +89,7 @@ class MOnumber(Atom):
>>> MOnumber(Decimal("23.3"))
<MOnumber 23.3>
>>> MOnumber(Decimal("-23.3"))
<MOnumber -23.3>
Or directly passe a decimal string
>>> MOnumber("23.3")
<MOnumber 23.3>
>>> MOnumber("-23.3")
<MOnumber -23.3>
<MOnumber - 23.3>
MOnumber initialisation is idempotent
@@ -107,7 +100,7 @@ class MOnumber(Atom):
>>> MOnumber("a")
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
@@ -124,26 +117,15 @@ class MOnumber(Atom):
"""
if isinstance(value, Atom) and isinstance(value.value, (int, Decimal, float)):
Atom.__init__(self, value.value)
elif isinstance(value, (float, Decimal)):
if int(value) == value:
Atom.__init__(self, int(value))
else:
Atom.__init__(self, Decimal(value))
elif isinstance(value, int):
elif isinstance(value, (int, Decimal)):
Atom.__init__(self, value)
elif isinstance(value, float):
Atom.__init__(self, Decimal(value))
else:
try:
v = float(value)
except (ValueError, TypeError):
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))
raise MOError(
"The value of an MOnumber need to be a int, a float or a Decimal",
f"(got {type(value)})",
)
self._signature = "scalar"
@@ -172,7 +154,7 @@ class MOnumber(Atom):
>>> MOnumber(-3).__tex__
'- 3'
"""
if self.value >= 0:
if self.value > 0:
return str(self.value)
return f"- {abs(self.value)}"
@@ -184,16 +166,6 @@ class MOnumber(Atom):
except AttributeError:
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):
@@ -259,8 +231,7 @@ class MOstr(Atom):
f"An MOstr should be initiate with a single caracter string, got {val}"
)
if not val.isalpha():
raise MOError(
f"An MOstr should be initiate with a alpha string, got {val}")
raise MOError(f"An MOstr should be initiate with a alpha string, got {val}")
Atom.__init__(self, val)
@@ -289,16 +260,6 @@ class MOstr(Atom):
def degree(self):
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'

View File

@@ -8,9 +8,6 @@
from mapytex.calculus.core.tree import Tree
from .mo import Molecule, MO
from .atoms import MOnumber
from decimal import Decimal
from ..arithmetic import gcd
__all__ = ["MOFraction"]
@@ -34,10 +31,10 @@ class MOFraction(Molecule):
>>> f = MOFraction(2, 3)
>>> f
<MOFraction 2 / 3>
>>> print(f.tree)
/
> 2
> 3
>>> print(f.__txt__)
2 / 3
>>> print(f.__tex__)
\\frac{2}{3}
>>> print(f)
2 / 3
>>> f = MOFraction(2, 3, negative = True)
@@ -46,13 +43,11 @@ class MOFraction(Molecule):
"""
_numerator = MO.factory(numerator)
_denominator = MO.factory(denominator)
base_tree = Tree("/", _numerator, _denominator)
if negative:
tree = Tree("-", None, base_tree)
else:
tree = base_tree
Molecule.__init__(self, tree)
self._numerator = _numerator
@@ -72,61 +67,10 @@ class MOFraction(Molecule):
def denominator(self):
return self._denominator
@property
def _value(self):
return Decimal(self._numerator._value) / Decimal(self._denominator._value)
def inverse(self):
""" return the inverse fraction """
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
def simplified(self):
""" Simplified version of self
:examplex
>>> f = MOFraction(2, 3)
>>> f
<MOFraction 2 / 3>
>>> f.simplified()
<MOFraction 2 / 3>
>>> f = MOFraction(2, 6)
>>> f
<MOFraction 2 / 6>
>>> f.simplified()
<MOFraction 1 / 3>
>>> f = MOFraction(32, 24)
>>> f.simplified()
<MOFraction 4 / 3>
>>> f = MOFraction(32, 8)
>>> f.simplified()
<MOnumber 4>
"""
frac_gcd = gcd(self.numerator._value, self.denominator._value)
new_num = self.numerator._value / frac_gcd
new_denom = self.denominator._value / frac_gcd
if new_denom == 1:
return MOnumber(new_num)
return MOFraction(new_num, new_denom)
# -----------------------------
# Reglages pour 'vim'

View File

@@ -57,12 +57,20 @@ class MO(ABC):
pass
def __repr__(self):
return f"<{self.__class__.__name__} {self.__str__()}>"
return f"<{self.__class__.__name__} {self.__txt__}>"
@abstractmethod
def __str__(self):
pass
@abstractmethod
def __txt__(self):
pass
@abstractmethod
def __tex__(self):
pass
def __hash__(self):
try:
return self._tree.__hash__()
@@ -89,9 +97,6 @@ class MO(ABC):
"""
return self._signature
def differentiate(self):
raise NotImplementedError
class Atom(MO):
@@ -153,21 +158,21 @@ class Molecule(MO):
It is a wrapping of tree
Its wrapping tree can be access through .tree property
Its wrapping tree can be access throw .tree property
"""
MAINOP = None
def __init__(self, tree):
def __init__(self, value):
""" Initiate the MO
It should be idempotent.
"""
try:
self._tree = tree._tree
self._tree = value._tree
except AttributeError:
self._tree = tree
self._tree = value
self.is_scalar = True
self._signature = None
@@ -178,10 +183,18 @@ class Molecule(MO):
@property
def content(self):
return self.tree
return self._tree
def __str__(self):
return tree2txt(self.tree)
return str(self.__txt__)
@property
def __txt__(self):
return tree2txt(self._tree)
@property
def __tex__(self):
return tree2tex(self._tree)
# -----------------------------

View File

@@ -31,10 +31,10 @@ class MOstrPower(Molecule):
<MOstrPower x^2>
>>> print(s)
x^2
>>> print(s.tree)
^
> x
> 2
>>> print(s.__txt__)
x^2
>>> print(s.__tex__)
x^{2}
>>> MOstrPower(3, 1)
Traceback (most recent call last):
...
@@ -59,8 +59,7 @@ class MOstrPower(Molecule):
"""
_variable = MO.factory(variable)
if not isinstance(_variable, MOstr):
raise MOError(
"The variable of a monomial should be convertible into MOstr")
raise MOError("The variable of a monomial should be convertible into MOstr")
self._variable = _variable
_power = MO.factory(power)
@@ -103,7 +102,7 @@ class MOstrPower(Molecule):
@property
def signature(self):
""" Name of the mo in the API
:example:
>>> MOstrPower("x", 3).signature
'monome3'
@@ -112,27 +111,10 @@ class MOstrPower(Molecule):
"""
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):
""" Monomial math object : ax^n"""
""" Monomial math object"""
MAINOP = "*"
@@ -149,20 +131,22 @@ class MOMonomial(Molecule):
<MOMonomial 4x>
>>> print(m)
4x
>>> print(m.tree)
*
> 4
> x
>>> print(m.__txt__)
4x
>>> print(m.__tex__)
4x
>>> x = MOstrPower('x', 2)
>>> MOMonomial(4, x)
<MOMonomial 4x^2>
>>> m = MOMonomial(-1, 'x')
>>> m = MOMonomial(4, 'x')
>>> m
<MOMonomial - x>
>>> print(m.tree)
-
> None
> x
<MOMonomial 4x>
>>> print(m)
4x
>>> print(m.__txt__)
4x
>>> print(m.__tex__)
4x
>>> MOMonomial(4, 'x', 1)
<MOMonomial 4x>
>>> MOMonomial(4, 'x', 2)
@@ -170,13 +154,6 @@ class MOMonomial(Molecule):
>>> x2 = MOstrPower('x', 2)
>>> MOMonomial(4, x2, 3)
<MOMonomial 4x^6>
>>> m = MOMonomial(-1, 'x', 2)
>>> m
<MOMonomial - x^2>
>>> print(m.tree)
-
> None
> x^2
>>> MOMonomial(0, x)
Traceback (most recent call last):
...
@@ -206,21 +183,34 @@ class MOMonomial(Molecule):
self._power = _power
try:
if self.coefficient.value == 1:
_tree = self.strpower
if self._coefficient.value != 1:
_tree = Tree("*", self._coefficient, self.strpower)
else:
_tree = Tree("*", self.coefficient, self.strpower)
_tree = self.strpower
except AttributeError:
_tree = Tree("*", self.coefficient, self.strpower)
_tree = Tree("*", self._coefficient, self.strpower)
Molecule.__init__(self, _tree)
@property
def tree(self):
if self._coefficient == -1:
return Tree("-", None, self.strpower)
def __str__(self):
if self._coefficient != -1:
return super(MOMonomial, self).__str__()
else:
return "- " + self.strpower.__str__()
return Tree("*", self.coefficient, self.strpower)
@property
def __txt__(self):
if self._coefficient != -1:
return super(MOMonomial, self).__txt__
else:
return "- " + self.strpower.__txt__
@property
def __tex__(self):
if self._coefficient != -1:
return super(MOMonomial, self).__tex__
else:
return "- " + self.strpower.__tex__
@property
def coefficient(self):
@@ -259,7 +249,7 @@ class MOMonomial(Molecule):
@property
def signature(self):
""" Name of the mo in the API
:example:
>>> MOMonomial(2, "x").signature
'monome1'
@@ -268,31 +258,6 @@ class MOMonomial(Molecule):
"""
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'

View File

@@ -6,7 +6,6 @@
#
# Distributed under terms of the MIT license.
from collections import OrderedDict
from mapytex.calculus.core.tree import Tree
from . import MO, MOstr
from .mo import Molecule
@@ -18,7 +17,7 @@ __all__ = ["MOpolynomial"]
class MOpolynomial(Molecule):
""" MO polynomial: ax^n + ... + z (can't be a monomial)"""
""" MO polynomial"""
MAINOP = "+"
@@ -39,14 +38,6 @@ class MOpolynomial(Molecule):
<MOpolynomial 4x^3 + 1>
>>> MOpolynomial('x', {0: 1, 3: 1})
<MOpolynomial x^3 + 1>
>>> MOpolynomial('x', [0, 0, 3])
Traceback (most recent call last):
...
TypeError: A MOpolynomial can't be monomial it has to have more than one coefficient.
>>> MOpolynomial('x', {3: 1})
Traceback (most recent call last):
...
TypeError: A MOpolynomial can't be monomial it has to have more than one coefficient.
"""
_variable = MO.factory(variable)
@@ -66,12 +57,8 @@ class MOpolynomial(Molecule):
raise TypeError("Coefs needs to be a dictionnary or a list")
self._coefs = _coefs
if len(self._coefs) == 1:
raise TypeError("A MOpolynomial can't be monomial it has to have more than one coefficient.")
monomials = OrderedDict()
for deg in sorted(self._coefs.keys()):
coef = self._coefs[deg]
monomials = {}
for deg, coef in self._coefs.items():
if deg == 0:
monomials[deg] = coef
elif deg == 1 and coef == 1:
@@ -133,34 +120,12 @@ class MOpolynomial(Molecule):
:example:
>>> p = MOpolynomial('x', [1, 2, 3])
>>> 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()
odict_values([<MOnumber 1>, <MOMonomial 2x>, <MOMonomial 3x^2>])
dict_values([<MOnumber 1>, <MOMonomial 2x>, <MOMonomial 3x^2>])
"""
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'

View File

@@ -66,7 +66,6 @@ from .tree import Tree, AssocialTree
from .compute import compute
from .typing import typing, TypingError
from .renders import tree2txt, tree2tex
from .random import list_generator as random_list
# -----------------------------

View File

@@ -40,7 +40,7 @@ def compute(node, left_v, right_v):
>>> compute("+", MOnumber(1), MOnumber(2))
<MOnumber 3>
>>> compute("-", None, MOnumber(2))
<MOnumber -2>
<MOnumber - 2>
>>> compute("*", MOnumber(1), MOnumber(2))
<MOnumber 2>
>>> compute("~", MOnumber(1), MOnumber(2))
@@ -70,8 +70,7 @@ def compute_capacities(node):
op = OPERATIONS[node]
lines = [[node] + [mo.__name__ for mo in MOS]]
for left_mo in MOS:
lines.append([left_mo.__name__] +
[(left_mo, i) in op.funcs for i in MOS])
lines.append([left_mo.__name__] + [(left_mo, i) in op.funcs for i in MOS])
return lines

View File

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

View File

@@ -35,7 +35,7 @@ def monumber(_, right):
>>> a = MOnumber(4)
>>> minus(None, a)
<MOnumber -4>
<MOnumber - 4>
"""
return MO.factory(-right.value)

View File

@@ -47,8 +47,8 @@ Tree with RdLeaf replaced by generated values
>>> leafs
['a', 'a*k']
>>> rd_varia = extract_rv(leafs)
>>> sorted(list(rd_varia))
['a', 'k']
>>> rd_varia # doctest: +SKIP
{'a', 'k'}
>>> generated = random_generator(rd_varia, conditions=['a%2+1'])
>>> generated # doctest: +SKIP
{'a': 7, 'k': 4}
@@ -61,18 +61,8 @@ Tree with RdLeaf replaced by generated values
> 7
> 28
List generator
--------------
This function ignores tree structure and works with lists
>>> values = list_generator(["a", "a*b", "b", "c"], conditions=["b%c==1"])
>>> values # doctest: +SKIP
{'a': -8, 'a*b': -40, 'b': 5, 'c': 4}
"""
__all__ = ["list_generator"]
from random import choice
from functools import reduce
from .leaf import RdLeaf
@@ -254,35 +244,3 @@ def build_variable_scope(rd_variables, rejected, min_max, variables_scope):
except KeyError:
complete_scope[v]["min_max"] = min_max
return complete_scope
def list_generator(var_list, conditions=[], rejected=[0], min_max=(-10, 10), variables_scope={}, dictionnary=False):
""" Generate random computed values from the list
:param rd_variables: list of random variables to generate (can be computed value - "a*b")
:param conditions: condition over variables
:param rejected: Rejected values for the generator (default [0])
:param min_max: (min, max) limits in between variables will be generated
:param variables_scope: rejected and min_max define for individual variables
:param dictionnary: the return value will be a dictionnary with var_list as keys (default False)
:return: dictionnary of generated variables
:example:
>>> a, ab, b, c = list_generator(["a", "a*b", "b", "c"])
>>> a, ab, b, c # doctest: +SKIP
(5, -20, -4, -3)
>>> a * b == ab
True
>>> ab # doctest: +SKIP
-20
>>> a, b # doctest: +SKIP
5, -4
>>> list_generator(["a", "a*b", "b", "c"], dictionnary=True) # doctest: +SKIP
{'a': -3, 'a*b': 18, 'b': -6, 'c': -4}
"""
rv = extract_rv(var_list)
rv_gen = random_generator(rv, conditions, rejected, min_max, variables_scope)
generated = compute_leafs(var_list, rv_gen)
if dictionnary:
return generated
return [generated[v] for v in var_list]

View File

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

View File

@@ -91,11 +91,9 @@ def mul2tex(left, right):
'- 3x'
>>> mul2tex(a, a)
'x \\times x'
>>> mul2tex(a, MO.factory(-3))
'x(- 3)'
"""
left_ = render_with_parenthesis(left, "*")
right_ = render_with_parenthesis(right, "*", is_at_right=True)
right_ = render_with_parenthesis(right, "*")
display_time = True
# if (right_[0].isalpha() and (left_.isnumeric() or left_.isdecimal())) or right_[
@@ -119,17 +117,17 @@ def div2tex(left, right):
>>> from ..MO import MO
>>> div2tex(MO.factory(2), MO.factory(3))
'\\dfrac{2}{3}'
'\\frac{2}{3}'
>>> from ..tree import Tree
>>> t = Tree.from_str("1/2")
>>> div2tex(t, MO.factory(3))
'\\dfrac{\\dfrac{1}{2}}{3}'
'\\frac{\\frac{1}{2}}{3}'
>>> t = Tree.from_str("1+2")
>>> div2tex(t, MO.factory(3))
'\\dfrac{1 + 2}{3}'
'\\frac{1 + 2}{3}'
>>> t = Tree.from_str("1*2")
>>> div2tex(MO.factory(3), t)
'\\dfrac{3}{1 \\times 2}'
'\\frac{3}{1 \\times 2}'
"""
try:
left_ = tree2tex(left)
@@ -140,7 +138,7 @@ def div2tex(left, right):
except (AttributeError, ValueError):
right_ = right.__tex__
return "\\dfrac{" + left_ + "}{" + right_ + "}"
return "\\frac{" + left_ + "}{" + right_ + "}"
def pow2tex(left, right):
@@ -185,17 +183,20 @@ def pow2tex(left, right):
return f"{left_}^{{{right_}}}"
def render_with_parenthesis(subtree, operator, is_at_right=False):
def render_with_parenthesis(subtree, operator):
subtree_need_parenthesis = False
try:
subtree.node
except AttributeError:
try:
subtree_ = subtree.__tex__
except AttributeError:
subtree_ = str(subtree)
if subtree_.startswith("-") and OPERATORS["-"]["precedence"] < OPERATORS[operator]["precedence"] and is_at_right:
subtree_need_parenthesis = True
if (
OPERATORS[subtree.MAINOP]["precedence"]
< OPERATORS[operator]["precedence"]
):
subtree_need_parenthesis = True
except (AttributeError, KeyError):
pass
subtree_ = subtree.__tex__
else:
if OPERATORS[subtree.node]["precedence"] < OPERATORS[operator]["precedence"]:
subtree_need_parenthesis = True
@@ -206,8 +207,7 @@ def render_with_parenthesis(subtree, operator, is_at_right=False):
return subtree_
OPERATOR2TEX = {"+": plus2tex, "-": minus2tex,
"*": mul2tex, "/": div2tex, "^": pow2tex}
OPERATOR2TEX = {"+": plus2tex, "-": minus2tex, "*": mul2tex, "/": div2tex, "^": pow2tex}
def tree2tex(tree):
@@ -227,17 +227,8 @@ def tree2tex(tree):
from ..tree import Tree
if not isinstance(tree, Tree):
raise ValueError(
f"Can only render a Tree (got {type(tree).__name__}: {tree})")
def expand(leaf):
try:
return leaf.tree
except AttributeError:
return leaf
expanded_tree = tree.map_on_leaf(expand)
return OPERATOR2TEX[expanded_tree.node](expanded_tree.left_value, expanded_tree.right_value)
raise ValueError(f"Can only render a Tree (got {type(tree).__name__}: {tree})")
return OPERATOR2TEX[tree.node](tree.left_value, tree.right_value)
# -----------------------------

View File

@@ -91,13 +91,11 @@ def mul2txt(left, right):
'- 3x'
>>> mul2txt(a, a)
'x * x'
>>> mul2txt(a, MO.factory(-3))
'x(- 3)'
"""
display_time = True
left_ = render_with_parenthesis(left, "*")
right_ = render_with_parenthesis(right, "*", is_at_right=True)
right_ = render_with_parenthesis(right, "*")
if right_[0].isalpha():
# TODO: C'est bien beurk en dessous... |ven. déc. 21 12:03:07 CET 2018
@@ -189,22 +187,20 @@ def pow2txt(left, right):
return f"{left_}^{right_}"
def tree_with_parenthesis(subtree, operator):
""" Assuming the subtree is a tree, then have .node """
pass
def render_with_parenthesis(subtree, operator, is_at_right=False):
def render_with_parenthesis(subtree, operator):
subtree_need_parenthesis = False
try:
subtree.node
except AttributeError:
try:
subtree_ = subtree.__txt__
except AttributeError:
subtree_ = str(subtree)
if subtree_.startswith("-") and OPERATORS["-"]["precedence"] < OPERATORS[operator]["precedence"] and is_at_right:
subtree_need_parenthesis = True
if (
OPERATORS[subtree.MAINOP]["precedence"]
< OPERATORS[operator]["precedence"]
):
subtree_need_parenthesis = True
except (AttributeError, KeyError):
pass
subtree_ = subtree.__txt__
else:
if OPERATORS[subtree.node]["precedence"] < OPERATORS[operator]["precedence"]:
subtree_need_parenthesis = True
@@ -215,8 +211,7 @@ def render_with_parenthesis(subtree, operator, is_at_right=False):
return subtree_
OPERATOR2TXT = {"+": plus2txt, "-": minus2txt,
"*": mul2txt, "/": div2txt, "^": pow2txt}
OPERATOR2TXT = {"+": plus2txt, "-": minus2txt, "*": mul2txt, "/": div2txt, "^": pow2txt}
def tree2txt(tree):
@@ -236,17 +231,8 @@ def tree2txt(tree):
from ..tree import Tree
if not isinstance(tree, Tree):
raise ValueError(
f"Can only render a Tree (got {type(tree).__name__}: {tree})")
def expand(leaf):
try:
return leaf.tree
except AttributeError:
return leaf
expanded_tree = tree.map_on_leaf(expand)
return OPERATOR2TXT[expanded_tree.node](expanded_tree.left_value, expanded_tree.right_value)
raise ValueError(f"Can only render a Tree (got {type(tree).__name__}: {tree})")
return OPERATOR2TXT[tree.node](tree.left_value, tree.right_value)
# -----------------------------

View File

@@ -15,7 +15,7 @@ from decimal import Decimal, InvalidOperation
from .coroutine import *
from .operator import is_operator
from .MO import moify_cor
from .random.leaf import look_for_rdleaf, RdLeaf
from .random.leaf import look_for_rdleaf
__all__ = ["str2"]
@@ -271,7 +271,7 @@ def concurent_broadcast(target, lookfors=[]):
>>> a = searcher.throw(STOOOP)
>>> print(a)
['az', 'ABC', 'a', 'b', 'az', 'b']
>>> lfop = lookfor(something_in("+-*/()"), lambda x: f"op{x}")
>>> searcher = concurent_broadcast(list_sink, [lfop])
>>> for i in '12+3+234':
@@ -395,10 +395,8 @@ def missing_times(target):
elif not is_operator(tok) and tok != ")":
target_.send("*")
if (
isinstance(tok, int)
or (isinstance(tok, str) and not is_operator(tok) and not tok == "(")
or (isinstance(tok, RdLeaf))
if isinstance(tok, int) or (
isinstance(tok, str) and not is_operator(tok) and not tok == "("
):
previous = tok
@@ -484,8 +482,7 @@ def lookforNumbers(target):
if current.replace("-", "", 1).isdigit():
current += tok
else:
raise ParsingError(
f"Can't build decimal with '{current}'")
raise ParsingError(f"Can't build decimal with '{current}'")
elif tok == "-":
if current == "":
current = tok
@@ -803,8 +800,7 @@ def str2(sink, convert_to_mo=True):
operator_corout(missing_times(moify_cor(pparser(sink))))
)
else:
str2_corout = lookforNumbers(
operator_corout(missing_times(pparser(sink))))
str2_corout = lookforNumbers(operator_corout(missing_times(pparser(sink))))
for i in expression.replace(" ", ""):
str2_corout.send(i)
@@ -821,17 +817,39 @@ def rdstr2(sink):
:example:
>>> rdstr2list = rdstr2(list_sink)
>>> rdstr2list("{a}+{a*b}-2")
[<RdLeaf a>, '+', <RdLeaf a*b>, '+', <MOnumber -2>]
>>> rdstr2list("{a}({b}x+{c})")
[<RdLeaf a>, '*', [<RdLeaf b>, '*', <MOstr x>, '+', <RdLeaf c>]]
[<RdLeaf a>, '+', <RdLeaf a*b>, '+', <MOnumber - 2>]
"""
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)))))
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)
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(" ", ""):

View File

@@ -77,22 +77,13 @@ class Tree:
> *
| > 3
| > n
>>> t = Tree.from_str("2+{n}x", random=True)
>>> t = Tree.from_str("2+{n}*x", random=True)
>>> print(t)
+
> 2
> *
| > {n}
| > 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
| | > 3
| > 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:
str_2_mut_tree = rdstr2(cls.sink)

View File

@@ -11,11 +11,11 @@ Add MO with typing
"""
from multipledispatch import Dispatcher
from ..MO import MOnumber, MOstr
from ..tree import Tree
from ..MO import MO, MOnumber, MOstr
from ..MO.monomial import MOstrPower, MOMonomial
from ..MO.polynomial import MOpolynomial
from ..MO.fraction import MOFraction
from ..compute.filters import special_case
add_doc = """ Add MOs
@@ -27,26 +27,8 @@ add_doc = """ Add MOs
add = Dispatcher("add", doc=add_doc)
def add_filter(left, right):
""" Special cases for add MO (adding 0)
:param left: MO
:param right: MO
:returns: MO if it is a special case, nothing other wise
"""
try:
if left == 0:
return right
except TypeError:
pass
try:
if right == 0:
return left
except TypeError:
pass
@add.register((MOnumber, MOFraction), MOstr)
@special_case(add_filter)
def moscalar_mostr(left, right):
""" add a scalar with a letter to create a MOpolynomial
@@ -57,15 +39,11 @@ def moscalar_mostr(left, right):
>>> a = MOFraction(1, 5)
>>> add(a, b)
<MOpolynomial x + 1 / 5>
>>> a = MOnumber(0)
>>> add(a, b)
<MOstr x>
"""
return MOpolynomial(right, [left, 1])
@add.register(MOstr, (MOnumber, MOFraction))
@special_case(add_filter)
def mostr_moscalar(left, right):
""" add a scalar with a letter to create a MOpolynomial
@@ -76,15 +54,11 @@ def mostr_moscalar(left, right):
>>> b = MOFraction(1, 5)
>>> add(a, b)
<MOpolynomial x + 1 / 5>
>>> b = MOnumber(0)
>>> add(a, b)
<MOstr x>
"""
return MOpolynomial(left, [right, 1])
@add.register((MOnumber, MOFraction), MOstrPower)
@special_case(add_filter)
def moscalar_mostrpower(left, right):
""" add a scalar with a letter to create a MOpolynomial
@@ -100,7 +74,6 @@ def moscalar_mostrpower(left, right):
@add.register(MOstrPower, (MOnumber, MOFraction))
@special_case(add_filter)
def mostrpower_moscalar(left, right):
""" add a scalar with a letter to create a MOpolynomial
@@ -116,28 +89,21 @@ def mostrpower_moscalar(left, right):
@add.register((MOnumber, MOFraction), MOMonomial)
@special_case(add_filter)
def moscalar_momonomial(left, right):
""" add a scalar with a MOMonomial to create a MOpolynomial
>>> a = MOnumber(2)
>>> b = MOMonomial(3, 'x', 4)
>>> add(a, b)
<MOpolynomial 3x^4 + 2>
<MOpolynomial 2 + 3x^4>
>>> a = MOFraction(1, 5)
>>> add(a, b)
<MOpolynomial 3x^4 + 1 / 5>
>>> a = MOnumber(0)
>>> b = MOMonomial(2, 'x', 4)
>>> add(a, b)
<MOMonomial 2x^4>
<MOpolynomial 1 / 5 + 3x^4>
"""
return MOpolynomial(right.variable, {right.power: right.coefficient, 0: left})
@add.register(MOMonomial, (MOnumber, MOFraction))
@special_case(add_filter)
def momonial_moscalar(left, right):
""" add a scalar with a letter to create a MOpolynomial
@@ -149,27 +115,21 @@ def momonial_moscalar(left, right):
>>> add(a, b)
<MOpolynomial 3x^4 + 1 / 5>
>>> a = MOMonomial(2, 'x', 4)
>>> b = MOnumber(0)
>>> add(a, b)
<MOMonomial 2x^4>
"""
return MOpolynomial(left.variable, {0: right, left.power: left.coefficient})
@add.register((MOnumber, MOFraction), MOpolynomial)
@special_case(add_filter)
def moscalar_mopolynomial(left, right):
""" add a scalar with a MOpolynomial to create a MOpolynomial
>>> a = MOnumber(2)
>>> b = MOpolynomial('x', [0, 2, 3])
>>> add(a, b)
<MOpolynomial 3x^2 + 2x + 2>
<MOpolynomial 2 + 3x^2 + 2x>
>>> a = MOFraction(1, 5)
>>> add(a, b)
<MOpolynomial 3x^2 + 2x + 1 / 5>
<MOpolynomial 1 / 5 + 3x^2 + 2x>
"""
if 0 in right.coefficients.keys():
raise NotImplementedError(
@@ -183,7 +143,6 @@ def moscalar_mopolynomial(left, right):
@add.register(MOpolynomial, (MOnumber, MOFraction))
@special_case(add_filter)
def mopolynomial_moscalar(left, right):
""" add a scalar with a MOpolynomial to create a MOpolynomial
@@ -205,7 +164,6 @@ def mopolynomial_moscalar(left, right):
@add.register(MOstr, MOstr)
@special_case(add_filter)
def mostr_mostr(left, right):
""" add 2 mostr
@@ -220,7 +178,6 @@ def mostr_mostr(left, right):
@add.register(MOstr, MOstrPower)
@special_case(add_filter)
def mostr_mostrpower(left, right):
""" add a scalar with a letter to create a MOpolynomial
@@ -238,7 +195,6 @@ def mostr_mostrpower(left, right):
@add.register(MOstrPower, MOstr)
@special_case(add_filter)
def mostrpower_mostr(left, right):
""" add a scalar with a letter to create a MOpolynomial
@@ -256,7 +212,6 @@ def mostrpower_mostr(left, right):
@add.register(MOstrPower, MOstrPower)
@special_case(add_filter)
def mostrpower_mostrpower(left, right):
""" add 2 mostrpower
@@ -276,14 +231,13 @@ def mostrpower_mostrpower(left, right):
@add.register(MOstr, MOpolynomial)
@special_case(add_filter)
def mostr_mopolynomial(left, right):
""" add a str with a MOpolynomial to create a MOpolynomial
>>> a = MOstr("x")
>>> b = MOpolynomial('x', [1, 0, 3])
>>> add(a, b)
<MOpolynomial 3x^2 + x + 1>
<MOpolynomial x + 3x^2 + 1>
"""
if 1 in right.coefficients.keys():
raise NotImplementedError("Polynomial with no constant, calculus to do")
@@ -295,14 +249,13 @@ def mostr_mopolynomial(left, right):
@add.register(MOpolynomial, MOstr)
@special_case(add_filter)
def mopolynomial_mostr(left, right):
""" add a str with a MOpolynomial to create a MOpolynomial
>>> a = MOpolynomial('x', [1, 0, 3])
>>> b = MOstr("x")
>>> add(a, b)
<MOpolynomial 3x^2 + x + 1>
<MOpolynomial 3x^2 + 1 + x>
"""
if 1 in left.coefficients.keys():
raise NotImplementedError("Polynomial with no constant, calculus to do")
@@ -313,14 +266,13 @@ def mopolynomial_mostr(left, right):
@add.register(MOstrPower, MOpolynomial)
@special_case(add_filter)
def mostrpower_mopolynomial(left, right):
""" add a strPower with a MOpolynomial to create a MOpolynomial
>>> a = MOstrPower("x", 2)
>>> b = MOpolynomial('x', [1, 2, 0, 4])
>>> add(a, b)
<MOpolynomial 4x^3 + x^2 + 2x + 1>
<MOpolynomial x^2 + 4x^3 + 2x + 1>
"""
if left.power in right.coefficients.keys():
raise NotImplementedError("Degree in common, need to compute")
@@ -332,14 +284,13 @@ def mostrpower_mopolynomial(left, right):
@add.register(MOpolynomial, MOstrPower)
@special_case(add_filter)
def mopolynomial_mostrpower(left, right):
""" add a strPower with a MOpolynomial to create a MOpolynomial
>>> a = MOpolynomial('x', [1, 2, 0, 4])
>>> b = MOstrPower("x", 2)
>>> add(a, b)
<MOpolynomial 4x^3 + x^2 + 2x + 1>
<MOpolynomial 4x^3 + 2x + 1 + x^2>
"""
if right.power in left.coefficients.keys():
raise NotImplementedError("Degree in common, need to compute")
@@ -350,14 +301,13 @@ def mopolynomial_mostrpower(left, right):
@add.register(MOMonomial, MOpolynomial)
@special_case(add_filter)
def momonomial_mopolynomial(left, right):
""" add a Monomial with a MOpolynomial to create a MOpolynomial
>>> a = MOMonomial(3, "x", 2)
>>> b = MOpolynomial('x', [1, 2, 0, 4])
>>> add(a, b)
<MOpolynomial 4x^3 + 3x^2 + 2x + 1>
<MOpolynomial 3x^2 + 4x^3 + 2x + 1>
"""
if left.power in right.coefficients.keys():
raise NotImplementedError("Degree in common, need to compute")
@@ -369,14 +319,13 @@ def momonomial_mopolynomial(left, right):
@add.register(MOpolynomial, MOMonomial)
@special_case(add_filter)
def mopolynomial_momonomial(left, right):
""" add a Monomial with a MOpolynomial to create a MOpolynomial
>>> a = MOpolynomial('x', [1, 2, 0, 4])
>>> b = MOMonomial(3, "x", 2)
>>> add(a, b)
<MOpolynomial 4x^3 + 3x^2 + 2x + 1>
<MOpolynomial 4x^3 + 2x + 1 + 3x^2>
"""
if right.power in left.coefficients.keys():
raise NotImplementedError("Degree in common, need to compute")
@@ -387,16 +336,15 @@ def mopolynomial_momonomial(left, right):
@add.register(MOpolynomial, MOpolynomial)
@special_case(add_filter)
def mopolynomial_mopolynomial(left, right):
""" add a polynomial with a MOpolynomial to create a MOpolynomial
>>> a = MOpolynomial('x', [1, 0, 3])
>>> b = MOpolynomial('x', [0, 2, 0, 4])
>>> add(a, b)
<MOpolynomial 4x^3 + 3x^2 + 2x + 1>
<MOpolynomial 3x^2 + 1 + 4x^3 + 2x>
>>> 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())
if common_degree:
@@ -407,14 +355,13 @@ def mopolynomial_mopolynomial(left, right):
@add.register(MOstr, MOMonomial)
@special_case(add_filter)
def mostr_monomial(left, right):
""" add a mostr with a MOMonomial to create a MOpolynomial
>>> a = MOstr('x')
>>> b = MOMonomial(3, 'x', 4)
>>> add(a, b)
<MOpolynomial 3x^4 + x>
<MOpolynomial x + 3x^4>
"""
if right.power == 1:
raise NotImplementedError("Monomial is deg 1, need to compute")
@@ -423,7 +370,6 @@ def mostr_monomial(left, right):
@add.register(MOMonomial, MOstr)
@special_case(add_filter)
def monomial_mostr(left, right):
""" add a mostr with a MOMonomial to create a MOpolynomial
@@ -439,14 +385,13 @@ def monomial_mostr(left, right):
@add.register(MOstrPower, MOMonomial)
@special_case(add_filter)
def mostrpower_monomial(left, right):
""" add a mostrPower with a MOMonomial to create a MOpolynomial
>>> a = MOstrPower('x', 2)
>>> b = MOMonomial(3, 'x', 4)
>>> add(a, b)
<MOpolynomial 3x^4 + x^2>
<MOpolynomial x^2 + 3x^4>
"""
if left.power == right.power:
raise NotImplementedError(
@@ -457,7 +402,6 @@ def mostrpower_monomial(left, right):
@add.register(MOMonomial, MOstrPower)
@special_case(add_filter)
def monomial_mostrpower(left, right):
""" add a mostrPower with a MOMonomial to create a MOpolynomial
@@ -475,7 +419,6 @@ def monomial_mostrpower(left, right):
@add.register(MOMonomial, MOMonomial)
@special_case(add_filter)
def monomial_momonomial(left, right):
""" add a moMonomial with a MOMonomial to create a MOpolynomial

View File

@@ -15,7 +15,6 @@ from ..tree import Tree
from ..MO import MO, MOnumber, MOstr
from ..MO.fraction import MOFraction
from ..MO.monomial import MOstrPower, MOMonomial
from ..compute.filters import special_case
multiply_doc = """ Multiply MOs
@@ -28,38 +27,7 @@ multiply_doc = """ Multiply MOs
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)
@special_case(multiply_filter)
def moscalar_mostr(left, right):
""" Multiply a scalar with a letter to create a MOMonomial
@@ -75,7 +43,6 @@ def moscalar_mostr(left, right):
@multiply.register(MOstr, (MOnumber, MOFraction))
@special_case(multiply_filter)
def mostr_moscalar(left, right):
""" Multiply a scalar with a letter to create a MOMonomial
@@ -91,7 +58,6 @@ def mostr_moscalar(left, right):
@multiply.register((MOnumber, MOFraction), MOstrPower)
@special_case(multiply_filter)
def moscalar_mostrpower(left, right):
""" Multiply a scalar with a MOstrPower
@@ -99,18 +65,12 @@ def moscalar_mostrpower(left, right):
>>> x = MOstrPower('x', 4)
>>> multiply(a, x)
<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)
@multiply.register(MOstrPower, (MOnumber, MOFraction))
@special_case(multiply_filter)
def mostrpower_moscalar(left, right):
""" Multiply a MOstrPower with a scalar

View File

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

View File

@@ -5,7 +5,7 @@
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
: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:
u, v = randint(v_min, v_max), randint(v_min, v_max)
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]
return formated_triplet
# -----------------------------
# Reglages pour 'vim'
# 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 -*-
#
@@ -32,17 +32,11 @@ class Dataset(list):
"""
@classmethod
def random(
cls,
length,
data_name="Valeurs",
distrib="gauss",
rd_args=(0, 1),
nbr_format=lambda x: round(x, 2),
v_min=None,
v_max=None,
exact_mean=None,
):
def random(cls, length, data_name="Valeurs",
distrib="gauss", 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
:param length: length of the dataset
@@ -53,9 +47,11 @@ class Dataset(list):
: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"
"""
data = random_generator(
length, distrib, rd_args, nbr_format, v_min, v_max, exact_mean
)
data = random_generator(length,
distrib, rd_args,
nbr_format,
v_min, v_max,
exact_mean)
return cls(data, data_name=data_name)
@@ -98,7 +94,7 @@ class Dataset(list):
def deviation(self):
""" Compute the deviation (not normalized) """
mean = self.mean()
return sum([(x - mean) ** 2 for x in self])
return sum([(x - mean)**2 for x in self])
@number_factory
def variance(self):
@@ -124,8 +120,7 @@ class Dataset(list):
self.quartile(1),
self.quartile(2),
self.quartile(3),
max(self),
)
max(self))
@number_factory
def quartile(self, quartile=1):
@@ -178,21 +173,18 @@ class Dataset(list):
""" Latex code to display dataset as a tabular """
d_per_line = self.effectif_total() // nbr_lines
d_last_line = self.effectif_total() % d_per_line
splited_data = [
self[x : x + d_per_line]
for x in range(0, self.effectif_total(), d_per_line)
]
splited_data = [self[x:x + 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
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
latex = "\\begin{{tabular}}{{|c|*{{{nbr_col}}}{{c|}}}} \n".format(
nbr_col=d_per_line
)
nbr_col=d_per_line)
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"

View File

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

View File

@@ -1,18 +1,14 @@
# /usr/bin/env python
#/usr/bin/env python
# -*- coding:Utf-8 -*-
from random import randint, uniform, gauss, choice
def random_generator(
length,
distrib=gauss,
rd_args=(0, 1),
nbr_format=lambda x: round(x, 2),
v_min=None,
v_max=None,
exact_mean=None,
):
def random_generator(length,
distrib=gauss, 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
:param length: length of the dataset
@@ -23,18 +19,12 @@ def random_generator(
: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"
>>> random_generator(10) # doctest: +SKIP
[-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)) # doctest: +SKIP
[9.01, 5.32, 5.59, 8.8, 7.36, 6.9, 6.05, 7.44, 9.47, 6.95]
>>> random_generator(10, distrib = "uniform", rd_args = (5, 10)) # doctest: +SKIP
[7.85, 9.01, 5.32, 5.59, 8.8, 7.36, 6.9, 6.05, 7.44, 9.47]
>>> 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]
>>> random_generator(10)
>>> random_generator(10, distrib = uniform, rd_args = (5, 10))
>>> random_generator(10, distrib = "uniform", rd_args = (5, 10))
>>> random_generator(10, v_min = 0)
>>> random_generator(10, exact_mean = 0)
>>> random_generator(10, distrib = gauss, rd_args = (50,20), nbr_format = int)
"""
# if exact_mean is set, we create automaticaly only length-1 value
@@ -57,8 +47,7 @@ def random_generator(
"gauss": gauss,
"uniform": uniform,
"randint": randint,
"choice": choice,
}
"choice": choice}
try:
distrib(*rd_args)
except TypeError:
@@ -78,13 +67,11 @@ def random_generator(
last_v = nbr_format((length + 1) * exact_mean - sum(data))
if not validate(last_v):
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)
return data
# -----------------------------
# Reglages pour 'vim'
# 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 -*-
"""
@@ -12,11 +12,9 @@ from .dataset import Dataset
from itertools import chain
from .number_tools import number_factory
def flatten_list(l):
return list(chain(*l))
class WeightedDataset(dict):
""" A weighted dataset with statistics and latex rendering methods
@@ -39,8 +37,11 @@ class WeightedDataset(dict):
"""
def __init__(
self, datas=[], data_name="Valeurs", weights=[], weight_name="Effectifs"
):
self,
datas=[],
data_name="Valeurs",
weights=[],
weight_name="Effectifs"):
"""
Initiate the WeightedDataset
"""
@@ -83,7 +84,7 @@ class WeightedDataset(dict):
def deviation(self):
""" Compute the deviation (not normalized) """
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
def variance(self):
@@ -108,13 +109,11 @@ class WeightedDataset(dict):
(1, 3, 4, 5, 5)
"""
return (
min(self.keys()),
self.quartile(1),
self.quartile(2),
self.quartile(3),
max(self.keys()),
)
return (min(self.keys()),
self.quartile(1),
self.quartile(2),
self.quartile(3),
max(self.keys()))
@number_factory
def quartile(self, quartile=1):
@@ -147,9 +146,8 @@ class WeightedDataset(dict):
position = self.posi_quartile(quartile) - 1
expanded_values = flatten_list([v * [k] for (k, v) in self.items()])
if position.is_integer():
return (
expanded_values[int(position)] + expanded_values[int(position) + 1]
) / 2
return (expanded_values[int(position)] +
expanded_values[int(position) + 1]) / 2
else:
return expanded_values[ceil(position)]
@@ -169,8 +167,7 @@ class WeightedDataset(dict):
def tabular_latex(self):
""" Latex code to display dataset as a tabular """
latex = "\\begin{{tabular}}{{|c|*{{{nbr_col}}}{{c|}}}} \n".format(
nbr_col=len(self.keys())
)
nbr_col=len(self.keys()))
latex += "\t \hline \n"
data_line = "\t {data_name} ".format(data_name=self.data_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

@@ -21,6 +21,6 @@ pyparsing==2.3.0
pytest==3.10.1
simplegeneric==0.8.1
six==1.11.0
tabulate==0.8.7
tabulate==0.8.2
traitlets==4.3.2
wcwidth==0.1.7

View File

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

View File

View File

@@ -1,31 +0,0 @@
import mapytex
def test_default_render():
assert mapytex.render.render_name == "txt"
def test_default_rending():
e = mapytex.Expression("2*3")
assert str(e) == "2*3"
def test_changing_render():
assert mapytex.render.render_name == "txt"
mapytex.render.set_render("tex")
assert mapytex.render.render_name == "tex"
mapytex.render.set_render("txt")
assert mapytex.render.render_name == "txt"
def test_changing_rending():
e = mapytex.Expression.from_str("2*3")
f = mapytex.Fraction("2/3")
assert str(e) == "2 * 3"
assert str(f) == "2 / 3"
mapytex.render.set_render("tex")
assert str(e) == "2 \\times 3"
assert str(f) == "\\dfrac{2}{3}"
mapytex.render.set_render("txt")
assert str(e) == "2 * 3"
assert str(f) == "2 / 3"