python math expressions to latex, verbatim (no reordering, factoring, etc.) - python

I have input of a whole lot of math expressions and equations, and I'd like to print out latex representation for each on them. So far I have tried Sage and sympy, but the tricky part is to not-reorder terms in expressions.
So, if my input is this, something that can be eval-ed in python:
(C - A*x) / B
I want output that will be something like this:
\frac{C - A x}{B}
What I don't want is something like this:
\frac{-(A x - C)}{B}
\frac{1}{B}(C - A x)
etc...
Can this be achieved? I'm slowly losing hope...
EDIT:
The input expressions are diverse, some containing square roots, nested parentheses, exponents etc. Looking for a generic solution.
Here is what doesn't work so far:
1) Sage:
sage: var('A B C x y')
(A, B, C, x, y)
sage: latex(y == (C - A*x) / B)
y = -\frac{A x - C}{B}
2) sympy:
>>> from sympy import *
>>> x = Symbol('x')
>>> A = Symbol('A')
>>> B = Symbol('B')
>>> C = Symbol('C')
>>> latex((C - A*x) / B)
'\\frac{1}{B} \\left(- A x + C\\right)'

Short of writing your own parser, I believe the only real way to do this is to use python's built-in compile() function and process the returned abstract syntax tree.

\You can do this by creating Symbol and Operator classes that implement the standard python data model (http://docs.python.org/2/reference/datamodel.html). This will keep things in the same order of python operator precedence, although you can rearrange via parens:
class Symbol(object):
def __init__(self, name):
self._name = name
def __str__(self):
return str(self._name)
def __div__(self, other):
return Div(self, other)
def __mul__(self, other):
return Mult(self, other)
def __add__(self, other):
return Add(self, other)
def __sub__(self, other):
return Sub(self, other)
def __rdiv__(self, other):
return Div(other, self)
def __rmul__(self, other):
return Mult(other, self)
def __radd__(self, other):
return Add(other, self)
def __rsub__(self, other):
return Sub(other, self)
class Operation(Symbol):
def __init__(self, a, b, op):
self._a = a
self._b = b
self._op = op
def __str__(self):
return self._op.format(self._a, self._b)
class Add(Operation):
precedence = 0
def __init__(self, a, b):
super(Add, self).__init__(a, b, "{0} + {1}")
class Sub(Operation):
precedence = 0
def __init__(self, a, b):
super(Sub, self).__init__(a, b, "{0} - {1}")
class Mult(Operation):
precedence = 1
def __init__(self, a, b):
if isinstance(a, Operation) and a.precedence < Mult.precedence:
a_form = "({0})"
else:
a_form = "{0}"
if isinstance(b, Operation) and b.precedence < Mult.precedence:
b_form = "({1})"
else:
b_form = "{1}"
super(Mult, self).__init__(a, b, a_form + " " + b_form)
class Div(Operation):
precedence = 1
def __init__(self, a, b):
super(Div, self).__init__(a, b, "\\frac{{{0}}}{{{1}}}")
A = Symbol('A')
B = Symbol('B')
C = Symbol('C')
x = Symbol('x')
Then:
>>> print (C - A * x) / (B)
\frac{C - A x}{B}
>>> print (C * (A + B))
C (A + B)
>>> print (C * (A + B + A + B + C + x))
C (A + B + A + B + C + x)

Related

How do I create a class where one attribute is a function chosen from one of several functions?

I'm learning OOP principles in Python by making a calculator and I'm not sure about how to proceed with the design I've chosen or really what design to choose. My question is more about OOP in Python than it is about a calculator, but I'm using it as an example because that's what I'm working on.
Question: How might I go about defining a class where one of its attributes is a function which returns a one of several possible functions and doesn't have access to the others. For example:
class Operator:
def __init__(self, symbol):
self.symbol = symbol
self.operation = select_operation(symbol)
def select_operation(symbol):
if symbol == '+':
return add
elif symbol == '-':
return subtract
elif symbol == '*':
return multiply
else:
return divide
def add(a, b):
return a + b
def subtract(a, b):
return a - b
def multiply(a, b):
return a * b
def divide(a, b):
return a / b
Expected result:
plus = Operator('+')
plus.symbol
'+'
plus.operation
add
plus.add(2, 3)
5
plus.subtract(2, 3)
doesn't have access to subtract function
I was thinking class methods could be used here, but I'm not sure.
Apart from some details, it is a possible way. The details is that the actual operations should not be public members. If they are you have no way to prevent them to be used. So I would change your code to:
class Operator:
_name = {'+': 'add', '-': 'substract', '*': 'multiply', '/': 'divide'}
def __init__(self, symbol):
self.symbol = symbol
self.operation = self._select_operation(symbol)
setattr(self, self._name[symbol], self.operation)
#staticmethod
def _select_operation(symbol):
return getattr(Operator, '_' + Operator._name[symbol])
#staticmethod
def _add(a, b):
return a + b
#staticmethod
def _subtract(a, b):
return a - b
#staticmethod
def _multiply(a, b):
return a * b
#staticmethod
def _divide(a, b):
return a / b
You can now do:
>>> op = Operator('+')
>>> op.operation(2, 3)
5
>>> op.add(2, 3)
5
>>> op.substract(3, 1)
Traceback (most recent call last):
File "<pyshell#6>", line 1, in <module>
op.substract(3, 1)
AttributeError: 'Operator' object has no attribute 'substract'
But IMHO it is not really the OOP way. I would create an Operator class and subclasses for the various operations. Then we could use a factory method to give the object of the appropriate subclass, or directly tweak the __new__ special method:
class Operator:
def __new__(cls, symbol):
return super().__new__(cls._name[symbol])
class Adder(Operator):
#staticmethod
def operation(a, b):
return a + b
class Substracter(Operator):
#staticmethod
def operation(a, b):
return a - b
class Multiplyier(Operator):
#staticmethod
def operation(a, b):
return a * b
class Diviser(Operator):
#staticmethod
def operation(a, b):
return a / b
Operator._name = {'+': Adder, '-': Substracter, '*': Multiplyier, '/': Diviser}
Again we can do:
>>> op = Operator('+')
>>> op.operation(2, 3)
5
but here I have never defined an add method...
add, subtract, etc. should be stand-alone functions, because they don't depend on anything in the object. Luckily, those functions are defined in the operator module:
import operator
# Use a dict to select the function.
OP_FUNCS = {
'+': operator.add,
'-': operator.sub,
'*': operator.mul,
'/': operator.truediv,
}
class Operator:
def __init__(self, symbol):
self.symbol = symbol
self.operation = OP_FUNCS[symbol]
plus = Operator('+')
print(plus.symbol)
print(plus.operation)
print(plus.operation(2, 3))
Output:
+
<built-in function add>
5
I don't think you should add an add method to the object. The operation is already fully defined by __init__. If the caller doesn't know how it created the object, it won't know which method to call anyway.

Adding two distinct subclasses as their common superclass

I have a class with a lot of subclasses in my code. Consider the following code:
class Dataset:
def __init__(self, samples):
self.data = samples
def __add__(self, other):
if isinstance(other, type(self)):
return type(self)(self.data + other.data)
return NotImplemented
def __radd__(self, other):
if isinstance(other, type(self)):
return type(self)(other.data + self.data)
return NotImplemented
def __str__(self):
return str(self.data)
class DatasetA(Dataset):
def __init__(self, samples):
super().__init__(samples)
self.a = len(self.data)
def __str__(self):
return f"{self.a}: {self.data}"
class DatasetB(Dataset):
def __init__(self, samples):
super().__init__(samples)
self.b = sum(self.data)
def __str__(self):
return f"{self.data} (sum: {self.b})"
d = Dataset([1, 2, 3])
a = DatasetA([4, 5])
b = DatasetB([6, 7])
print(d + d)
print(d + a)
print(a + d)
print(d + b)
print(b + d)
print(a + a)
print(b + b)
print(a + b)
print(b + a)
The idea is to define an __add__ method in the superclass that won't require being overridden in each of the subclasses and will still add them correctly (for instance, two DatasetBs should add up to another DatasetB). This works correctly (the first 7 prints are fine), however there is one additional functionality I'd like to implement, represented in the last 2 prints.
I would like any two distinct subclasses to add up to the first common superclass. For example, a + b should result in a Dataset instance. Also if we add up instances of two subclasses of DatasetB, the result should be a DatasetB instance.
I've tried changing return NotImplemented to return super().__add__(other) (and similar for __radd__), but that resulted in an AttributeError on the 3rd print statement already.
Is there a way to implement this desired functionality without breaking the existing one (i.e. still have the first 7 prints execute properly), and without explicitly having to override __add__ in each of the subclasses?
You could change:
return type(self)(self.data + other.data)
to:
klass = next(c for c in type(self).mro() if c in type(other).mro())
return klass(self.data + other.data)
This will pick the most specific superclass the two have in common, leveraging the method resolution order.

Interval class - Python

I am supposed to write a class for Intervals, then I need to define addition (how to add two intervals together).
I've done this and it works:
def __add__ (self, other):
return Interval (self.a + other.a, self.b + other.b)
where a and b are ending points of one interval.
Now I need to modify the code so that addition between an interval and number c (float or int) is defined.
[a,b] + c = [a+c,b+c] and
c + [a,b] = [a+c,b+c].
I've tried a lot of things that don't work, something like:
def __add__ (self, other, *args):
if args:
return Interval (self.a + other.a, self.b + other.b)
else:
return Interval (self.a + int(number), self.b + int(number))
Whatever I try it doesn't work. If you have time, please take a look and give me a hint. I'd really appreciate that!
If you want to define both Interval(a, b) + Interval(c, d) and Interval(a, b) + c (for some non-Interval type of c), you need to examine the argument other in the definition.
def __add__(self, other):
if instanceof(other, Interval):
return Interval(self.a + other.a, self.b + other.b)
elif instanceof(other, (int, float)):
return Interval(self.a + other, self.b + other)
else:
return NotImplemented
To support c + Interval(a, b) as well, you need to define __radd__:
def __radd__(self, other):
return self + other
If you right 3 + Interval(a, b), 3.__add__(Interval(a, b)) doesn't know how to deal with an Interval, so it returns NotImplemented, which is Python's cue to try Interval(a, b).__radd__(3) instead. The definition of __radd__ usually isn't too complicated, unless your operation isn't commutative (that is, 3 + Interval(a, b) and Interval(a, b) + 3 are not equal).
You could assume that other is already an Interval and try the addition, but catch an exception when not:
def __add__ (self, other):
try:
return Interval (self.a + other.a, self.b + other.b)
except AttributeError:
pass
return Interval (self.a + int(other), self.b + int(other))
If you then want to calculate 42 + x you need the radd method:
def __radd__(self, other):
return self + other

How to concat multiple functions in python

How to do this in python?
def f(a, b):
return a, b+2, b+3
def g(a, b, c):
return a+b+c
How to get something like k = f+g hence that
k(a, b) is g(f(a,b))
Note that this is an abstract question. I wonder whether there's a function that can return a concat of f+g or even concat([...]) generally working regardless of the args of f.
In another word, I want a function whose args is f and g and returns k:
def concat(f,g):
return something_equivalent_to_k_above
Answer for original question
Define:
k = lambda x,y:g(*f(x,y))
Example:
>>> k(2,3)
13
Answer for revised question
Define:
concat = lambda a,b:lambda *args: b(*a(*args))
Example:
>>> k = concat(f,g)
>>> k(2,3)
13
There are at least 2 approaches to do this:
Approach A (recommended):
def compound(inner_func, outer_func):
def wrapper(*args, **kwargs):
return outer_func(*inner_func(*args, **kwargs))
return wrapper
Note that inner_func mustn't return a dictionary, in which case we should write return outer_func(**inner_func(*args, **argv)).
Usage:
def f(a, b):
return a, b+2, b+3
def g(a, b, c):
return a+b+c
k = compound(f, g)
print k(1, 2)
# output: 10
Approach B:
First, define a "decorator factory" like this:
def decorator_factory(inner_func):
def decorator(outer_func):
def wrapper(*args, **kwargs):
return outer_func(*inner_func(*args, **kwargs))
return wrapper
return decorator
Then you can use it like this:
def f(a, b):
return a, b+2, b+3
#decorator_factory(f)
def g(a, b, c):
return a+b+c
print g(1, 2)
# output: 10
Why not define a third function as you did with f and g:
>>> def f(a, b):
return a, b+2, b+3
>>> def g(a,b,c):
return a+b+c
>>> def k(a,b):
return g(*f(a,b))
>>> k(2,3)
13

Intersection of two lists containing Object types in python

How to find the intersection of two lists containing tuples of float values in Python?
For example:
A = [(1.1,2.2),(3.3,4.4),(5.5,6.6)]
B = [(1.1,2.2),(7.7,8.8),(3.3,4.4)]
I need
A intersection B = [(1.1,2.2),(3.3,4.4)]
Update:
My bad. Thanks for the response but I there was a misconception in my understanding.
The question should be
For example:
A = [Point(1.1,2.2),Point(3.3,4.4),Point(5.5,6.6)]
B = [Point(1.1,2.2),Point(7.7,8.8),Point(3.3,4.4)]
I need
A intersection B = [Point(1.1,2.2),Point(3.3,4.4)]
where Point is my python class containing two float variables as shown
class Point:
def __init__(self, a_, b_):
self.a = a_
self.b = b_
If order does not matter use set.intersection:
A = [(1.1,2.2),(3.3,4.4),(5.5,6.6)]
B = [(1.1,2.2),(7.7,8.8),(3.3,4.4)]
print(set(A).intersection(B))
set([(3.3, 4.4), (1.1, 2.2)])
Or make B a set and iterate over A keeping common elements:
st = set(B)
print([ele for ele in A if ele in st ])
[(1.1, 2.2), (3.3, 4.4)]
If you are looking for objects with the same attribute values:
A = [Point(1.1,2.2),Point(3.3,4.4),Point(5.5,6.6)]
B = [Point(1.1,2.2),Point(7.7,8.8),Point(3.3,4.4)]
st = set((p.a,p.b) for p in B)
print([p for p in A if (p.a,p.b) in st])
Or create a hash method in your class:
class Point(object):
def __init__(self, a_, b_):
self.a = a_
self.b = b_
def __hash__(self):
return hash((self.a, self.b))
def __eq__(self, other):
return self.a, self.b == other.a,other.b
def __ne__(self, other):
return not self.__eq__(other)
A = [Point(1.1,2.2),Point(3.3,4.4),Point(5.5,6.6)]
B = [Point(1.1,2.2),Point(7.7,8.8),Point(3.3,4.4)]
print(set(A).intersection(B))

Categories