def _distributive(self): if self.type in __distributions: star = __distributions[self.type] extracted = [] factorList = Counter() def _pushToExtracted(child, count): assert count == 1 extracted.append(child) factorList.update(keysExcept(child.children, Expression.isConstant)) (rest, hasStar) = performIf(self.children, Expression.isType(star), _pushToExtracted) if hasStar: # Algorithm for factorization: # # 1. find the most common factor # 2. check if that factor has appeared >1 times. If no, quit. # 3. otherwise, scan for all children which contain that factor. # 4. remove that factor from those children, and create a new # a*(b+c+d) style expression. factorizedOnce = False while factorList: (commonest, count) = factorList.most_common(1)[0] if count == 1: if factorizedOnce: rest.update(extracted) return self.replaceChildren(rest) else: return None else: factorizedOnce = True oldExtracted = extracted extracted = [] newChildrenList = [] for child in oldExtracted: if commonest in child.children: factorList.subtract( keysExcept(child.children, Expression.isConstant)) newChildChildren = Counter(child.children) newChildChildren[commonest] -= 1 newChild = child.replaceChildren(newChildChildren) newChildrenList.append(newChild) else: extracted.append(child) newExpression = Expression( star, commonest, Expression(self.type, *newChildrenList)) extracted.append(newExpression) factorList.update( keysExcept(child.children, Expression.isConstant))
def _involution(self): # (a ^ a) == 0 if self.type == '^': if any(count > 1 for count in list(self.children.values())): return Expression( self.type, *(k for k, c in list(self.children.items()) if c % 2 != 0))
def _evaluateIfThenElse(self): if self.type == '?:' and Expression.isConstant(self.children[0]): if self.children[0].value: return self.children[1] else: return self.children[2] else: return None
def _negatedComparison(self): # !(a < b) <=> b <= a if self.type == '!': child = self.children[0] if child.type in __negatedComparisonMap: return Expression(__negatedComparisonMap[child.type], child.children[1], child.children[0])
def _shortCircuit(self): if self.type in __shortCircuitTarget: target = __shortCircuitTarget[self.type] targetType = type(target) for v in self.children: if Expression.isConstant(v): if targetType(v.value) == target: return Constant(target)
def _distributive(self): if self.type in __distributions: star = __distributions[self.type] extracted = [] factorList = Counter() def _pushToExtracted(child, count): assert count == 1 extracted.append(child) factorList.update( keysExcept(child.children, Expression.isConstant)) (rest, hasStar) = performIf(self.children, Expression.isType(star), _pushToExtracted) if hasStar: # Algorithm for factorization: # # 1. find the most common factor # 2. check if that factor has appeared >1 times. If no, quit. # 3. otherwise, scan for all children which contain that factor. # 4. remove that factor from those children, and create a new # a*(b+c+d) style expression. factorizedOnce = False while factorList: (commonest, count) = factorList.most_common(1)[0] if count == 1: if factorizedOnce: rest.update(extracted) return self.replaceChildren(rest) else: return None else: factorizedOnce = True oldExtracted = extracted extracted = [] newChildrenList = [] for child in oldExtracted: if commonest in child.children: factorList.subtract( keysExcept(child.children, Expression.isConstant)) newChildChildren = Counter(child.children) newChildChildren[commonest] -= 1 newChild = child.replaceChildren(newChildChildren) newChildrenList.append(newChild) else: extracted.append(child) newExpression = Expression( star, commonest, Expression(self.type, *newChildrenList)) extracted.append(newExpression) factorList.update( keysExcept(child.children, Expression.isConstant))
def _equalityWithZero(self): # a == 0 <=> !a if self.type in ('==', '!='): otherChildIndex = __findNonZeroSide(self) if otherChildIndex >= 0: rv = self.children[otherChildIndex] if self.type == '==': rv = Expression.not_(rv) return rv
def _recursiveRule(self): if Expression.isAtomic(self): return None simplifier = _ChildSimplifier() if isinstance(self.children, Counter): retval = Counter({simplifier(child): count for child, count in self.children.items()}) else: retval = [simplifier(child) for child in self.children] if simplifier.simplified: return self.replaceChildren(retval)
def _flatten(self): # flatten an expression tree of the same type by applying associativity. # a + (b + c) == a + b + c. if self.type in ('+', '*', '&', '|', '^', '&&', '||'): flatPart = Counter() def _flattenAction(child, count): nonlocal flatPart flatPart += Counter({k: v*count for k, v in child.children.items()}) (rest, hasFlatten) = performIf(self.children, Expression.isType(self.type), _flattenAction) if hasFlatten: rest += flatPart return self.replaceChildren(rest)
def _recursiveRule(self): if Expression.isAtomic(self): return None simplifier = _ChildSimplifier() if isinstance(self.children, Counter): retval = Counter({ simplifier(child): count for child, count in list(self.children.items()) }) else: retval = [simplifier(child) for child in self.children] if simplifier.simplified: return self.replaceChildren(retval)
def _flatten(self): # flatten an expression tree of the same type by applying associativity. # a + (b + c) == a + b + c. if self.type in ('+', '*', '&', '|', '^', '&&', '||'): flatPart = Counter() def _flattenAction(child, count): nonlocal flatPart flatPart += Counter( {k: v * count for k, v in list(child.children.items())}) (rest, hasFlatten) = performIf(self.children, Expression.isType(self.type), _flattenAction) if hasFlatten: rest += flatPart return self.replaceChildren(rest)
class _ChildSimplifier(object): def __init__(self): self.simplified = False def __call__(self, child): if not Expression.isAtomic(child): (child, hasSimplified) = child.simplify(getSimplifyState=True) self.simplified = self.simplified or hasSimplified return child def _recursiveRule(self): if Expression.isAtomic(self): return None simplifier = _ChildSimplifier() if isinstance(self.children, Counter): retval = Counter({ simplifier(child): count for child, count in list(self.children.items()) }) else: retval = [simplifier(child) for child in self.children] if simplifier.simplified: return self.replaceChildren(retval) Expression.addSimplificationRule(_recursiveRule, 'recursive simplify')
return Expression(self.type, *list(self.children.keys())) # 1-ary and 0-ary cases are handled in fold_constant.py already. def _involution(self): # (a ^ a) == 0 if self.type == '^': if any(count > 1 for count in list(self.children.values())): return Expression( self.type, *(k for k, c in list(self.children.items()) if c % 2 != 0)) Expression.addSimplificationRule(_flatten, 'commutative semigroup (a*(b*c) == a*b*c)') Expression.addSimplificationRule(_idempotent, 'idempotent ((a&a) == a)') Expression.addSimplificationRule(_involution, 'involution ((a^a) == 0)') if __name__ == '__main__': import symbolic.simplify.recursive from symbolic.expression import Symbol Expression.setDebugSimplify(True) a = Symbol('foo') + Symbol('bar') + Symbol( 'baz') + Symbol('ma') * Symbol('maz') - Symbol('y') assert a.simplify() == Expression( '+', Symbol('foo'), Symbol('bar'), Symbol('baz'), Expression('*', Symbol('ma'), Symbol('maz')), -Symbol('y'))
def __call__(self, child): if not Expression.isAtomic(child): (child, hasSimplified) = child.simplify(getSimplifyState=True) self.simplified = self.simplified or hasSimplified return child
return Constant(__naryDefaultValue[self.type]) elif uniqueChildrenCount == 1: (child, value) = list(self.children.items())[0] if value == 1: return child def _evaluateIfThenElse(self): if self.type == '?:' and Expression.isConstant(self.children[0]): if self.children[0].value: return self.children[1] else: return self.children[2] else: return None Expression.addSimplificationRule(_unary, 'fold constant (unary)') Expression.addSimplificationRule(_binary, 'fold constant (binary)') Expression.addSimplificationRule(_shortCircuit, 'short circuit') Expression.addSimplificationRule(_nary, 'fold constant (N-ary)') Expression.addSimplificationRule(_naryBaseCondition, 'base condition (N-ary)') Expression.addSimplificationRule(_evaluateIfThenElse, 'constant condition (?:)') if __name__ == '__main__': from symbolic.simplify.recursive import * from symbolic.expression import Symbol Expression.setDebugSimplify(True) a = Constant(3) / Constant(2) assert Constant(1.5) == a.simplify()
def __isNegativeFactor(self): if self.type == '*': for c in self.children: if Expression.isConstant(c) and c.value < 0: return True return False
from symbolic.expression import Expression from collections import Counter class _ChildSimplifier(object): def __init__(self): self.simplified = False def __call__(self, child): if not Expression.isAtomic(child): (child, hasSimplified) = child.simplify(getSimplifyState=True) self.simplified = self.simplified or hasSimplified return child def _recursiveRule(self): if Expression.isAtomic(self): return None simplifier = _ChildSimplifier() if isinstance(self.children, Counter): retval = Counter({simplifier(child): count for child, count in self.children.items()}) else: retval = [simplifier(child) for child in self.children] if simplifier.simplified: return self.replaceChildren(retval) Expression.addSimplificationRule(_recursiveRule, 'recursive simplify')
__negatedComparisonMap = {'==': '!=', '!=': '==', '<': '<=', '<=': '<'} def _negatedComparison(self): # !(a < b) <=> b <= a if self.type == '!': child = self.children[0] if child.type in __negatedComparisonMap: return Expression(__negatedComparisonMap[child.type], child.children[1], child.children[0]) Expression.addSimplificationRule(_selfCompare, 'self comparison (a==a <=> True)') Expression.addSimplificationRule(_negatedComparison, 'negated comparison (!(a<b) <=> b<=a)') Expression.addSimplificationRule(_subtractionAndCompareWithZero, 'subtract and compare (a-b<0 <=> a<b)') Expression.addSimplificationRule(_equalityWithZero, 'equality with zero (a==0 <=> !a)') if __name__ == '__main__': import symbolic.simplify.recursive import symbolic.simplify.fold_constant import symbolic.simplify.distributive from symbolic.expression import Symbol Expression.setDebugSimplify(True)
for child in oldExtracted: if commonest in child.children: factorList.subtract(keysExcept(child.children, Expression.isConstant)) newChildChildren = Counter(child.children) newChildChildren[commonest] -= 1 newChild = child.replaceChildren(newChildChildren) newChildrenList.append(newChild) else: extracted.append(child) newExpression = Expression(star, commonest, Expression(self.type, *newChildrenList)) extracted.append(newExpression) factorList.update(keysExcept(child.children, Expression.isConstant)) Expression.addSimplificationRule(_repetition, 'repetition (a+a+a=3*a)') Expression.addSimplificationRule(_distributive, 'distributive (a*b+a*c=a*(b+c))') if __name__ == '__main__': import symbolic.simplify.recursive from symbolic.expression import Symbol Expression.setDebugSimplify(True) a = Expression('+', Symbol('a'), Symbol('a'), Symbol('b'), Symbol('a'), Symbol('b'), Symbol('b'), Symbol('a'), Symbol('c')) + \ Expression('+', Symbol('c'), Symbol('a'), Symbol('c'), Symbol('c'), Symbol('b'), Symbol('a'), Symbol('a'), Symbol('d')) assert a.simplify() == Expression('+', Expression('*', Symbol('a'), Constant(7)), Expression('*', Symbol('b'), Constant(4)), Expression('*', Symbol('c'), Constant(4)), Symbol('d'))
def __findNonZeroSide(self): # Check if one side is zero. for i, c in enumerate(self.children): if Expression.isConstant(c) and c.value == 0: return (1 - i) return -1
def _idempotent(self): # (a & a) == a if self.type in ('&', '|', '&&', '||'): if any(count > 1 for count in list(self.children.values())): return Expression(self.type, *list(self.children.keys()))
def _evaluteRepetition(child, count): grouped.append(Expression(star, child, Constant(count)))
def _binary(self): if self.type in __binaryFuncs and Expression.isConstant(self.children[0]) and Expression.isConstant(self.children[1]): return Constant(__binaryFuncs[self.type](self.children[0].value, self.children[1].value))
def _idempotent(self): # (a & a) == a if self.type in ('&', '|', '&&', '||'): if any(count > 1 for count in self.children.values()): return Expression(self.type, *self.children.keys()) # 1-ary and 0-ary cases are handled in fold_constant.py already. def _involution(self): # (a ^ a) == 0 if self.type == '^': if any(count > 1 for count in self.children.values()): return Expression(self.type, *(k for k, c in self.children.items() if c % 2 != 0)) Expression.addSimplificationRule(_flatten, 'commutative semigroup (a*(b*c) == a*b*c)') Expression.addSimplificationRule(_idempotent, 'idempotent ((a&a) == a)') Expression.addSimplificationRule(_involution, 'involution ((a^a) == 0)') if __name__ == '__main__': import symbolic.simplify.recursive from symbolic.expression import Symbol Expression.setDebugSimplify(True) a = Symbol('foo') + Symbol('bar') + Symbol('baz') + Symbol('ma') * Symbol('maz') - Symbol('y') assert a.simplify() == Expression('+', Symbol('foo'), Symbol('bar'), Symbol('baz'), Expression('*', Symbol('ma'), Symbol('maz')), -Symbol('y')) a = (Expression('&', Symbol('foo'), Symbol('bar'), Symbol('foo'), Symbol('baz'), Symbol('bar'), Symbol('bar')) & \ Expression('^', Symbol('foo'), Symbol('bar'), Symbol('foo'), Symbol('baz'), Symbol('bar'), Symbol('bar'))) assert a.simplify() == Expression('&', Symbol('foo'), Symbol('bar'), Symbol('baz'), Expression('^', Symbol('bar'), Symbol('baz')))
(child, value) = list(self.children.items())[0] if value == 1: return child def _evaluateIfThenElse(self): if self.type == '?:' and Expression.isConstant(self.children[0]): if self.children[0].value: return self.children[1] else: return self.children[2] else: return None Expression.addSimplificationRule(_unary, 'fold constant (unary)') Expression.addSimplificationRule(_binary, 'fold constant (binary)') Expression.addSimplificationRule(_shortCircuit, 'short circuit') Expression.addSimplificationRule(_nary, 'fold constant (N-ary)') Expression.addSimplificationRule(_naryBaseCondition, 'base condition (N-ary)') Expression.addSimplificationRule(_evaluateIfThenElse, 'constant condition (?:)') if __name__ == '__main__': from symbolic.simplify.recursive import * from symbolic.expression import Symbol Expression.setDebugSimplify(True) a = Constant(3) / Constant(2) assert Constant(1.5) == a.simplify()
Expression.isConstant)) newChildChildren = Counter(child.children) newChildChildren[commonest] -= 1 newChild = child.replaceChildren(newChildChildren) newChildrenList.append(newChild) else: extracted.append(child) newExpression = Expression( star, commonest, Expression(self.type, *newChildrenList)) extracted.append(newExpression) factorList.update( keysExcept(child.children, Expression.isConstant)) Expression.addSimplificationRule(_repetition, 'repetition (a+a+a=3*a)') Expression.addSimplificationRule(_distributive, 'distributive (a*b+a*c=a*(b+c))') if __name__ == '__main__': import symbolic.simplify.recursive from symbolic.expression import Symbol Expression.setDebugSimplify(True) a = Expression('+', Symbol('a'), Symbol('a'), Symbol('b'), Symbol('a'), Symbol('b'), Symbol('b'), Symbol('a'), Symbol('c')) + \ Expression('+', Symbol('c'), Symbol('a'), Symbol('c'), Symbol('c'), Symbol('b'), Symbol('a'), Symbol('a'), Symbol('d')) assert a.simplify() == Expression( '+', Expression('*', Symbol('a'), Constant(7)), Expression('*', Symbol('b'), Constant(4)), Expression('*', Symbol('c'), Constant(4)), Symbol('d'))
def _binary(self): if self.type in __binaryFuncs and Expression.isConstant( self.children[0]) and Expression.isConstant(self.children[1]): return Constant(__binaryFuncs[self.type](self.children[0].value, self.children[1].value))
'==': '!=', '!=': '==', '<': '<=', '<=': '<'} def _negatedComparison(self): # !(a < b) <=> b <= a if self.type == '!': child = self.children[0] if child.type in __negatedComparisonMap: return Expression(__negatedComparisonMap[child.type], child.children[1], child.children[0]) Expression.addSimplificationRule(_selfCompare, 'self comparison (a==a <=> True)') Expression.addSimplificationRule(_negatedComparison, 'negated comparison (!(a<b) <=> b<=a)') Expression.addSimplificationRule(_subtractionAndCompareWithZero, 'subtract and compare (a-b<0 <=> a<b)') Expression.addSimplificationRule(_equalityWithZero, 'equality with zero (a==0 <=> !a)') if __name__ == '__main__': import symbolic.simplify.recursive import symbolic.simplify.fold_constant import symbolic.simplify.distributive from symbolic.expression import Symbol Expression.setDebugSimplify(True) a = Expression.lt(Symbol('aaa'), Symbol('aaa')) assert Constant(False) == a.simplify()