60 Commits

Author SHA1 Message Date
78ce8f767a Fix: fraction rendering
All checks were successful
continuous-integration/drone/push Build is passing
2021-09-25 18:05:05 +02:00
cbcead48f7 Feat: Init polynomial with coefficients
All checks were successful
continuous-integration/drone/push Build is passing
2021-02-04 10:18:12 +01:00
ff4d8471ef Feat: dirty way to manage () and * in render 2020-12-15 16:01:54 +01:00
460255b151 Fix: Rendering and changing it works 2020-12-15 15:37:27 +01:00
95fd12c430 Feat: test num and denom for fraction 2020-12-15 15:02:39 +01:00
a1608a20d1 Feat: add test on what doesn't work! 2020-12-15 14:36:39 +01:00
48088762be Feat: update tabulate version
Some checks failed
continuous-integration/drone/push Build is failing
2020-12-12 23:25:44 +01:00
2109fc46cb Fix: simplify rendering by expanding tree. still bugs with set_render 2020-12-12 23:14:44 +01:00
1a20e6927d Feat: remove __txt__ and __tex__
All checks were successful
continuous-integration/drone/push Build is passing
2020-12-12 22:10:53 +01:00
7649928be8 Fix: clean __str__ method for token 2020-12-12 22:10:53 +01:00
bb43611f67 Test: separate expression build and showing it
All checks were successful
continuous-integration/drone/push Build is passing
2020-11-15 21:12:26 +01:00
9dc4d08619 Feat: return value for random_list 2020-11-15 21:12:26 +01:00
33c4872ff7 Fix: rename list_generator to random_list 2020-11-15 21:12:26 +01:00
76dad93f03 Tagging: bad version number! 2020-11-15 21:12:26 +01:00
6f7fd416be Tagging: change version to 2.2 2020-11-15 21:12:26 +01:00
8d9294afe1 Feat: random list generator 2020-11-15 21:12:26 +01:00
700665c0a5 Feat: add autopublishing 2020-11-15 21:12:26 +01:00
7e4bfc9ba0 Feat: doctest skip
All checks were successful
continuous-integration/drone/push Build is passing
2020-08-20 16:57:17 +02:00
cdfd3e47b5 Mise à jour de 'README.md' 2020-08-20 16:57:17 +02:00
364f16b872 Mise à jour de '.drone.yml' 2020-08-20 16:57:17 +02:00
cc94f1f50c Feat: nothing 2020-08-20 16:57:17 +02:00
ad6923f8cd Feat: add a drone for testing 2020-08-20 16:57:17 +02:00
fadd93b9a6 Feat: change frac to dfrac for fractions 2019-11-04 09:43:52 +01:00
cb8010c7ac Feat: force subtree to be str and tex instead of txt in tree2tex 2019-11-04 09:39:52 +01:00
f12ec38746 Feat: Polynomial are displayed in nicer order! 2019-10-30 21:12:58 +01:00
f8b24472d1 Fix: clean __init__ 2019-10-30 20:59:09 +01:00
240857a7ad Fix: chante value to content in raw 2019-10-30 20:58:46 +01:00
70ec6933ac Feat: missing_times works with RdLeaf 2019-10-30 18:46:57 +01:00
91779bd945 Feat: Add doctest to rdstr2 2019-10-30 10:49:07 +01:00
69e1e43966 Feat: raw methods for tokens 2019-10-16 22:45:41 +02:00
16a6c4953d Feat: remove precision setting for Decimal 2019-10-15 19:41:09 +02:00
84c71f6b5d Feat: overload pow for tokens 2019-10-15 19:32:19 +02:00
2a74722b19 Feat: add doctest add int and Decimal 2019-10-15 19:15:32 +02:00
43fd46379c Feat: dirty way to get decimal approx of a function 2019-10-14 22:26:51 +02:00
2d296cf1d2 Feat: allowing to import Decimal 2019-10-14 22:22:08 +02:00
12109156d1 Feat: allow import Integer in calculus 2019-10-13 22:38:49 +02:00
2fad004d6d Feat: dirty sub (repeatition in explain) 2019-10-13 22:38:49 +02:00
207dead5d0 Feat: doctest for operation between Token and str 2019-10-13 22:38:49 +02:00
e24bff23db Feat: tokens can operate with builtin int 2019-10-13 22:38:49 +02:00
69c2b3718d Fix: precessing -> processing 2019-10-13 22:38:49 +02:00
9c25142a4b Feat: start using nox 2019-10-13 22:38:49 +02:00
931e55c35e Fix: Black does its job 2019-10-13 22:38:49 +02:00
aba43c9f19 Feat: no more round in quadratic roots 2019-07-17 09:55:32 +02:00
b93f34bc38 Feat: MOnumber creating with a integer string 2019-07-17 09:53:03 +02:00
204fdf755b Feat: to_be_token decorator and force token to be Integer when it's
possible
2019-07-17 09:49:50 +02:00
d75fd4c4cf Feat: tokenify for everything! 2019-07-17 09:25:12 +02:00
a32b684b6b Feat: Test and validate roots (but not elegant) 2019-07-16 16:49:33 +02:00
a5f062a151 Feat: doctest for composing Expressions 2019-07-16 09:31:30 +02:00
f8e1c7f962 Feat: Expression call works with tokens 2019-07-16 09:30:30 +02:00
1685c73051 Feat: Allow pure string rendering 2019-07-16 09:09:39 +02:00
04ac74d17a Fix: move __call__ to expressions 2019-07-15 18:37:16 +02:00
23ab39f7d4 Feat: add tree2tex in all import 2019-07-15 18:36:59 +02:00
41385ec561 Feat: Polynoms can be differentiate!!!! 2019-07-15 17:48:59 +02:00
8389a319f0 Fix: Issue with typing null monomial 2019-07-15 17:27:38 +02:00
6e24756746 Fix: replace notimplemented by NotImplementedError 2019-07-15 17:16:00 +02:00
d45ab560c9 Feat: coefficients, delta and some roots for polynomial, Linear and Quadratic 2019-07-15 11:59:28 +02:00
fa8beb6bb2 Feat: Polynomial can be evaluated 2019-07-15 11:14:48 +02:00
b81281d78a Feat: Move some import into functions 2019-07-15 11:09:59 +02:00
071b8caf1d Feat: start working on eval for polynoms 2019-07-15 10:42:49 +02:00
1ed0b9715a Fix: typing filter for multiply 2019-07-15 10:42:13 +02:00
22 changed files with 293 additions and 244 deletions

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
# encoding: utf-8 # encoding: utf-8
from .calculus import Expression, Integer, Decimal, random_list from .calculus import Expression, Integer, Decimal, random_list, render, Polynomial, Fraction
# Expression.set_render('tex') # Expression.set_render('tex')

View File

@@ -108,26 +108,35 @@ x^7
(6 + 6) * x + 4x^2 + 9 (6 + 6) * x + 4x^2 + 9
4x^2 + 12x + 9 4x^2 + 12x + 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 .expression import Expression
from .tokens.number import Integer, Decimal from .tokens import Token
from .tokens.polynomial import Polynomial
from .tokens.number import Integer, Decimal, Fraction
if __name__ == "__main__": if __name__ == "__main__":
e = Expression.from_str("1+2/3/4/5") e = Expression.from_str("(2x-3)(-x+2)")
et = e._typing() e_simplified = e.simplify()
print("typing") e_simplified
print(e._tree) for s in e_simplified.explain():
e = et._order() print(s._tree.map_on_leaf(lambda x: type(x)))
print("order") print(s)
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' # Reglages pour 'vim'

View File

@@ -21,7 +21,7 @@ from ..core.random import (
) )
from ..core.MO import moify from ..core.MO import moify
from .tokens import factory from .tokens import factory
from .renders import renders from .renders import render
class Expression(object): class Expression(object):
@@ -42,7 +42,6 @@ class Expression(object):
14 14
""" """
RENDER = "txt"
def __init__(self, tree, ancestor=None): def __init__(self, tree, ancestor=None):
""" """
@@ -50,41 +49,6 @@ class Expression(object):
self._tree = tree self._tree = tree
self._ancestor = ancestor 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 + \\dfrac{3}{4}
>>> es = e.simplify()
>>> print(es)
\\dfrac{11}{4}
>>> Expression.set_render('txt')
"""
from .tokens.token import Token
Token.set_render(render)
cls.RENDER = render
@classmethod @classmethod
def from_str(cls, string, typing=True): def from_str(cls, string, typing=True):
""" Initiate the expression from a string """ Initiate the expression from a string
@@ -136,9 +100,11 @@ class Expression(object):
:returns: TODO :returns: TODO
:example: :example:
>>> Expression.random("{a}/{a*k}") # doctest: +SKIP >>> e = Expression.random("{a}/{a*k}")
>>> e # doctest: +SKIP
<Exp: -3 / -15> <Exp: -3 / -15>
>>> Expression.random("{a}/{a*k} - 3*{b}", variables_scope={'a':{'min_max':(10, 30)}}) # doctest: +SKIP >>> e = Expression.random("{a}/{a*k} - 3*{b}", variables_scope={'a':{'min_max':(10, 30)}})
>>> e # doctest: +SKIP
<Exp: 18 / 108 - 3 * 9> <Exp: 18 / 108 - 3 * 9>
>>> e = Expression.random("{a}*x + {b}*x + 3", ["a>b"], rejected=[0, 1]) >>> e = Expression.random("{a}*x + {b}*x + 3", ["a>b"], rejected=[0, 1])
>>> ee = e.simplify() >>> ee = e.simplify()
@@ -172,16 +138,16 @@ class Expression(object):
return cls(t) return cls(t)
def __str__(self): def __str__(self):
return renders[self.RENDER](self._tree) return render(self._tree)
def __repr__(self): def __repr__(self):
return f"<Exp: {renders['txt'](self._tree)}>" return f"<Exp: {render(self._tree, 'txt')}>"
def _order(self, exclude_nodes=["*", "/", "**"]): def _order(self, exclude_nodes=["*", "/", "**"]):
""" Order the expression base on types """ Order the expression base on types
:example: :example:
>>> e = Expression.from_str("1 + 2x + 3 + 4x") >>> e = Expression.from_str("1 + 2x + 3 + 4x")
>>> print(e) >>> print(e)
1 + 2x + 3 + 4x 1 + 2x + 3 + 4x
@@ -202,7 +168,8 @@ class Expression(object):
return type(leaf) return type(leaf)
else: else:
try: 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 return typed_leaf.signature
except (AttributeError, NotImplementedError, TypingError): except (AttributeError, NotImplementedError, TypingError):
return type(leaf) return type(leaf)
@@ -219,7 +186,7 @@ class Expression(object):
def _optimize(self, exclude_nodes=["/", "**"]): def _optimize(self, exclude_nodes=["/", "**"]):
""" Return a copy of self with an optimize tree """ Return a copy of self with an optimize tree
:example: :example:
>>> e = Expression.from_str("2x^2+2x+3x") >>> e = Expression.from_str("2x^2+2x+3x")
>>> print(e._tree) >>> print(e._tree)
@@ -260,7 +227,7 @@ class Expression(object):
def _typing(self): def _typing(self):
""" Build a copy of self with as much typing as possible """ Build a copy of self with as much typing as possible
:example: :example:
>>> e = Expression.from_str("2x", typing=False) >>> e = Expression.from_str("2x", typing=False)
>>> print(e._tree.map_on_leaf(lambda x: type(x).__name__)) >>> print(e._tree.map_on_leaf(lambda x: type(x).__name__))
@@ -305,7 +272,7 @@ class Expression(object):
def _compute(self): def _compute(self):
"""" Compute one step of self """" Compute one step of self
""" """
try: try:
return Expression(self._tree.apply_on_last_level(compute)) return Expression(self._tree.apply_on_last_level(compute))
@@ -370,7 +337,7 @@ class Expression(object):
def explain(self): def explain(self):
""" Yield every calculus step which have lead to self """ Yield every calculus step which have lead to self
:example: :example:
>>> e = Expression.from_str("2+3*4") >>> e = Expression.from_str("2+3*4")
>>> f = e.simplify() >>> f = e.simplify()

View File

@@ -12,34 +12,59 @@ Expression
""" """
from ..core import tree2txt, tree2tex from ..core import tree2txt, tree2tex
class Render(object):
""" Object which render Expression or token """
def _txt(mo_tree): def __init__(self, default="txt"):
""" txt render for MOs or Trees""" self._default = default
try: self._render = default
return tree2txt(mo_tree) self.renders = {}
except ValueError:
pass
try: def register_render(self, name, func, attribute):
return mo_tree.__txt__ """ register a render """
except AttributeError: try:
return str(mo_tree) 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")
def _tex(mo_tree):
""" Tex render for MOs or Trees"""
try:
return tree2tex(mo_tree)
except ValueError:
pass
try: render = Render()
return mo_tree.__tex__ render.register_render("txt", tree2txt, "__txt__")
except AttributeError: render.register_render("tex", tree2tex, "__tex__")
return str(mo_tree)
renders = {"txt": _txt, "tex": _tex}
# ----------------------------- # -----------------------------
# Reglages pour 'vim' # Reglages pour 'vim'

View File

@@ -18,7 +18,7 @@ from ...core.MO import MO, MOnumber
from ...core.MO.fraction import MOFraction from ...core.MO.fraction import MOFraction
from random import random from random import random
__all__ = ["Integer", "Decimal"] __all__ = ["Integer", "Decimal", "Fraction"]
class Integer(Token): class Integer(Token):
@@ -141,7 +141,7 @@ class Decimal(Token):
class Fraction(Token): class Fraction(Token):
""" Token representing a fraction """ Token representing a fraction of numbers
:example: :example:
>>> Fraction("3/4") >>> Fraction("3/4")
@@ -237,11 +237,25 @@ class Fraction(Token):
@property @property
def numerator(self): def numerator(self):
return self._mo.numerator """ Get numerator of the fraction
:example:
>>> a = Fraction("3/4")
>>> a.numerator
<Integer 3>
"""
return Integer(self._mo.numerator)
@property @property
def denominator(self): def denominator(self):
return self._mo.denominator """ Get denominator of the fraction
:example:
>>> a = Fraction("3/4")
>>> a.denominator
<Integer 4>
"""
return Integer(self._mo.denominator)
@property @property
def decimal(self): def decimal(self):

View File

@@ -15,6 +15,7 @@ from .token import Token
from . import to_be_token from . import to_be_token
from ...core.MO import MO from ...core.MO import MO
from ...core.MO.atoms import moify from ...core.MO.atoms import moify
from ...core.MO.polynomial import MOpolynomial
__all__ = ["Polynomial", "Quadratic", "Linear"] __all__ = ["Polynomial", "Quadratic", "Linear"]
@@ -44,9 +45,21 @@ class Polynomial(Token):
return cls(mo, name, ancestor) return cls(mo, name, ancestor)
@classmethod @classmethod
def from_coefficients(cls, coefficients): def from_coefficients(cls, coefficients, variable_name="x", name=""):
""" Initiate polynomial from list of coefficients """ """ Initiate polynomial from list of coefficients
pass
: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 @classmethod
def random(cls): def random(cls):

View File

@@ -10,7 +10,7 @@
Tokens: practical envelop of math object Tokens: practical envelop of math object
""" """
from ..renders import renders from ..renders import render
from ...core.MO.atoms import moify from ...core.MO.atoms import moify
@@ -18,8 +18,6 @@ class Token(object):
""" Token: practical envelop of an math object """ """ Token: practical envelop of an math object """
RENDER = "txt"
def __init__(self, mo, name="", ancestor=None): def __init__(self, mo, name="", ancestor=None):
self._mo = mo self._mo = mo
self.name = name self.name = name
@@ -34,17 +32,9 @@ class Token(object):
): ):
raise NotImplemented 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): def explain(self):
""" Yield every calculus step which have lead to self """ Yield every calculus step which have lead to self
:example: :example:
>>> from mapytex.calculus.API import Expression >>> from mapytex.calculus.API import Expression
>>> e = Expression.from_str("2+3*4") >>> e = Expression.from_str("2+3*4")
@@ -64,25 +54,16 @@ class Token(object):
yield self yield self
def __repr__(self): def __repr__(self):
return f"<{self.__class__.__name__} {self.__txt__}>" try:
return f"<{self.__class__.__name__} {render(self._mo._tree, 'txt')}>"
except AttributeError:
return f"<{self.__class__.__name__} {render(self._mo, 'txt')}>"
def __str__(self): def __str__(self):
if self.RENDER == "tex": try:
return self.__tex__ return render(self._mo._tree)
elif self.RENDER == "txt": except AttributeError:
return self.__txt__ return render(self._mo)
else:
raise ValueError(f"Unknow render {self.RENDER}")
# return renders[self.RENDER](self._mo)
@property
def __txt__(self):
return self._mo.__txt__
@property
def __tex__(self):
return self._mo.__tex__
@property @property
def raw(self): def raw(self):
@@ -261,11 +242,10 @@ class Token(object):
>>> c = a ** 2 >>> c = a ** 2
>>> c >>> c
<Decimal 5.29> <Decimal 5.29>
"""
"""
return self._operate(other, "^") return self._operate(other, "^")
def _roperate(self, other, operation): def _roperate(self, other, operation):
""" Make a operation between 2 Tokens """ """ Make a operation between 2 Tokens """
from ..expression import Expression from ..expression import Expression
@@ -315,6 +295,7 @@ class Token(object):
<Linear x - 3> <Linear x - 3>
""" """
return self._roperate(other, "-") return self._roperate(other, "-")
def __rmul__(self, other): def __rmul__(self, other):
""" Multiply 2 Tokens or a Token and a Expression """ Multiply 2 Tokens or a Token and a Expression
@@ -342,7 +323,6 @@ class Token(object):
""" """
return self._roperate(other, "/") return self._roperate(other, "/")
def _get_soul(self, other=None): def _get_soul(self, other=None):
""" Get the builtin soul of self or other """ """ Get the builtin soul of self or other """
if isinstance(other, Token): if isinstance(other, Token):

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. 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 >>> from mapytex.calculus import Expression
>>> Expression.set_render("txt") >>> render.set_render("txt")
>>> e = Expression.from_str("2x + 6 - 3x") >>> e = Expression.from_str("2x + 6 - 3x")
>>> print(e) >>> print(e)
2x + 6 - 3x 2x + 6 - 3x
@@ -30,7 +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, Integer, Decimal, render, Polynomial, Fraction
from .core import random_list from .core import random_list
from decimal import getcontext from decimal import getcontext
#getcontext().prec = 2 #getcontext().prec = 2

View File

@@ -37,7 +37,7 @@ def moify_cor(target):
>>> for i in [-2, "+", "x", "*", Decimal("3.3")]: >>> for i in [-2, "+", "x", "*", Decimal("3.3")]:
... list2molist.send(i) ... list2molist.send(i)
>>> list2molist.throw(STOOOP) >>> list2molist.throw(STOOOP)
[<MOnumber - 2>, '+', <MOstr x>, '*', <MOnumber 3.3>] [<MOnumber -2>, '+', <MOstr x>, '*', <MOnumber 3.3>]
""" """
try: try:
@@ -78,7 +78,7 @@ class MOnumber(Atom):
>>> MOnumber(23) >>> MOnumber(23)
<MOnumber 23> <MOnumber 23>
>>> MOnumber(-23) >>> MOnumber(-23)
<MOnumber - 23> <MOnumber -23>
As expected there will be trouble with float As expected there will be trouble with float
@@ -90,13 +90,13 @@ class MOnumber(Atom):
>>> MOnumber(Decimal("23.3")) >>> MOnumber(Decimal("23.3"))
<MOnumber 23.3> <MOnumber 23.3>
>>> MOnumber(Decimal("-23.3")) >>> MOnumber(Decimal("-23.3"))
<MOnumber - 23.3> <MOnumber -23.3>
Or directly passe a decimal string Or directly passe a decimal string
>>> MOnumber("23.3") >>> MOnumber("23.3")
<MOnumber 23.3> <MOnumber 23.3>
>>> MOnumber("-23.3") >>> MOnumber("-23.3")
<MOnumber - 23.3> <MOnumber -23.3>
MOnumber initialisation is idempotent MOnumber initialisation is idempotent
@@ -259,7 +259,8 @@ class MOstr(Atom):
f"An MOstr should be initiate with a single caracter string, got {val}" f"An MOstr should be initiate with a single caracter string, got {val}"
) )
if not val.isalpha(): 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) Atom.__init__(self, val)

View File

@@ -32,10 +32,10 @@ class MOFraction(Molecule):
>>> f = MOFraction(2, 3) >>> f = MOFraction(2, 3)
>>> f >>> f
<MOFraction 2 / 3> <MOFraction 2 / 3>
>>> print(f.__txt__) >>> print(f.tree)
2 / 3 /
>>> print(f.__tex__) > 2
\\dfrac{2}{3} > 3
>>> print(f) >>> print(f)
2 / 3 2 / 3
>>> f = MOFraction(2, 3, negative = True) >>> f = MOFraction(2, 3, negative = True)
@@ -44,11 +44,13 @@ class MOFraction(Molecule):
""" """
_numerator = MO.factory(numerator) _numerator = MO.factory(numerator)
_denominator = MO.factory(denominator) _denominator = MO.factory(denominator)
base_tree = Tree("/", _numerator, _denominator) base_tree = Tree("/", _numerator, _denominator)
if negative: if negative:
tree = Tree("-", None, base_tree) tree = Tree("-", None, base_tree)
else: else:
tree = base_tree tree = base_tree
Molecule.__init__(self, tree) Molecule.__init__(self, tree)
self._numerator = _numerator self._numerator = _numerator

View File

@@ -57,20 +57,12 @@ class MO(ABC):
pass pass
def __repr__(self): def __repr__(self):
return f"<{self.__class__.__name__} {self.__txt__}>" return f"<{self.__class__.__name__} {self.__str__()}>"
@abstractmethod @abstractmethod
def __str__(self): def __str__(self):
pass pass
@abstractmethod
def __txt__(self):
pass
@abstractmethod
def __tex__(self):
pass
def __hash__(self): def __hash__(self):
try: try:
return self._tree.__hash__() return self._tree.__hash__()
@@ -161,21 +153,21 @@ class Molecule(MO):
It is a wrapping of tree It is a wrapping of tree
Its wrapping tree can be access throw .tree property Its wrapping tree can be access through .tree property
""" """
MAINOP = None MAINOP = None
def __init__(self, value): def __init__(self, tree):
""" Initiate the MO """ Initiate the MO
It should be idempotent. It should be idempotent.
""" """
try: try:
self._tree = value._tree self._tree = tree._tree
except AttributeError: except AttributeError:
self._tree = value self._tree = tree
self.is_scalar = True self.is_scalar = True
self._signature = None self._signature = None
@@ -186,18 +178,10 @@ class Molecule(MO):
@property @property
def content(self): def content(self):
return self._tree return self.tree
def __str__(self): def __str__(self):
return str(self.__txt__) return tree2txt(self.tree)
@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> <MOstrPower x^2>
>>> print(s) >>> print(s)
x^2 x^2
>>> print(s.__txt__) >>> print(s.tree)
x^2 ^
>>> print(s.__tex__) > x
x^{2} > 2
>>> MOstrPower(3, 1) >>> MOstrPower(3, 1)
Traceback (most recent call last): Traceback (most recent call last):
... ...
@@ -59,7 +59,8 @@ class MOstrPower(Molecule):
""" """
_variable = MO.factory(variable) _variable = MO.factory(variable)
if not isinstance(_variable, MOstr): 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 self._variable = _variable
_power = MO.factory(power) _power = MO.factory(power)
@@ -102,7 +103,7 @@ class MOstrPower(Molecule):
@property @property
def signature(self): def signature(self):
""" Name of the mo in the API """ Name of the mo in the API
:example: :example:
>>> MOstrPower("x", 3).signature >>> MOstrPower("x", 3).signature
'monome3' 'monome3'
@@ -123,7 +124,8 @@ class MOstrPower(Molecule):
""" """
if self._power > 2: if self._power > 2:
return Tree( return Tree(
"*", self.power, MOstrPower(self.variable, self._power._value - 1) "*", self.power, MOstrPower(self.variable,
self._power._value - 1)
) )
return Tree("*", self.power, MOstr(self.variable)) return Tree("*", self.power, MOstr(self.variable))
@@ -147,22 +149,20 @@ class MOMonomial(Molecule):
<MOMonomial 4x> <MOMonomial 4x>
>>> print(m) >>> print(m)
4x 4x
>>> print(m.__txt__) >>> print(m.tree)
4x *
>>> print(m.__tex__) > 4
4x > x
>>> x = MOstrPower('x', 2) >>> x = MOstrPower('x', 2)
>>> MOMonomial(4, x) >>> MOMonomial(4, x)
<MOMonomial 4x^2> <MOMonomial 4x^2>
>>> m = MOMonomial(4, 'x') >>> m = MOMonomial(-1, 'x')
>>> m >>> m
<MOMonomial 4x> <MOMonomial - x>
>>> print(m) >>> print(m.tree)
4x -
>>> print(m.__txt__) > None
4x > x
>>> print(m.__tex__)
4x
>>> MOMonomial(4, 'x', 1) >>> MOMonomial(4, 'x', 1)
<MOMonomial 4x> <MOMonomial 4x>
>>> MOMonomial(4, 'x', 2) >>> MOMonomial(4, 'x', 2)
@@ -170,6 +170,13 @@ class MOMonomial(Molecule):
>>> x2 = MOstrPower('x', 2) >>> x2 = MOstrPower('x', 2)
>>> MOMonomial(4, x2, 3) >>> MOMonomial(4, x2, 3)
<MOMonomial 4x^6> <MOMonomial 4x^6>
>>> m = MOMonomial(-1, 'x', 2)
>>> m
<MOMonomial - x^2>
>>> print(m.tree)
-
> None
> x^2
>>> MOMonomial(0, x) >>> MOMonomial(0, x)
Traceback (most recent call last): Traceback (most recent call last):
... ...
@@ -199,34 +206,21 @@ class MOMonomial(Molecule):
self._power = _power self._power = _power
try: try:
if self._coefficient.value != 1: if self.coefficient.value == 1:
_tree = Tree("*", self._coefficient, self.strpower)
else:
_tree = self.strpower _tree = self.strpower
else:
_tree = Tree("*", self.coefficient, self.strpower)
except AttributeError: except AttributeError:
_tree = Tree("*", self._coefficient, self.strpower) _tree = Tree("*", self.coefficient, self.strpower)
Molecule.__init__(self, _tree) Molecule.__init__(self, _tree)
def __str__(self):
if self._coefficient != -1:
return super(MOMonomial, self).__str__()
else:
return "- " + self.strpower.__str__()
@property @property
def __txt__(self): def tree(self):
if self._coefficient != -1: if self._coefficient == -1:
return super(MOMonomial, self).__txt__ return Tree("-", None, self.strpower)
else:
return "- " + self.strpower.__txt__
@property return Tree("*", self.coefficient, self.strpower)
def __tex__(self):
if self._coefficient != -1:
return super(MOMonomial, self).__tex__
else:
return "- " + self.strpower.__tex__
@property @property
def coefficient(self): def coefficient(self):
@@ -265,7 +259,7 @@ class MOMonomial(Molecule):
@property @property
def signature(self): def signature(self):
""" Name of the mo in the API """ Name of the mo in the API
:example: :example:
>>> MOMonomial(2, "x").signature >>> MOMonomial(2, "x").signature
'monome1' 'monome1'

View File

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

View File

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

View File

@@ -71,7 +71,7 @@ This function ignores tree structure and works with lists
{'a': -8, 'a*b': -40, 'b': 5, 'c': 4} {'a': -8, 'a*b': -40, 'b': 5, 'c': 4}
""" """
__all__ = ["generator"] __all__ = ["list_generator"]
from random import choice from random import choice
from functools import reduce from functools import reduce
@@ -256,7 +256,7 @@ def build_variable_scope(rd_variables, rejected, min_max, variables_scope):
return complete_scope return complete_scope
def list_generator(var_list, conditions=[], rejected=[0], min_max=(-10, 10), variables_scope={}): def list_generator(var_list, conditions=[], rejected=[0], min_max=(-10, 10), variables_scope={}, dictionnary=False):
""" Generate random computed values from the list """ Generate random computed values from the list
:param rd_variables: list of random variables to generate (can be computed value - "a*b") :param rd_variables: list of random variables to generate (can be computed value - "a*b")
@@ -264,17 +264,25 @@ def list_generator(var_list, conditions=[], rejected=[0], min_max=(-10, 10), var
:param rejected: Rejected values for the generator (default [0]) :param rejected: Rejected values for the generator (default [0])
:param min_max: (min, max) limits in between variables will be generated :param min_max: (min, max) limits in between variables will be generated
:param variables_scope: rejected and min_max define for individual variables :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 :return: dictionnary of generated variables
:example: :example:
>>> values = list_generator(["a", "a*b", "b", "c"]) >>> a, ab, b, c = list_generator(["a", "a*b", "b", "c"])
>>> values # doctest: +SKIP >>> a, ab, b, c # doctest: +SKIP
>>> values["a"] * values["b"] == values["a*b"] (5, -20, -4, -3)
>>> a * b == ab
True True
>>> values["a*b"] # doctest: +SKIP >>> ab # doctest: +SKIP
>>> values["a"] * values["b"] # 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 = extract_rv(var_list)
rv_gen = random_generator(rv, conditions, rejected, min_max, variables_scope) rv_gen = random_generator(rv, conditions, rejected, min_max, variables_scope)
generated = compute_leafs(var_list, rv_gen) generated = compute_leafs(var_list, rv_gen)
return generated if dictionnary:
return generated
return [generated[v] for v in var_list]

View File

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

View File

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

View File

@@ -271,7 +271,7 @@ def concurent_broadcast(target, lookfors=[]):
>>> a = searcher.throw(STOOOP) >>> a = searcher.throw(STOOOP)
>>> print(a) >>> print(a)
['az', 'ABC', 'a', 'b', 'az', 'b'] ['az', 'ABC', 'a', 'b', 'az', 'b']
>>> lfop = lookfor(something_in("+-*/()"), lambda x: f"op{x}") >>> lfop = lookfor(something_in("+-*/()"), lambda x: f"op{x}")
>>> searcher = concurent_broadcast(list_sink, [lfop]) >>> searcher = concurent_broadcast(list_sink, [lfop])
>>> for i in '12+3+234': >>> for i in '12+3+234':
@@ -484,7 +484,8 @@ def lookforNumbers(target):
if current.replace("-", "", 1).isdigit(): if current.replace("-", "", 1).isdigit():
current += tok current += tok
else: else:
raise ParsingError(f"Can't build decimal with '{current}'") raise ParsingError(
f"Can't build decimal with '{current}'")
elif tok == "-": elif tok == "-":
if current == "": if current == "":
current = tok current = tok
@@ -802,7 +803,8 @@ def str2(sink, convert_to_mo=True):
operator_corout(missing_times(moify_cor(pparser(sink)))) operator_corout(missing_times(moify_cor(pparser(sink))))
) )
else: else:
str2_corout = lookforNumbers(operator_corout(missing_times(pparser(sink)))) str2_corout = lookforNumbers(
operator_corout(missing_times(pparser(sink))))
for i in expression.replace(" ", ""): for i in expression.replace(" ", ""):
str2_corout.send(i) str2_corout.send(i)
@@ -819,7 +821,7 @@ def rdstr2(sink):
:example: :example:
>>> 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})") >>> rdstr2list("{a}({b}x+{c})")
[<RdLeaf a>, '*', [<RdLeaf b>, '*', <MOstr x>, '+', <RdLeaf c>]] [<RdLeaf a>, '*', [<RdLeaf b>, '*', <MOstr x>, '+', <RdLeaf c>]]
""" """
@@ -828,7 +830,8 @@ def rdstr2(sink):
def pipeline(expression): def pipeline(expression):
str2_corout = look_for_rdleaf( 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(" ", ""): for i in expression.replace(" ", ""):

View File

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

View File

@@ -7,7 +7,7 @@ except ImportError:
setup( setup(
name="mapytex", name="mapytex",
version="2.3.1", version="2.3.2",
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",

0
test/__init__.py Normal file
View File

View File

@@ -0,0 +1,31 @@
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"