NOTE: This post is heavy edited. I found a solution.
My question was, how I can apply all possible combinations of a truth table for an arbitary number of variables to a dictionary. E.g. for 3 vars I have a dictionary like 'lib = {"x":0, "y":0, "z":0}' I wanted to generate all 8 possible combinations.
I need the n-tuples in this format to evaluate a given expression.
Because some wanted to know why I needed it. I was working on an exercise where I created a class with subclasses to handle boolean expressions.
While brooding over the problem I looked over the previous chapters in the exercise book and realized, that I might solve it with recursion.
For those interested here is my implementation and with my solution:
# classes to handle boolean expressions
class ExprBoolean:
def __str__(self): # necessary for considering precedence: not > and > or > equal
return self.str_aux(0)
def init_Lib_Keys(self): # if not existent, generate class var: lib - dictionary with all
try: # vars, keys - list with all vars, tt - empty list for truth table
self.lib
except:
self.lib = self.getVar({})
self.keys = list(self.lib.keys())
self.tt = []
# ---- Solution I was looking for --------------------------------
def TruthTable(self,keys): # generates truth table considering all vars and entered expression
if keys == []: # condition to insert n-tupel as row in lib-string
dummy = []
for key in self.lib:
dummy += [self.lib[key]] + ["\t| "]
dummy += [self.eval(self.lib)] + ["\n"]
self.tt += [dummy]
return
for keyN in range(len(keys)):
for i in [True,False]:
self.lib[keys[keyN]] = i
self.TruthTable(keys[1:])
break
# ----------------------------------------------------------------
def make_tt(self): # prints a truth table
self.init_Lib_Keys()
for key in self.keys:
print("{}\t\t| ".format(key),end="") # header of table with
print(self) # expression in last column
if not self.tt:
self.TruthTable(self.keys) # generates truth table
for row in self.tt: # prints row after row
for col in row:
print(col,end="")
def isTauto(self): # checks if expression is tautology (always True)
self.init_Lib_Keys()
if not self.tt:
self.TruthTable(self.keys)
for row in self.tt:
if not row[-2]: # in row[-2] is evaluated expression
print(row[-2])
return
print(True)
class Not(ExprBoolean):
prec = 3 # precedence rank for brackets
def __init__(self,arg):
self.arg = arg
def str_aux(self,prec):
return "!" + self.arg.str_aux(self.prec)
def getVar(self,env):
return self.arg.getVar(env)
def eval(self,env):
return not self.arg.eval(env)
class BooOp(ExprBoolean):
def __init__(self,x,y):
self.x = x
self.y = y
def str_aux(self,prec):
s = self.x.str_aux(self.prec) + self.op + self.y.str_aux(self.prec)
if self.prec < prec:
return "(" + s + ")"
else:
return s
def getVar(self,env):
new_env = self.x.getVar(env)
new_env = self.y.getVar(new_env)
return new_env
def eval(self,env):
return self.fun(self.x.eval(env),self.y.eval(env))
class And(BooOp):
prec = 2
op = "&"
def fun(self,x,y):
return x & y
class Or(BooOp):
prec = 1
op = "|"
def fun(self,x,y):
return x | y
class Eq(BooOp):
prec = 0
op = "=="
def fun(self,x,y):
return x == y
class Var(ExprBoolean):
def __init__(self,name):
self.name = name
def str_aux(self,prec):
return self.name
def getVar(self,env):
env[self.name] = 0
return env
def eval(self,env):
return env[self.name]
# examples to test class
lib = {"x":True, "y":False, "z":True}
e1 = Or(Var("x"),Not(Var("x")))
e2 = Eq(Var("x"),Not(Not(Var("x"))))
e3 = Eq(Not(And(Var("x"),Var("y"))),Or(Not(Var("x")),Not(Var("y"))))
e4 = Eq(Not(And(Var("x"),Var("y"))),And(Not(Var("x")),Not(Var("y"))))
e5 = Eq(Eq(Eq(Var("p"),Var("q")),Var("r")),Eq(Var("p"),Eq(Var("q"),Var("r"))))
e6 = And(Or(Var("x"),Var("y")),Eq(Var("x"),Var("y")))
e4.make_tt()
e4.isTauto()
If I understand the problem, this is one way to help solve it without using eval() or having to parse the expression yourself. (You will have to implement the other Boolean functions yourself.)
def AND(a, b):
return a and b
def OR(a, b):
return a or b
def NOT(a):
return not a
x = True
y = False
z = True
print(AND(x, y))
# False
print(NOT(AND(x, y)))
# True
print(OR(x, y))
# True
print(AND(AND(x, z), NOT(y)))
# True
Even though python has classes, that doesn't mean you should treat every problem as an invitation to write a class hierarchy.
Evaluating expression is a verb, not a noun.
First off, you don't always have to write an expression evaluator. Python has eval and exec, and boolean and arithmetic expressions in Python are pretty much standard:
>>> eval("3+5")
8
>>> eval("x=3+5")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 1
x=3+5
^
SyntaxError: invalid syntax
>>> exec("x=3+5")
>>> x
8
If that is enough, look at the ast module in the standard library.
Even if you want to write it yourself, programming is often more about creating smart data that writing lots of code.
Below is a small implementation of a postfix expression parser (from one of my github repos).
import operator
import math
# Global constants {{{1
_add, _sub, _mul = operator.add, operator.sub, operator.mul
_truediv, _pow, _sqrt = operator.truediv, operator.pow, math.sqrt
_sin, _cos, _tan, _radians = math.sin, math.cos, math.tan, math.radians
_asin, _acos, _atan = math.asin, math.acos, math.atan
_degrees, _log, _log10 = math.degrees, math.log, math.log10
_e, _pi = math.e, math.pi
_ops = {
"+": (2, _add),
"-": (2, _sub),
"*": (2, _mul),
"/": (2, _truediv),
"**": (2, _pow),
"sin": (1, _sin),
"cos": (1, _cos),
"tan": (1, _tan),
"asin": (1, _asin),
"acos": (1, _acos),
"atan": (1, _atan),
"sqrt": (1, _sqrt),
"rad": (1, _radians),
"deg": (1, _degrees),
"ln": (1, _log),
"log": (1, _log10),
}
_okeys = tuple(_ops.keys())
_consts = {"e": _e, "pi": _pi}
_ckeys = tuple(_consts.keys())
def postfix(expression): # {{{1
"""
Evaluate a postfix expression.
Arguments:
expression: The expression to evaluate. Should be a string or a
sequence of strings. In a string numbers and operators
should be separated by whitespace
Returns:
The result of the expression.
"""
if isinstance(expression, str):
expression = expression.split()
stack = []
for val in expression:
if val in _okeys:
n, op = _ops[val]
if n > len(stack):
raise ValueError("not enough data on the stack")
args = stack[-n:]
stack[-n:] = [op(*args)]
elif val in _ckeys:
stack.append(_consts[val])
else:
stack.append(float(val))
return stack[-1]
The "intelligence" of this is in the _ops dictionary, which links an operator to a two-tuple of a number of arguments and an operator function.
Because of that, the evaluator itself is only 14 lines of code.
Related
or-tools module in python adds custom syntax where functions can take arbitrary expressions as arguments (something like below), which is evaluated not instantaneously but solved as a constraint later
model.Add(x + 2 * y -1 >= z)
When I print the type of the argument from my function, it shows
<class 'ortools.sat.python.cp_model.BoundedLinearExpression'>
A simple way is to pass the expression as a string, but it feels better. I wish to understand how this is achieved. Is this a way to create custom syntax in python? Does it require updating the parser or something like that?
Here is the simple program
from ortools.sat.python import cp_model
def foo(expr):
print(expr, type(expr))
def main():
model = cp_model.CpModel()
var_upper_bound = max(50, 45, 37)
x = model.NewIntVar(0, var_upper_bound, 'x')
y = model.NewIntVar(0, var_upper_bound, 'y')
z = model.NewIntVar(0, var_upper_bound, 'z')
a = 0
b = 0
c = 0
model.Add(2*x + 7*y + 3*z == 50)
solver = cp_model.CpSolver()
status = solver.Solve(model)
if status == cp_model.OPTIMAL:
print('x value: ', solver.Value(x))
print('y value: ', solver.Value(y))
print('z value: ', solver.Value(z))
foo(2*x + 7*y + 3*z == 50)
foo(2*a + 7*b + 3*c == 50)
if __name__ == '__main__':
main()
x, y, z are special variables (instances of some class) and the expression with x, y, z is stored as expression
a, b, c are simple integers and the expression is evaluated immediately and the result is stored as a bool
They override the python operators.
References:
https://docs.python.org/3/reference/datamodel.html#emulating-numeric-types
https://github.com/google/or-tools/blob/b37d9c786b69128f3505f15beca09e89bf078a89/ortools/sat/python/cp_model.py#L212-L233
def __mul__(self, arg):
if isinstance(arg, numbers.Integral):
if arg == 1:
return self
elif arg == 0:
return 0
cp_model_helper.AssertIsInt64(arg)
return _ProductCst(self, arg)
else:
raise TypeError('Not an integer linear expression: ' + str(arg))
The or-tools module defines its own classes for model variables, and within the class definitions the module defines methods for applying operators to those classes.
As a trivial example we can define our own class along with a method for '+'.
# New class will normally not support math operators.
class Blah(object):
def __init__(self, context):
self.context = context
def __add__(self, value):
# Support '+' operator with class Blah.
return self.context.format(value)
x = Blah("Chocolate with {} is the result.")
# '*' operator is not supported.
x * 2
# Traceback (most recent call last):
#
# File "<ipython-input-26-80b83cb135a7>", line 1, in <module>
# x * 2
#
# TypeError: unsupported operand type(s) for *: 'Blah' and 'int'
# '+' operator is implemented for class Blah.
x + 3
# 'Chocolate with 3 is the result.'
The Question
Is there a straightforward algorithm for figuring out if a variable is "used" within a given scope?
In a Python AST, I want to remove all assignments to variables that are not otherwise used anywhere, within a given scope.
Details
Motivating example
In the following code, it is obvious to me (a human), that _hy_anon_var_1 is unused, and therefore the _hy_anon_var_1 = None statements can be removed without changing the result:
# Before
def hailstone_sequence(n: int) -> Iterable[int]:
while n != 1:
if 0 == n % 2:
n //= 2
_hy_anon_var_1 = None
else:
n = 3 * n + 1
_hy_anon_var_1 = None
yield n
# After
def hailstone_sequence(n: int) -> Iterable[int]:
while n != 1:
if 0 == n % 2:
n //= 2
else:
n = 3 * n + 1
yield n
Bonus version
Extend this to []-lookups with string literals as keys.
In this example, I would expect _hyx_letXUffffX25['x'] to be eliminated as unused, because _hyx_letXUffffX25 is local to h, so _hyx_letXUffffX25['x'] is essentially the same thing as a local variable. I would then expect _hyx_letXUffffX25 itself to be eliminated once there are no more references to it.
# Before
def h():
_hyx_letXUffffX25 = {}
_hyx_letXUffffX25['x'] = 5
return 3
# After
def h():
return 3
From what I can tell, this is somewhat of an edge case, and I think the basic algorithmic problem is the same.
Definition of "used"
Assume that no dynamic name lookups are used in the code.
A name is used if any of these are true in a given scope:
It is referenced anywhere in an expression. Examples include: an expression in a return statement, an expression on the right-hand side of an assignment statement, a default argument in a function definition, being referenced inside a local function definition, etc.
It is referenced on the left-hand side of an "augmented assignment" statement, i.e. it is an augtarget therein. This might represent "useless work" in a lot of programs, but for the purpose of this task that's OK and distinct from being an entirely unused name.
It is nonlocal or global. These might be useless nonlocals or globals, but because they reach beyond the given scope, it is OK for my purposes to assume that they are "used".
Please let me know in the comments if this seems incorrect, or if you think I am missing something.
Examples of "used" and "unused"
Example 1: unused
Variable i in f is unused:
def f():
i = 0
return 5
Example 2: unused
Variable x in f is unused:
def f():
def g(x):
return x/5
x = 10
return g(100)
The name x does appear in g, but the variable x in g is local to g. It shadows the variable x created in f, but the two x names are not the same variable.
Variation
If g has no parameter x, then x is in fact used:
def f():
x = 10
def g():
return x/5
return g(100)
Example 3: used
Variable i in f is used:
def f():
i = 0
return i
Example 4: used
Variable accum in silly_map and silly_sum is used in both examples:
def silly_map(func, data):
data = iter(data)
accum = []
def _impl():
try:
value = next(data)
except StopIteration:
return accum
else:
accum.append(value)
return _impl()
return _impl()
def silly_any(func, data):
data = iter(data)
accum = False
def _impl():
nonlocal accum, data
try:
value = next(data)
except StopIteration:
return accum
else:
if value:
data = []
accum = True
else:
return _impl()
return _impl()
The solution below works in two parts. First, the syntax tree of the source is traversed and all unused target assignment statements are discovered. Second, the tree is traversed again via a custom ast.NodeTransformer class, which removes these offending assignment statements. The process is repeated until all unused assignment statements are removed. Once this is finished, the final source is written out.
The ast traverser class:
import ast, itertools, collections as cl
class AssgnCheck:
def __init__(self, scopes = None):
self.scopes = scopes or cl.defaultdict(list)
#classmethod
def eq_ast(cls, a1, a2):
#check that two `ast`s are the same
if type(a1) != type(a2):
return False
if isinstance(a1, list):
return all(cls.eq_ast(*i) for i in itertools.zip_longest(a1, a2))
if not isinstance(a1, ast.AST):
return a1 == a2
return all(cls.eq_ast(getattr(a1, i, None), getattr(a2, i, None))
for i in set(a1._fields)|set(a2._fields) if i != 'ctx')
def check_exist(self, t_ast, s_path):
#traverse the scope stack and remove scope assignments that are discovered in the `ast`
s_scopes = []
for _ast in t_ast:
for sid in s_path[::-1]:
s_scopes.extend(found:=[b for _, b in self.scopes[sid] if AssgnCheck.eq_ast(_ast, b) and \
all(not AssgnCheck.eq_ast(j, b) for j in s_scopes)])
self.scopes[sid] = [(a, b) for a, b in self.scopes[sid] if b not in found]
def traverse(self, _ast, s_path = [1]):
#walk the ast object itself
_t_ast = None
if isinstance(_ast, ast.Assign): #if assignment statement, add ast object to current scope
self.traverse(_ast.targets[0], s_path)
self.scopes[s_path[-1]].append((True, _ast.targets[0]))
_ast = _ast.value
if isinstance(_ast, (ast.ClassDef, ast.FunctionDef, ast.AsyncFunctionDef)):
s_path = [*s_path, (nid:=(1 if not self.scopes else max(self.scopes)+1))]
if isinstance(_ast, (ast.FunctionDef, ast.AsyncFunctionDef)):
self.scopes[nid].extend([(False, ast.Name(i.arg)) for i in _ast.args.args])
_t_ast = [*_ast.args.defaults, *_ast.body]
self.check_exist(_t_ast if _t_ast is not None else [_ast], s_path) #determine if any assignment statement targets have previously defined names
if _t_ast is None:
for _b in _ast._fields:
if isinstance((b:=getattr(_ast, _b)), list):
for i in b:
self.traverse(i, s_path)
elif isinstance(b, ast.AST):
self.traverse(b, s_path)
else:
for _ast in _t_ast:
self.traverse(_ast, s_path)
Putting it all together:
class Visit(ast.NodeTransformer):
def __init__(self, asgn):
super().__init__()
self.asgn = asgn
def visit_Assign(self, node):
#remove assignment nodes marked as unused
if any(node.targets[0] == i for i in self.asgn):
return None
return node
def remove_assgn(f_name):
tree = ast.parse(open(f_name).read())
while True:
r = AssgnCheck()
r.traverse(tree)
if not (k:=[j for b in r.scopes.values() for k, j in b if k]):
break
v = Visit(k)
tree = v.visit(tree)
return ast.unparse(tree)
print(remove_assgn('test_name_assign.py'))
Output Samples
Contents of test_name_assign.py:
def hailstone_sequence(n: int) -> Iterable[int]:
while n != 1:
if 0 == n % 2:
n //= 2
_hy_anon_var_1 = None
else:
n = 3 * n + 1
_hy_anon_var_1 = None
yield n
Output:
def hailstone_sequence(n: int) -> Iterable[int]:
while n != 1:
if 0 == n % 2:
n //= 2
else:
n = 3 * n + 1
yield n
Contents of test_name_assign.py:
def h():
_hyx_letXUffffX25 = {}
_hyx_letXUffffX25['x'] = 5
return 3
Output:
def h():
return 3
Contents of test_name_assign.py:
def f():
i = 0
return 5
Output:
def f():
return 5
Contents of test_name_assign.py:
def f():
x = 10
def g():
return x/5
return g(100)
Ouptut:
def f():
x = 10
def g():
return x / 5
return g(100)
I have a ladder operator â which acts on kets in the following way:
â |n〉= √n |n−1〉
â⁺ |n〉= √(n+1) |n+1〉
I want to use these relations to simplify more complicated expressions, for example â⁺â²|n〉. This is my Python code:
import sympy
from sympy import *
from sympy.physics.quantum import *
n = symbols('n')
a = Operator('a')
ad = Dagger(a)
down = Eq( a *Ket(n), sqrt(n )*Ket(n-1) )
up = Eq( ad*Ket(n), sqrt(n+1)*Ket(n+1) )
expr = ad * a**2 * Ket(n)
The standard and most basic way to use known equalities in sympy is subs.
But in this case it only works on |n〉, but not on |n+1〉 or other kets containing the symbol n. As can be seen in this example:
In[1]: expr.subs( down.lhs, down.rhs ).subs( down.lhs, down.rhs ).subs( up.lhs, up.rhs )
Out[1]: √n â⁺ â |n−1〉
I achieved the best result by writing a simple algorithm for this myself. The algorithm is very similar to the one in this answer and you can use it like this:
expr.apply_operator(down,up).factor()
Result
√n (n−1) |n−1〉
The algorithm only supports “pure” operators (without any operation) and adjoint operators on the left-hand side. With some minor changes to is_operator and is_expandable_pow_of the algorithm could be expanded to allow inverse operators on the left-hand side, for example.
from sympy.core.operations import AssocOp
def apply_operator(expr, eqns):
if not isinstance(expr, Basic):
raise TypeError("The expression to simplify is not a sympy expression.")
if not isinstance(eqns, list) and not isinstance(eqns, tuple):
eqns = (eqns,)
rules = []
class Rule(object):
operator = None
ketSymbol = None
result = None
generic = False
def is_operator(op):
return isinstance(op, Operator) \
or isinstance(op, Dagger) \
and isinstance(op.args[0], Operator)
for eqn in eqns:
if not isinstance(eqn, Eq):
raise TypeError("One of the equations is not a valid sympy equation.")
lhs = eqn.lhs
rhs = eqn.rhs
if not isinstance(lhs, Mul) \
or len(lhs.args) != 2 \
or not is_operator(lhs.args[0]) \
or not isinstance(lhs.args[1], KetBase):
raise ValueError("The left-hand side has to be an operator applied to a ket.")
rule = Rule()
rule.operator = lhs.args[0]
rule.ketSymbol = lhs.args[1].args[0]
rule.result = rhs
if not isinstance(rule.ketSymbol, Symbol):
raise ValueError("The left-hand ket has to contain a simple symbol.")
for ket in preorder_traversal(rhs):
if isinstance(ket, KetBase):
for symb in preorder_traversal(ket):
if symb == rule.ketSymbol:
rule.generic = True
break
rules.append(rule)
def is_expandable_pow_of(base, expr):
return isinstance(expr, Pow) \
and base == expr.args[0] \
and isinstance(expr.args[1], Number) \
and expr.args[1] >= 1
def is_ket_of_rule(ket, rule):
if not isinstance(ket, KetBase):
return False
if rule.generic:
for sym in preorder_traversal(ket):
if sym == rule.ketSymbol:
return True
return False
else:
return ket.args[0] == rule.ketSymbol
def walk_tree(expr):
if isinstance(expr, Number):
return expr
if not isinstance(expr, AssocOp) and not isinstance(expr, Function):
return expr.copy()
elif not isinstance(expr, Mul):
return expr.func(*(walk_tree(node) for node in expr.args))
else:
args = [arg for arg in expr.args]
for rule in rules:
A = rule.operator
ketSym = rule.ketSymbol
for i in range(len(args)-1):
x = args[i]
y = args[i+1]
if A == x and is_ket_of_rule(y, rule):
ev = rule.result
if rule.generic:
ev = ev.subs(rule.ketSymbol, y.args[0])
args = args[0:i] + [ev] + args[i+2:]
return walk_tree( Mul(*args).expand() )
if is_expandable_pow_of(A, x) and is_ket_of_rule(y, rule):
xpow = Pow(A, x.args[1] - 1)
ev = rule.result
if rule.generic:
ev = ev.subs(rule.ketSymbol, y.args[0])
args = args[0:i] + [xpow, ev] + args[i+2:]
return walk_tree( Mul(*args).expand() )
return expr.copy()
return walk_tree(expr)
Basic.apply_operator = lambda self, *eqns: apply_operator(self, eqns)
(No rights reserved.)
I am currently practising python on code wars, here is a prompt:
Create a Vector object that supports addition, subtraction, dot products, and norms. So, for example:
a = Vector([1, 2, 3])
b = Vector([3, 4, 5])
c = Vector([5, 6, 7, 8])
a.add(b) # should return a new Vector([4, 6, 8])
a.subtract(b) # should return a new Vector([-2, -2, -2])
a.dot(b) # should return 1*3 + 2*4 + 3*5 = 26
a.norm() # should return sqrt(1^2 + 2^2 + 3^2) = sqrt(14)
a.add(c) # raises an exception
I have written functions add and subtract that pass some of the tests. However, I am running into issues with overwriting my previous list values of 'a' after running the add function. When I go into subtract, the 'a' values in the vector are the summations computed from the previous instance of the add function.
I suspect its due to me running this line of code:
return self.__class__(self.list) causing the instance of the class to overwrite itself.
Kindly please help, I believe I need to return a copy of the instance of the class but don't know how to do it.
class Vector:
def __init__(self, list):
self.list = list #[1,2]
self.copylist = list
def add(self,Vector):
try:
self.list = self.copylist
#take list from other vector
other = Vector.list
#take each value from other Vector and add it to self.list
for index,item in enumerate(Vector.list,0):
self.list[index] = item + self.list[index]
except:
print("Different size vectors")
#return the instance of a class
return self.__class__(self.list)
def subtract(self,Vector):
self.list = self.copylist
other = Vector.list
print(self.list)
print(other)
for index,item in enumerate(Vector.list,0):
self.list[index] = self.list[index] - item
return self.__class__(self.list)
def dot(self,Vector):
self.list = self.copylist
other = Vector.list
#print(self.list)
#print(other)
running_sum =0
for index,item in enumerate(Vector.list,0):
running_sum = running_sum + item * self.list[index]
#print(running_sum, " ", self.list[index], " ", item)
return running_sum
def norm(self):
running_sum = 0
for item in self.list:
running_sum += item**2
return running_sum ** 0.5
def toString(self):
return str(self.list)
`def equals(self,Vector):
return self.list == Vector.list
Here are some of the tests:
a = Vector([1, 2])
b = Vector([3, 4])
test.expect(a.add(b).equals(Vector([4, 6])))
a = Vector([1, 2, 3])
b = Vector([3, 4, 5])
test.expect(a.add(b).equals(Vector([4, 6, 8])))
test.expect(a.subtract(b).equals(Vector([-2, -2, -2]))) #code fails here
test.assert_equals(a.dot(b), 26)
test.assert_equals(a.norm(), 14 ** 0.5)
I think you're making this more complicated than it needs to be. You shouldn't be working with class objects at all. You should just be working with instances of the Vector class. Here's what I think your code should look like:
class Vector:
def __init__(self, initial_elements):
self.elements = list(initial_elements) # make a copy of the incoming list of elements
def add(self, other):
# insure that the two vectors match in length
if len(self.elements) != len(other.elements):
raise Exception("Vector sizes are different")
# copy our elements
r = list(self.elements)
# add the elements from the second vector
for index, item in enumerate(other.elements, 0):
r[index] += item
# return a new vector object defined by the computed elements
return Vector(r)
def subtract(self, other):
# insure that the two vectors match in length
if len(self.elements) != len(other.elements):
raise Exception("Vector sizes are different")
# copy our elements
r = list(self.elements)
# subtract the elements from the second vector
for index, item in enumerate(other.elements, 0):
r[index] -= item
# return a new vector object defined by the computed elements
return Vector(r)
def dot(self, other):
running_sum = 0
for index, item in enumerate(other.elements, 0):
running_sum += item * self.elements[index]
return running_sum
def norm(self):
running_sum = 0
for item in self.elements:
running_sum += item ** 2
return running_sum ** 0.5
def toString(self):
return str(self.elements)
def equals(self, other):
return self.elements == other.elements
def test():
a = Vector([1, 2])
b = Vector([3, 4])
print(a.add(b).equals(Vector([4, 6])))
a = Vector([1, 2, 3])
b = Vector([3, 4, 5])
print(a.add(b).equals(Vector([4, 6, 8])))
print(a.subtract(b).equals(Vector([-2, -2, -2])))
print(a.dot(b) == 26)
print(a.norm() == 14 ** 0.5)
test()
Result:
True
True
True
True
True
The general structure of your code is spot on.
One thing to note is that you shouldn't be using list as a variable name, as it is a type name in Python. Also, you don't want to be passing around Vector as a value. You want to be passing instances of Vector and list, with names that do not conflict with these type names.
My solution assumes you want Vector instances to be immutable, so each of your operations will return a new Vector object. You could also have them not be immutable and have, for example, the add method just add the incoming vector into the target vector without creating a new object. I like keeping them immutable. I've been doing more and more of this "functional style" programming lately, where calls to object methods don't modify the target object (don't have side effects), but rather just return a new object.
I like your use of the test class to do your testing. I chose to not deal with this, and just print the results of each test comparison to see that they all come out to True. I'll leave it to you to restore your tests to using a test object with expect and assert_equals methods.
UPDATE: Here is a more compact way to write your add and subtract methods:
def add(self, other):
# insure that the two vectors match in length
if len(self.elements) != len(other.elements):
raise Exception("Vector sizes are different")
return Vector([self.elements[i] + other.elements[i] for i in range(len(self.elements))])
def subtract(self, other):
# insure that the two vectors match in length
if len(self.elements) != len(other.elements):
raise Exception("Vector sizes are different")
return Vector([self.elements[i] - other.elements[i] for i in range(len(self.elements))])
change:
return self.__class__(self.list)
to:
return self
although this would the same as,
return Vector(self.list)
if the class is more complicated it is better to return self
I think that's the issue, hope it helps :)
also, it is good practice to use different names. you used Vector for the class name as well as many of the inputs of the functions, you will run into problems when you do that.
Please change function toString to str . its' already done.
class Vector :
def __init__(self , lst_vec):
self.lst_vec = lst_vec
def show_vector(self):
return self.lst_vec
def add(self , v ):
size_self = len(self.lst_vec)
size_v = len(v.lst_vec)
new_vector = []
if ( size_self != size_v ):
return Exception("error add")
else:
for i in range(size_self):
new_vector.append(self.lst_vec[i] + v.lst_vec[i])
return Vector(new_vector)
def subtract(self , v ):
size_self = len(self.lst_vec)
size_v = len(v.lst_vec)
new_vector = []
if ( size_self != size_v ):
return Exception("error subtract")
else:
for i in range(size_self):
new_vector.append(self.lst_vec[i] - v.lst_vec[i])
return Vector(new_vector)
def dot(self , v ):
size_self = len(self.lst_vec)
size_v = len(v.lst_vec)
new_vector = []
sum_vec = 0
if ( size_self != size_v ):
return Exception("Vector sizes are different")
else:
for i in range(size_self):
new_vector.append(self.lst_vec[i] * v.lst_vec[i])
for i in range(len(new_vector)):
sum_vec+=new_vector[i]
return sum_vec
def norm (self):
new_vec_sum = 0
for i in range(len(self.lst_vec)):
new_vec_sum +=( self.lst_vec[i] ) **2
return new_vec_sum ** 0.5
def toString(self):
str_self = '('
for i in range(len(self.lst_vec)):
str_self += str(self.lst_vec[i])
if i < (len(self.lst_vec)-1):
str_self+=','
else : pass
str_self+=')'
return str_self
def equals(self , v ):
return self.lst_vec == v.lst_vec
a = Vector([1,2,3])
b = Vector([3,4,5])
c = Vector([5,6,7,8])
print(a.add(b).show_vector())
print( a.add(b).equals(Vector([4,6,8])) )
print(a.subtract(b).show_vector())
print(a.dot(b))
print(a.norm())
print((a.toString() == '(1,2,3)'))
print(c.toString())
I'm trying to use the parent class's __div__() in order to maintain the same type so that many operations can be called at once as in the last example mix1 = bf2/bf4*bf1%bf5 in main() below where multiple arithmetic operations are strung together. For some reason, I can use super() in __add__() but not in __div__(). The error is "IndexError: list index out of range" and I've been going over and over this without any progress. Note that this is all related to polynomial arithmetic within a finite field.
I'm including the parsePolyVariable() and it's dependents (sorry if it looks like there's a bit of code but I assure you it's all for a good cause and builds character), since that's where the list error seems to be stemming from but I can't for the life of me figure out where everything is going very wrong. I'm teaching myself Python, so I'm sure there are some other beginners out there who will see where I'm missing the obvious.
I've been looking over these but they don't seem to be related to this situation:
http://docs.python.org/2/library/functions.html#super
Python super(Class, self).method vs super(Parent, self).method
How can I use Python's super() to update a parent value?
import re
class GF2Polynomial(object): #classes should generally inherit from object
def __init__(self, string):
'''__init__ is a standard special method used to initialize objects.
Here __init__ will initialize a gf2infix object based on a string.'''
self.string = string #basically the initial string (polynomial)
#if self.parsePolyVariable(string) == "0": self.key,self.lst = "0",[0]
#else:
self.key,self.lst = self.parsePolyVariable(string) # key determines polynomial compatibility
self.bin = self.prepBinary(string) #main value used in operations
def id(self,lst):
"""returns modulus 2 (1,0,0,1,1,....) for input lists"""
return [int(lst[i])%2 for i in range(len(lst))]
def listToInt(self,lst):
"""converts list to integer for later use"""
result = self.id(lst)
return int(''.join(map(str,result)))
def parsePolyToListInput(self,poly):
"""
replaced by parsePolyVariable. still functional but not needed.
performs regex on raw string and converts to list
"""
c = [int(i.group(0)) for i in re.finditer(r'\d+', poly)]
return [1 if x in c else 0 for x in xrange(max(c), -1, -1)]
def parsePolyVariable(self,poly):
"""
performs regex on raw string, converts to list.
also determines key (main variable used) in each polynomial on intake
"""
c = [int(m.group(0)) for m in re.finditer(r'\d+', poly)] #re.finditer returns an iterator
if sum(c) == 0: return "0",[0]
letter = [str(m.group(0)) for m in re.finditer(r'[a-z]', poly)]
degree = max(c); varmatch = True; key = letter[0]
for i in range(len(letter)):
if letter[i] != key: varmatch = False
else: varmatch = True
if varmatch == False: return "error: not all variables in %s are the same"%a
lst = [1 if x in c else (1 if x==0 else (1 if x=='x' else 0)) for x in xrange(degree, -1, -1)]
return key,lst
def polyVariableCheck(self,other):
return self.key == other.key
def prepBinary(self,poly):
"""converts to base 2; bina,binb are binary values like 110100101100....."""
x = self.lst; a = self.listToInt(x)
return int(str(a),2)
def __add__(self,other):
"""
__add__ is another special method, and is used to override the + operator. This will only
work for instances of gf2pim and its subclasses.
self,other are gf2infix instances; returns GF(2) polynomial in string format
"""
if self.polyVariableCheck(other) == False:
return "error: variables of %s and %s do not match"%(self.string,other.string)
return GF2Polynomial(self.outFormat(self.bin^other.bin))
def __sub__(self,other):
"""
__sub__ is the special method for overriding the - operator
same as addition in GF(2)
"""
return self.__add__(other)
def __mul__(self,other):
"""
__mul__ is the special method for overriding the * operator
returns product of 2 polynomials in gf2; self,other are values 10110011...
"""
if self.polyVariableCheck(other) == False:
return "error: variables of %s and %s do not match"%(self.string,other.string)
bitsa = reversed("{0:b}".format(self.bin))
g = [(other.bin<<i)*int(bit) for i,bit in enumerate(bitsa)]
return GF2Polynomial(self.outFormat(reduce(lambda x,y: x^y,g)))
def __div__(self,other):
"""
__div__ is the special method for overriding the / operator
returns quotient formatted as polynomial
"""
if self.polyVariableCheck(other) == False:
return "error: variables of %s and %s do not match"%(self.string,other.string)
if self.bin == other.bin: return 1
return GF2Polynomial(self.outFormat(self.bin/other.bin))
def __mod__(self,other):
"""
__mod__ is the special method for overriding the % operator
returns remainder formatted as polynomial
"""
if self.polyVariableCheck(other) == False:
return "error: variables of %s and %s do not match"%(self.string,other.string)
if self.bin == other.bin: return 0
return GF2Polynomial(self.outFormat(self.bin%other.bin))
def __str__(self):
return self.string
def outFormat(self,raw):
"""process resulting values into polynomial format"""
raw = "{0:b}".format(raw); raw = str(raw[::-1]); g = [] #reverse binary string for enumeration
g = [i for i,c in enumerate(raw) if c == '1']
processed = "x**"+" + x**".join(map(str, g[::-1]))
proc1 = processed.replace("x**1","x"); proc2 = proc1.replace("x**0","1")
if len(g) == 0: return 0 #return 0 if list empty
return proc2 #returns result in gf(2) polynomial form
class BinaryField(GF2Polynomial):
def __init__(self, poly, mod):
if mod == "0": self.string = "Error: modulus division by 0"
elif mod == "0": self.string = "%s is 0 so resulting mod is 0"%(poly)
fieldPoly = GF2Polynomial(poly) % mod
if fieldPoly == 0: self.string = "%s and %s are the same so resulting mod is 0"%(poly,mod)
else: super(BinaryField, self).__init__(fieldPoly.string)
#self.degree = len(str(fieldPoly))
def polyFieldCheck(self,other):
return self.degree() == other.degree()
def __add__(self, other):
"""
inherited from GF2Polynomial
"""
return super(BinaryField, self).__add__(other) % min(other,self)
def __sub__(self,other):
"""
inherited from GF2Polynomial
"""
return self.__add__(other)
def __mul__(self, other):
"""
special method of BinaryField, needed for format adjustments between classes
"""
#print "self = %s,%s other = %s,%s "%(self.degree(),type(self.degree()),other.degree(),type(other.degree()))
if self.polyVariableCheck(other) == False:
return "error: variables of %s and %s do not match"%(self.string,other.string)
if self.polyFieldCheck(other) == False:
return "error: fields of %s and %s do not match"%(self.string,other.string)
else: print "Operation will proceed: fields of %s and %s match"%(self.string,other.string)
bitsa = reversed("{0:b}".format(self.bin))
g = [(other.bin<<i)*int(bit) for i,bit in enumerate(bitsa)]
result = reduce(lambda x,y: x^y,g)%min(self.bin,other.bin)
return GF2Polynomial(self.outFormat(result))
def __div__(self, other):
"""
special method of BinaryField, needed for format adjustments between classes
"""
if self.polyVariableCheck(other) == False:
return "error: variables of %s and %s do not match"%(self.string,other.string)
if self.polyFieldCheck(other) == False:
return "error: fields of %s and %s do not match"%(self.string,other.string)
else: print "Operation will proceed: fields of %s and %s match"%(self.string,other.string)
if self.bin == other.bin: return 1
result = self.bin/other.bin
#return self.outFormat(result)
return super(BinaryField, self).__div__(other) #% min(other,self)
def degree(self):
return len(self.lst)-1
And here's the main():
if __name__ == '__main__':
## "x**1 + x**0" polynomial string style input
poly1 = "x**14 + x**1 + x**0"; poly2 = "x**6 + x**2 + x**1"; poly3 = "y**6 + y**2 + y**1"
a = GF2Polynomial(poly1); b = GF2Polynomial(poly2); c = GF2Polynomial(poly3)
## "x+1" polynomial string style input
poly4 = "x**14 + x + 1"; poly5 = "x**6 + x**2 + x"; poly6 = "x**8 + x**3 + 1"
d = GF2Polynomial(poly4); e = GF2Polynomial(poly5); f = GF2Polynomial(poly6)
poly7 = "x**9 + x**5 + 1"; poly8 = "x**11 + x**7 + x**4 + 1"; poly9 = "x**5 + x**4 + x**2 + x"
g = GF2Polynomial(poly7); h = GF2Polynomial(poly8); i = GF2Polynomial(poly9)
## g = GF2Polynomial("x**5 + x**4 + x**3 + 1"); h = GF2Polynomial("x**5 + x"); print "(g*h)%b = ",(g*h)%b
## dd = GF2Polynomial("x**0"); print "dd -- ",dd
## ee = GF2Polynomial("0"); print "ee -- ",ee
bf1 = BinaryField(poly1,b); print bf1; print "degree bf1 = ",bf1.degree()
bf2 = BinaryField(poly4,e); print "bf2 ",bf2; bf3 = BinaryField(poly4,d); print "bf3 ",bf3,type(bf3)
bf4 = BinaryField(poly4,h); bf5 = BinaryField(poly9,e); bf6 = BinaryField(poly8,i)
add1 = bf1+bf2
print "add1 ",add1
div1 = bf1/bf2
print "div1 ",div1,type(div1)
mix1 = bf2*bf1%bf5
print "mix1 ",mix1,type(mix1)
EDIT:
The full traceback --
Message File Name Line Position
Traceback
<module> C:\Users\win7pro-vm\Desktop\crypto\GF2BinaryField.py 233
__div__ C:\Users\win7pro-vm\Desktop\crypto\GF2BinaryField.py 197
__div__ C:\Users\win7pro-vm\Desktop\crypto\GF2BinaryField.py 100
__init__ C:\Users\win7pro-vm\Desktop\crypto\GF2BinaryField.py 20
parsePolyVariable C:\Users\win7pro-vm\Desktop\crypto\GF2BinaryField.py 48
IndexError: list index out of range
For reference line 48 is degree = max(c); varmatch = True; key = letter[0].
Personal notes and information were removed, adjusting the line numbers.
Your return GF2Polynomial(self.outFormat(self.bin/other.bin)) line results in the string 1, which is then passed to the GF2Polynomial.parsePolyVariable() method.
This value has no letters, so the line:
letter = [str(m.group(0)) for m in re.finditer(r'[a-z]', poly)]
returns an empty list. The next line:
degree = max(c); varmatch = True; key = letter[0]
then fails because key = letter[0] gives a IndexError exception.
Your code is hard to read because you use one-letter variables and put multiple statements on one line, so it is hard to make out what your expectations are in that function.
The exception has otherwise nothing to do with super(). There is a simple bug in your own code somewhere.