__all__ = [
- 'Expression',
+ 'LinExpr',
'Symbol', 'Dummy', 'symbols',
'Rational',
]
def _polymorphic(func):
@functools.wraps(func)
def wrapper(left, right):
- if isinstance(right, Expression):
+ if isinstance(right, LinExpr):
return func(left, right)
elif isinstance(right, numbers.Rational):
right = Rational(right)
return wrapper
-class Expression:
+class LinExpr:
"""
This class implements linear expressions.
"""
def __new__(cls, coefficients=None, constant=0):
+ """
+ Create a new expression.
+ """
if isinstance(coefficients, str):
if constant != 0:
raise TypeError('too many arguments')
- return Expression.fromstring(coefficients)
+ return LinExpr.fromstring(coefficients)
if coefficients is None:
return Rational(constant)
if isinstance(coefficients, Mapping):
return self
def coefficient(self, symbol):
+ """
+ Return the coefficient value of the given symbol.
+ """
if not isinstance(symbol, Symbol):
raise TypeError('symbol must be a Symbol instance')
return Rational(self._coefficients.get(symbol, 0))
__getitem__ = coefficient
def coefficients(self):
+ """
+ Return a list of the coefficients of an expression
+ """
for symbol, coefficient in self._coefficients.items():
yield symbol, Rational(coefficient)
@property
def constant(self):
+ """
+ Return the constant value of an expression.
+ """
return Rational(self._constant)
@property
def symbols(self):
+ """
+ Return a list of symbols in an expression.
+ """
return self._symbols
@property
def dimension(self):
+ """
+ Create and return a new linear expression from a string or a list of coefficients and a constant.
+ """
return self._dimension
def __hash__(self):
return hash((tuple(self._coefficients.items()), self._constant))
def isconstant(self):
+ """
+ Return true if an expression is a constant.
+ """
return False
def issymbol(self):
+ """
+ Return true if an expression is a symbol.
+ """
return False
def values(self):
+ """
+ Return the coefficient and constant values of an expression.
+ """
for coefficient in self._coefficients.values():
yield Rational(coefficient)
yield Rational(self._constant)
@_polymorphic
def __add__(self, other):
+ """
+ Return the sum of two expressions.
+ """
coefficients = defaultdict(Fraction, self._coefficients)
for symbol, coefficient in other._coefficients.items():
coefficients[symbol] += coefficient
constant = self._constant + other._constant
- return Expression(coefficients, constant)
+ return LinExpr(coefficients, constant)
__radd__ = __add__
@_polymorphic
def __sub__(self, other):
+ """
+ Return the difference between two expressions.
+ """
coefficients = defaultdict(Fraction, self._coefficients)
for symbol, coefficient in other._coefficients.items():
coefficients[symbol] -= coefficient
constant = self._constant - other._constant
- return Expression(coefficients, constant)
+ return LinExpr(coefficients, constant)
@_polymorphic
def __rsub__(self, other):
return other - self
def __mul__(self, other):
+ """
+ Return the product of two expressions if other is a rational number.
+ """
if isinstance(other, numbers.Rational):
coefficients = ((symbol, coefficient * other)
for symbol, coefficient in self._coefficients.items())
constant = self._constant * other
- return Expression(coefficients, constant)
+ return LinExpr(coefficients, constant)
return NotImplemented
__rmul__ = __mul__
coefficients = ((symbol, coefficient / other)
for symbol, coefficient in self._coefficients.items())
constant = self._constant / other
- return Expression(coefficients, constant)
+ return LinExpr(coefficients, constant)
return NotImplemented
@_polymorphic
def __eq__(self, other):
- # returns a boolean, not a constraint
- # see http://docs.sympy.org/dev/tutorial/gotchas.html#equals-signs
- return isinstance(other, Expression) and \
+ """
+ Test whether two expressions are equal
+ """
+ return isinstance(other, LinExpr) and \
self._coefficients == other._coefficients and \
self._constant == other._constant
return Gt(self, other)
def scaleint(self):
+ """
+ Multiply an expression by a scalar to make all coefficients integer values.
+ """
lcm = functools.reduce(lambda a, b: a*b // gcd(a, b),
[value.denominator for value in self.values()])
return self * lcm
def subs(self, symbol, expression=None):
+ """
+ Subsitute symbol by expression in equations and return the resulting
+ expression.
+ """
if expression is None:
if isinstance(symbol, Mapping):
symbol = symbol.items()
if othersymbol != symbol]
coefficient = result._coefficients.get(symbol, 0)
constant = result._constant
- result = Expression(coefficients, constant) + coefficient*expression
+ result = LinExpr(coefficients, constant) + coefficient*expression
return result
@classmethod
@classmethod
def fromstring(cls, string):
+ """
+ Create an expression from a string.
+ """
# add implicit multiplication operators, e.g. '5x' -> '5*x'
- string = Expression._RE_NUM_VAR.sub(r'\1*\2', string)
+ string = LinExpr._RE_NUM_VAR.sub(r'\1*\2', string)
tree = ast.parse(string, 'eval')
return cls._fromast(tree)
@classmethod
def fromsympy(cls, expr):
+ """
+ Convert sympy object to an expression.
+ """
import sympy
coefficients = []
constant = 0
coefficients.append((symbol, coefficient))
else:
raise ValueError('non-linear expression: {!r}'.format(expr))
- return Expression(coefficients, constant)
+ return LinExpr(coefficients, constant)
def tosympy(self):
+ """
+ Return an expression as a sympy object.
+ """
import sympy
expr = 0
for symbol, coefficient in self.coefficients():
return expr
-class Symbol(Expression):
+class Symbol(LinExpr):
def __new__(cls, name):
+ """
+ Create and return a symbol from a string.
+ """
if not isinstance(name, str):
raise TypeError('name must be a string')
self = object().__new__(cls)
return self.sortkey() == other.sortkey()
def asdummy(self):
+ """
+ Return a symbol as a Dummy Symbol.
+ """
return Dummy(self.name)
@classmethod
class Dummy(Symbol):
-
+ """
+ This class returns a dummy symbol to ensure that no variables are repeated in an expression
+ """
_count = 0
def __new__(cls, name=None):
+ """
+ Create and return a new dummy symbol.
+ """
if name is None:
name = 'Dummy_{}'.format(Dummy._count)
elif not isinstance(name, str):
def symbols(names):
+ """
+ Transform strings into instances of the Symbol class
+ """
if isinstance(names, str):
names = names.replace(',', ' ').split()
return tuple(Symbol(name) for name in names)
-class Rational(Expression, Fraction):
+class Rational(LinExpr, Fraction):
+ """
+ This class represents integers and rational numbers of any size.
+ """
def __new__(cls, numerator=0, denominator=None):
self = object().__new__(cls)
@property
def constant(self):
+ """
+ Return rational as a constant.
+ """
return self
def isconstant(self):
+ """
+ Test whether a value is a constant.
+ """
return True
def __bool__(self):
@classmethod
def fromsympy(cls, expr):
+ """
+ Create a rational object from a sympy expression
+ """
import sympy
if isinstance(expr, sympy.Rational):
return Rational(expr.p, expr.q)