Matrix class & Test - python

as part of my studies I created this Matrix class. To test the results I was provided with a 'test.py' file.
My problem now is that the test file always gives me this error:
> --------------------------------------------------------------------------- AttributeError Traceback (most recent call
> last) <ipython-input-7-daeb08264590> in <module>()
> 6 # and then selecting matrix.py
> 7
> ----> 8 import test
>
> /home/workspace/test.py in <module>()
> 76 return True
> 77
> ---> 78 test()
>
> /home/workspace/test.py in test()
> 57 assert equal(m2 * m1, m2_x_m1), "Error in your __mul__ function"
> 58 """
> ---> 59 assert equal(m1_x_m2.inverse(), m1_m2_inv), "Error in your inverse function for the 1 x 1 case"
> 60 assert equal(I2.inverse(), I2), "Error in your inverse function for the 2 x 2 case"
> 61 assert equal(top_ones.T(), left_ones), "Error in your T function (transpose)"
>
> /home/workspace/test.py in equal(m1, m2)
> 68
> 69 def equal(m1, m2):
> ---> 70 if len(m1.g) != len(m2.g): return False
> 71 if len(m1.g[0]) != len(m2.g[0]): return False
> 72 for r1, r2 in zip(m1.g, m2.g):
>
> AttributeError: 'list' object has no attribute 'g'
However when testing the cases myself, it works and show the same result.
matrix.py
import math
from math import sqrt
import numbers
def zeroes(height, width):
"""
Creates a matrix of zeroes.
"""
g = [[0.0 for _ in range(width)] for __ in range(height)]
return Matrix(g)
def identity(n):
"""
Creates a n x n identity matrix.
"""
I = zeroes(n, n)
for i in range(n):
I.g[i][i] = 1.0
return I
class Matrix(object):
# Constructor
def __init__(self, grid):
self.g = grid
self.h = len(grid)
self.w = len(grid[0])
#
# Primary matrix math methods
#############################
def determinant(self):
if not self.is_square():
raise(ValueError, "Cannot calculate determinant of non-square matrix.")
if self.h > 2:
raise(NotImplementedError, "Calculating determinant not implemented for matrices largerer than 2x2.")
if self.h == 1:
return self.g[0][0]
elif self.h == 2:
return (self.g[0][0]*self.g[1][1]-self.g[0][1]*self.g[1][0])
def trace(self):
if not self.is_square():
raise(ValueError, "Cannot calculate the trace of a non-square matrix.")
sum_trace = 0
for i in range(self.h):
for j in range(self.w):
if i == j:
sum_trace = sum_trace + self.g[i][j]
return sum_trace
def inverse(self):
if not self.is_square():
raise(ValueError, "Non-square Matrix does not have an inverse.")
if self.h > 2:
raise(NotImplementedError, "inversion not implemented for matrices larger than 2x2.")
if self.h == 2:
if self.g[0][0] * self.g[1][1] == self.g[0][1] * self.g[1][0]:
return "ad = bc. Therefore Matrix does not have an inverse"
else:
det_A = 1/(self.g[0][0]*self.g[1][1]-self.g[0][1]*self.g[1][0])
inverse = [[det_A*self.g[1][1],det_A*-self.g[0][1]],[det_A*-self.g[1][0],det_A*self.g[0][0]]]
elif self.h == 1:
inverse = [[1/self.g[0][0]]]
def T(self):
matrix_transpose = []
#Iterate through columns (e.g. j=0)
for j in range(self.w):
#Reset row for each itteration
new_row = []
#Iterate through rows (e.g. j = 0, i loops 0;1)
for i in range(self.h):
#i = 0, j = 0; i = 1, j = 0 > new row created out of matrix columns
new_row.append(self.g[i][j])
#new_row appended to matrix_transpose
matrix_transpose.append(new_row)
return matrix_transpose
def is_square(self):
return self.h == self.w
#
# Begin Operator Overloading
############################
def __getitem__(self,idx):
return self.g[idx]
def __repr__(self):
s = ""
for row in self.g:
s += " ".join(["{} ".format(x) for x in row])
s += "\n"
return s
def __add__(self,other):
if self.h != other.h or self.w != other.w:
raise(ValueError, "Matrices can only be added if the dimensions are the same")
matrix_addition = []
for i in range(self.h):
new_row = []
for j in range(self.w):
addition = self.g[i][j] + other.g[i][j]
new_row.append(addition)
matrix_addition.append(new_row)
return Matrix(matrix_addition)
def __neg__(self):
for i in range(self.h):
for j in range(self.w):
self.g[i][j] *= -1
return Matrix(self.g)
def __sub__(self, other):
if self.h != other.h or self.w != other.w:
raise(ValueError, "Matrices can only be substracted if the dimensions are the same")
matrix_substraction = []
for i in range(self.h):
new_row = []
for j in range(self.w):
addition = self.g[i][j] - other.g[i][j]
new_row.append(addition)
matrix_substraction.append(new_row)
return Matrix(matrix_substraction)
def __mul__(self, other):
#dot_product func
def dot_product(vector_one, vector_two):
dot_product = 0
for i in range(len(vector_one)):
dot_product += vector_one[i] * vector_two[i]
return dot_product
#get_row func
def get_row(matrix, row):
return matrix[row]
#get_column func
def get_column(matrix, column_number):
column = []
for i in range(len(matrix)):
column.append(matrix[i][column_number])
return column
result = []
for i in range(self.h):
row_result = []
for j in range(self.w):
vector_one = get_row(self.g, i)
vector_two = get_column(other.g, j)
calulated_dot_product = dot_product(vector_one, vector_two)
row_result.append(calulated_dot_product)
result.append(row_result)
return Matrix(result)
def __rmul__(self, other):
if isinstance(other, numbers.Number):
pass
for i in range(self.h):
for j in range(self.w):
self.g[i][j] = 2 * self.g[i][j]
return Matrix(self.g)
test.py
import matrix as m
def test():
I2 = m.Matrix([
[1, 0],
[0, 1]
])
I2_neg = m.Matrix([
[-1, 0],
[0, -1]
])
zero = m.Matrix([
[0,0],
[0,0]
])
m1 = m.Matrix([
[1,2,3],
[4,5,6]
])
m2 = m.Matrix([
[7,-2],
[-3,-5],
[4,1]
])
m1_x_m2 = m.Matrix([
[ 13, -9],
[ 37, -27]])
m2_x_m1 = m.Matrix([
[ -1, 4, 9],
[-23, -31, -39],
[ 8, 13, 18]])
m1_m2_inv = m.Matrix([
[1.5, -0.5],
[2.0555556, -0.722222222]
])
top_ones = m.Matrix([
[1,1],
[0,0],
])
left_ones = m.Matrix([
[1,0],
[1,0]
])
assert equal(-I2, I2_neg), "Error in your __neg__ function"
assert equal(I2 + I2_neg, zero), "Error in your __add__ function"
assert equal(m1 * m2, m1_x_m2), "Error in your __mul__ function"
assert equal(m2 * m1, m2_x_m1), "Error in your __mul__ function"
assert equal(m1_x_m2.inverse(), m1_m2_inv), "Error in your inverse function for the 1 x 1 case"
assert equal(I2.inverse(), I2), "Error in your inverse function for the 2 x 2 case"
assert equal(top_ones.T(), left_ones), "Error in your T function (transpose)"
assert equal(left_ones.T(), top_ones), "Error in your T function (transpose)"
assert equal(top_ones - left_ones.T(), m.zeroes(2,2)), "Error in your __sub__ function"
assert (4*m.identity(5))[0][0] == 4, "Error in your __rmul__ function"
assert (4*m.identity(5)).trace() == 20 , "Error in your trace function"
print("Congratulations! All tests pass. Your Matrix class is working as expected.")
def equal(m1, m2):
if len(m1.g) != len(m2.g): return False
if len(m1.g[0]) != len(m2.g[0]): return False
for r1, r2 in zip(m1.g, m2.g):
for v1, v2 in zip(r1, r2):
if abs(v1 - v2) > 0.0001:
return False
return True
test()
playground.py (To test it & import both files)
# Run this cell but don't modify it.
%load_ext autoreload
%autoreload 2
from matrix import Matrix, zeroes, identity
# Try running this code. You should get an assertion error.
# You will continue to get assertion errors until all the
# methods in matrix.py are correctly implemented.
# You can open matrix.py by selecting File > Open...
# and then selecting matrix.py
import test
I tried to break the problem down, so I could reduce the code I have to share here with you. But no matter what I tried to change, I got errors back from test.py. I hope anyone from you guys could look into it and maybe has an idea where the problem lies.
/Marc

A quick check in that codebase reveals that the methods inverse and T of the Matrix class do not return Matrix instances, but bare grids (2d-lists); these grids, of course, have no g attribute when the equal function in the test tries to compare them with Matrix instances. Wrap the return values of these two methods into matrixes:
class Matrix(object):
def inverse(self):
# ....
# not: return inverse
return Matrix(inverse)
def T(self):
# ...
# not: return matrix_transpose
return Matrix(matrix_transpose)

Related

A* algorithm TypeError: cannot unpack non-iterable int object

This is the python code which uses A* algorithm for finding solution for 8 puzzle problems, I got some error messages, how can I fix it?(The error message is under the code)
There are several object-oriented programming concepts for Problems class, Node class that are implemented to express the problem solution search that you need to understand in order to make the Python program complete. The priority queue is to make the nodes to be explored to be sorted according to their f-evaluation function score and return the min one as the first node to be searched next.
There is also a memorize function to memorize the heuristic value of state as a look-up table so that you don’t need to calculate the redundant computing of heuristic estimation value, so you can ignore it at this point if you don’t understand.
The components you need to implement is to make the abstract part of the program realizable for 8 -puzzle with the successor methods attached to a problem class which consists of initial state and goal state. Make sure the program can run correctly to generate the solution sequence that move the empty tile so that the 8-puzzle can move "Up", "Down", "Left", "Right", from initial state to goal state.
import math
infinity = math.inf
from itertools import chain
import numpy as np
import bisect
class memoize:
def __init__(self, f, memo={}):
self.f = f
self.memo = {}
def __call__(self, *args):
if not str(args) in self.memo:
self.memo[str(args)] = self.f(*args)
return self.memo[str(args)]
def coordinate(state):
index_state = {}
index = [[0,0], [0,1], [0,2], [1,0], [1,1], [1,2], [2,0], [2,1], [2,2]]
for i in range(len(state)):
index_state[state[i]] = index[i]
return index_state
def getInvCount(arr):
inv_count = 0
empty_value = -1
for i in range(0, 9):
for j in range(i + 1, 9):
if arr[j] != empty_value and arr[i] != empty_value and arr[i] > arr[j]:
inv_count += 1
return inv_count
def isSolvable(puzzle) :
inv_count = getInvCount([j for sub in puzzle for j in sub])
return (inv_count % 2 == 0)
def linear(state):
return sum([1 if state[i] != goal[i] else 0 for i in range(9)])
#memoize
def manhattan(state):
index_goal = coordinate(goal)
index_state = coordinate(state)
mhd = 0
for i in range(9):
for j in range(2):
mhd = abs(index_goal[i][j] - index_state[i][j]) + mhd
return mhd
#memoize
def sqrt_manhattan(state):
index_goal = coordinate(goal)
index_state = coordinate(state)
mhd = 0
for i in range(9):
for j in range(2):
mhd = (index_goal[i][j] - index_state[i][j])**2 + mhd
return math.sqrt(mhd)
#memoize
def max_heuristic(state):
score1 = manhattan(state)
score2 = linear(state)
return max(score1, score2)
class PriorityQueueElmt:
def __init__(self,val,e):
self.val = val
self.e = e
def __lt__(self,other):
return self.val < other.val
def value(self):
return self.val
def elem(self):
return self.e
class Queue:
def __init__(self):
pass
def extend(self, items):
for item in items: self.append(item)
class PriorityQueue(Queue):
def __init__(self, order=min, f=None):
self.A=[]
self.order=order
self.f=f
def append(self, item):
queueElmt = PriorityQueueElmt(self.f(item),item)
bisect.insort(self.A, queueElmt)
def __len__(self):
return len(self.A)
def pop(self):
if self.order == min:
return self.A.pop(0).elem()
else:
return self.A.pop().elem()
# Heuristics for 8 Puzzle Problem
class Problem:
def __init__(self, initial, goal=None):
self.initial = initial; self.goal = goal
def successor(self, state):
reachable = []
def get_key(val):
for key, value in index_state.items():
if val == value:
return key
return -1
def candidate(state, Position):
state = state.copy()
zero_index = state.index(0)
swap_index = state.index(get_key(Position))
state[zero_index], state[swap_index] = state[swap_index], state[zero_index]
return state
index_state = coordinate(state)
zero_position = index_state[0]
move_pair = {"left":[zero_position[0], zero_position[1] - 1],
"right":[zero_position[0], zero_position[1] + 1],
"up":[zero_position[0] - 1, zero_position[1]],
"down":[zero_position[0] + 1, zero_position[1]]
}
for action, position in move_pair.items():
#print(action, position)
if get_key(position) != -1:
reachable.append((action, candidate(state, position)))
#print(reachable)
return reachable
def goal_test(self, state):
return state == self.goal
def path_cost(self, c, state1, action, state2):
return c + 1
def value(self):
abstract
class Node:
def __init__(self, state, parent=None, action=None, path_cost=0, depth =0):
self.parent = parent
if parent:
self.depth = parent.depth + 1
else:
self.depth = 0
self.path_cost = path_cost
self.state = state
if action:
self.action = action
else: self.action = "init"
def __repr__(self):
return "Node state:\n " + str(np.array(self.state).reshape(3,3)) +"\n -> action: " + self.action + "\n -> depth: " + str(self.depth)
def path(self):
x, result = self, [self]
while x.parent:
result.append(x.parent)
x = x.parent
return result
def expand(self, problem):
for (act,n) in problem.successor(self.state):
if n not in [node.state for node in self.path()]:
yield Node(n, self, act,
problem.path_cost(self.path_cost, self.state, act, n))
def graph_search(problem, fringe):
closed = {}
fringe.append(Node(problem.initial,depth=0))
while fringe:
node = fringe.pop()
if problem.goal_test(node.state):
return node
if str(node.state) not in closed:
closed[str(node.state)] = True
fringe.extend(node.expand(problem))
return None
def best_first_graph_search(problem, f):
return graph_search(problem, PriorityQueue(min, f))
def astar_search(problem, h = None):
h = h or problem.h
def f(n):
return max(getattr(n, 'f', -infinity), n.path_cost + h(n.state))
return best_first_graph_search(problem, f)
def print_path(path, method):
print("*" * 30)
print("\nPath: (%s distance)" % method)
for i in range(len(path)-1, -1, -1):
print("-" * 15)
print(path[i])
goal = [1, 2, 3, 4, 5, 6, 7, 8, 0]
# Solving the puzzle
puzzle = [7, 2, 4, 5, 0, 6, 8, 3, 1]
if(isSolvable(np.array(puzzle).reshape(3,3))): # even true
# checks whether the initialized configuration is solvable or not
print("Solvable!")
problem = Problem(puzzle,goal)
path = astar_search(problem, manhattan).path()
print_path(path, "manhattan")
path = astar_search(problem, linear).path()
print_path(path, "linear")
path = astar_search(problem, sqrt_manhattan).path()
print_path(path, "sqrt_manhattan")
path = astar_search(problem, max_heuristic).path()
print_path(path, "max_heuristic")
else :
print("Not Solvable!") # non-even false
TypeError Traceback (most recent call last)
<ipython-input-124-2a60ddc8c009> in <module>
9 problem = Problem(puzzle,goal)
10
---> 11 path = astar_search(problem, manhattan).path()
12 print_path(path, "manhattan")
13
<ipython-input-123-caa97275712e> in astar_search(problem, h)
18 def f(n):
19 return max(getattr(n, 'f', -infinity), n.path_cost + h(n.state))
---> 20 return best_first_graph_search(problem, f)
21
22 def print_path(path, method):
<ipython-input-123-caa97275712e> in best_first_graph_search(problem, f)
12
13 def best_first_graph_search(problem, f):
---> 14 return graph_search(problem, PriorityQueue(min, f))
15
16 def astar_search(problem, h = None):
<ipython-input-123-caa97275712e> in graph_search(problem, fringe)
8 if str(node.state) not in closed:
9 closed[str(node.state)] = True
---> 10 fringe.extend(node.expand(problem))
11 return None
12
<ipython-input-121-e5a968bd54f0> in extend(self, items)
18
19 def extend(self, items):
---> 20 for item in items: self.append(item)
21
22 class PriorityQueue(Queue):
<ipython-input-122-db21613469b9> in expand(self, problem)
69
70 def expand(self, problem):
---> 71 for (act,n) in problem.successor(self.state):
72 if n not in [node.state for node in self.path()]:
73 yield Node(n, self, act,
TypeError: cannot unpack non-iterable int object
I got some error messages, how can I fix it?
There is one error message, The pieces of codes you get in the error message are the stack trace, which might help you to know how the execution got at the final point where the error occurred. In this case that is not so important. The essence of the error is this:
for (act,n) in problem.successor(self.state)
TypeError: cannot unpack non-iterable int object
So this means that the successor method returned an int instead of a list.
Looking at the code for successor, I notice that it intends to return a list called reachable, but there is a return statement right in the middle of the code, leaving the largest part of that code unexecuted (so-called "dead code"):
return state
This statement makes no sense where it is positioned. It seems to be an indentation problem: that return belongs inside the function just above it, like this:
def candidate(state, Position):
state = state.copy()
zero_index = state.index(0)
swap_index = state.index(get_key(Position))
state[zero_index], state[swap_index] = state[swap_index], state[zero_index]
return state # <-- indentation!

How to use recursion inside a class function

I made a class function that deals with matrices or a list within a list. Sort of like a calculator that looks like this:
class RealMat:
def __init__(self, mat):
self.mat = mat
def __add__(self, other):
if len(self.mat) != len(other.mat) or len(self.mat[0]) != len(self.mat[0]):
return None
result = self.mat
for i in range(len(self.mat)):
for j in range(len(self.mat[0])):
result[i][j] = self.mat[i][j] + other.mat[i][j]
return RealMat(result)
def __sub__(self, other):
if len(self.mat) != len(other.mat) or len(self.mat[0]) != len(self.mat[0]):
return None
result = self.mat
for i in range(len(self.mat)):
for j in range(len(self.mat[0])):
result[i][j] = self.mat[i][j] - other.mat[i][j]
return RealMat(result)
def __mul__(self, other):
if isinstance(other, int):
for x in range(len(self.mat)):
for y in range(len(self.mat[0])):
self.mat[x][y] = self.mat[x][y] * other
return RealMat(self.mat)
if len(self.mat[0])!=len(other.mat):
return None
column=0
result=[[0]*len(self.mat) for i in range(len(other.mat[0]))]
for x in range(len(other.mat[0])):
row = 0
for x in range(len(self.mat)):
total = 0
i = 0
for x in range(len(self.mat[0])):
total += self.mat[row][i] * other.mat[i][column]
i += 1
result[row][column] = total
row += 1
column += 1
return RealMat(result)
def __rmul__(self, factor):
for x in range(len(self.mat)):
for y in range(len(self.mat[0])):
self.mat[x][y] = self.mat[x][y] * factor
return RealMat(self.mat)
def __pow__(self, n):
if len(self.mat) != len(self.mat[0]):
return None
if n == 0:
identity = [[1 if i == j else 0 for i in range(len(self.mat))] for j in range(len(self.mat))]
return RealMat(identity)
result = RealMat(self.mat)
for x in range(n-1):
result = result * RealMat(self.mat)
return result
def __eq__(self, other):
if len(self.mat) != len(other.mat) or len(self.mat[0]) != len(other.mat[0]):
return False
for x in range(len(self.mat)):
for y in range(len(self.mat[0])):
if self.mat[x][y] != other.mat[x][y]:
return False
return True
def getMatrixMinor(self,i,j):
return [row[:j] + row[j+1:] for row in (self.mat[:i]+self.mat[i+1:])]
def getMatrixDeternminant(self):
if len(self.mat) == 2:
return self.mat[0][0]*self.mat[1][1]-self.mat[0][1]*self.mat[1][0]
determinant = 0
for c in range(len(self.mat)):
determinant += ((-1)**c)*self.mat[0][c]*getMatrixDeternminant(getMatrixMinor(self.mat,0,c))
return determinant
All the other code works fine and I'm happy with it but I can't seem to make my determinant finder code to work
def getMatrixMinor(self,i,j):
return [row[:j] + row[j+1:] for row in (self.mat[:i]+self.mat[i+1:])]
This code gets me the minor matrix while this:
def getMatrixDeternminant(self):
if len(self.mat) == 2:
return self.mat[0][0]*self.mat[1][1]-self.mat[0][1]*self.mat[1][0]
determinant = 0
for c in range(len(self.mat)):
determinant += ((-1)**c)*self.mat[0][c]*getMatrixDeternminant(getMatrixMinor(self.mat,0,c))
return determinant
Solves the determinant of nxn matrix. However it keeps saying "getMatrixDeternminant is not defined" when I try to recurse it. Maybe because it is inside a class? I just got into OOP so I am not well versed in this. Can someone point out what I did wrong?
edit* I tried using the self.getMatrixDeternminant()
def getMatrixMinor(self,m,i,j):
return [row[:j] + row[j+1:] for row in (m[:i]+m[i+1:])]
def getMatrixDeternminant(self):
if len(self.mat) == 2:
return self.mat[0][0]*self.mat[1][1]-self.mat[0][1]*self.mat[1][0]
determinant = 0
for c in range(len(self.mat)):
determinant += ((-1)**c)*self.mat[0][c]*self.getMatrixDeternminant(self.getMatrixMinor(self.mat,0,c))
return determinant
it says:
TypeError: getMatrixDeternminant() takes 1 positional argument but 2 were given
Edit**: I actually got the code from somewhere and tried to implement it in my class. This is the original code:
def getMatrixMinor(m,i,j):
return [row[:j] + row[j+1:] for row in (m[:i]+m[i+1:])]
def getMatrixDeternminant(m):
#base case for 2x2 matrix
if len(m) == 2:
return m[0][0]*m[1][1]-m[0][1]*m[1][0]
determinant = 0
for c in range(len(m)):
determinant += ((-1)**c)*m[0][c]*getMatrixDeternminant(getMatrixMinor(m,0,c))
return determinant
Your getMatrixDeternminant [sic] calculates the determinant of the current object (self). However, your algorithm requires calculating the determinant of other matrices.
Therefore, first keep the algorithm as it was originally (usually you would make it a static method or keep it outside the class). Then, from your method, call that algorithm passing it your current matrix.

How would I create a matrix in python without importing modules? How would I make a tensor without importing modules?

I am trying to make a matrix and tensor in python without importing any modules such as numpy. Would there be a way to do this?
A matrix is just a list of lists. You can do so as such:
x = [[0, 1, 2, 3, 4],
[1, 2, 3, 4, 5],
[3, 4, 5, 6, 7]]
As far as performing operations without numpy goes, that will be up to you to create functions for likely using nested loops.
It would be a list of lists, e.g.:
matrix = [
[1, 0, 0],
[0, 1, 0],
[0, 0, 0],
]
You would then have to implement all of the mathematical operations (matrix multiplication etc) on top of that data structure.
Python reserves the method name __matmul__ for matrix multiplication
class Matrix:
def __mul__(left, right):
print("__mul__ was called")
def __matmul__(left, right):
print("__MATMUL__ WAS CALLED")
def __rmatmul__(right, left):
print(40*"#")
print("__rmatmul__")
print("left == ", left)
print("right == ", right)
print(40 * "#")
def __imatmul__(total, step):
print("__imatmul__")
a = Matrix()
b = Matrix()
a * b # scalar multiplication __mul__
a # b # matrix multiplication __matmul__
3 # b # matrix multiplication __rmatmul__
a #= b
__imatmul__ is similar to the following:
x = 5
x += 2 # __iadd__(x, 2) `x = x + 2`
x *= 1 # __imul__(x, 3) `x = x * 3`
Whenever you write x * y python attempts to get the definition of multiplication from the left-hand argument first. That is, x * y is initially type(x).__add__(x, y)
However, sometimes the left-hand thing doesn't know how to multiply itself by the right-hand thing.
class K:
pass
a = K()
result = 55*a
result = type(55).__mul__(55, a)
The int class does not know how to multiply together 55 and an instance of class K. If type(left).__mul__(left, right) fails, then the back-up mechanism type(right).__rmul__(right, left) is called. If you write your own matrix class, then 3 __rmatmul__ and __rmul__ are what will be called when you put a scalar multiple out in front, like 88.
m = Matrix()
88 * m # __rmul__(m, 88)
88 # m # __rmatmul__(m, 88)
One way to implement a matrix is as a list of lists:
matrix = [
[1, 0, 0],
[0, 1, 0],
[0, 0, 0],
]
This has several notable disadvantages. One is that it is easy to select a row of the matrix:
matrix[1] == [0, 1, 0]
However, selecting columns would be very inefficient:
def get_column(coli:int):
coli = 1
column = list()
for rowi in range(len(matrix)):
column.append(matrix[rowi][coli])
return column
One solution would be to have two different lists of lists:
one in "row-major" order.
the other in "column-major" order.
row_then_col = [
[[("r0", "c0")], [("r0", "c1")], [("r0", "c2")]],
[[("r1", "c0")], [("r1", "c1")], [("r1", "c2")]],
[[("r2", "c0")], [("r2", "c1")], [("r2", "c2")]],
]
ncols = 3
nrows = 3
col_then_row = list()
for coli in range(ncols):
col_then_row.append([None]*ncols)
for rowi in range(nrows):
col_then_row[coli]
col_then_row[coli][rowi] = row_then_col[rowi][coli]
Then col_then_row[coli] will return a whole column and row_then_col[rowi] will return a whole row. You can fake "pointers" with a list of one element. This is will allow a change in col_then_row to automatically be visible in row_then_col, a vis versa, without updating anything.
row_then_col[1][2][0] = "happy"
print(col_then_row[2][1][0]) # "happy"
There are many algorithms for matrix multiplication. I recommend implementing Strassen's algorithm. It is not the fastest in the world, but it is easier to understand than the truly fast ones.
There are many ways to implement matrices. The beginnings of one implementation is shown below:
import io
import math
import abc
def flatten(container):
for elem in container:
if not hasattr(elem, "__iter__"):
yield elem
else:
it_elem = iter(elem)
subelem = next(it_elem)
if subelem != elem:
yield subelem
for j in flatten(it_elem):
yield j
class MatrixNodeState(abc.ABC):
"""
Abstract Base Class
"""
pass
MatrixNodeState.MatrixNodeState = MatrixNodeState
class MatrixNodeStateNullNullNullClass(MatrixNodeState):
#classmethod
def ERR(cls):
with io.StringIO() as string_stream:
print(
"YOU ARE OFF THE EDGE OF THE MAP!",
"STOP ITERATING!",
file=string_stream
)
msg = string_stream.getvalue()
raise cls.OFF_THE_EDGE_OF_THE_MAP(msg)
class OFF_THE_EDGE_OF_THE_MAP(Exception):
pass
def __getattribute__(self, *args):
type(self).ERR()
def __setattr__(self, *args):
type(self).ERR()
MatrixNodeState.nullnullnull = MatrixNodeStateNullNullNullClass()
class MatrixNodeStateNullNullClass(MatrixNodeState):
def __setattr__(*args):
pass
def __getattribute__(self, *args):
return type(self).nullnullnull
MatrixNodeState.nullnull = MatrixNodeStateNullNullClass()
class MatrixNodeStateNullClass(MatrixNodeState):
"""
This class exists because `None.left = n`
would produce an error
`null.left = k` -----`no operation`.
Does nothing
Does not set the `left` attribute
of the nullnull node.
`x = node.left` returns `nullnull`
"""
def __setattr__(*args):
pass
def __getattribute__(self, *args):
return type(self).nullnull
MatrixNodeState.null = MatrixNodeStateNullClass()
class MatrixNodeStateNonNullNull(MatrixNodeState):
def __init__(self, data):
self.data = data
self.up = type(self).null
self.right = type(self).null
self.down = type(self).null
self.left = type(self).null
def __setattr__(self, key, value):
if isinstance(value, type(self).nullnull):
value = type(self).null
elif isinstance(value, type(self).nullnullnull):
value = type(self).null
super().__setattr__(self, key, value)
MatrixNodeState.MatrixNodeStateNonNullNull = MatrixNodeStateNonNullNull
class MatrixNode:
def __init__(self, data=None):
MatrixNodeState = type(self)
if data:
self.state = MatrixNodeState.MatrixNodeStateNonNullNull(data)
else:
self.state = MatrixNodeState.nullnull
def __getattr__(self, attrname):
return self.state.attrname
def __setattr__(self, attr_name, attr_value):
try:
object.__getattr__(self)
super().__setattr__(self, attr_name, attr_value)
except AttributeError:
setattr(self.state, attr_name, attr_value)
MatrixNode.MatrixNodeState = MatrixNodeState
class Matrix:
"""
"""
MatrixNode = MatrixNode
def __init__(self, xdims, xelems):
"""
Example 1:
m = Matrix([3, 3], [1, 0, 0, 0, 1, 0, 0, 0, 1])
Example 2
m = Matrix([3, 3], [[1, 0, 0], [0, 1, 0], [0, 0, 1]])
"""
MatrixNode = type(self).MatrixNode
idims = tuple(map(int, xdims))
ielems = iter(flatten(xelems))
nrows = idims[0]
ncols = idims[1]
self.d = dict()
try:
elem_count = 0
left_node = MatrixNode.nullnull
up_node = MatrixNode.nullnull
for rowi in range(nrows):
for coli in range(ncols):
ielem = next(ielem)
elem_count += 1
up_node = left_node.up.right
node = MatrixNode(ielem)
self.d[(rowi, coli)] = node
node.left = left_node
left_node.right = node
node.up = up_node
up_node.down = node
left_node = node
except StopIteration:
with io.StringIO() as string_stream:
print(
"Dimensions", idims, "indicated",
"that there should be", math.prod(idims),
"elements.", "Instead, only ", elem_count,
"elements were found.",
file=string_stream
)
msg = string_stream.getvalue()
raise TypeError(msg)
def __getitem__(self, xkey):
ikey = tuple(map(int, iter(flatten(xkey))))
return self.d[ikey].data
def __setitem__(self, xkey, xval):
ikey = tuple(map(int, iter(flatten(xkey))))
self.d[ikey].data = xval
return
def get_column(self, coli):
coli = int(str(coli))
def get_row(self, rowi):
rowi = int(str(rowi))
def __mul__(left, right):
print("__mul__ was called")
raise NotImplementedError()
def __rmul__(right, left):
"""
m = Matrix([1, 2, 3])
88 * m
"""
print("__rmul__ was called")
raise NotImplementedError()
def __matmul__(left, right):
print("__MATMUL__ WAS CALLED")
raise NotImplementedError()
def __rmatmul__(right, left):
print(40*"#")
print("__rmatmul__")
print("left == ", left)
print("right == ", right)
print(40 * "#")
raise NotImplementedError()
def __imatmul__(total, step):
print("__imatmul__")
raise NotImplementedError()
def __str__(self):
raise NotImplementedError()
def __repr__(self):
return type(self) + str(self)
row_then_col = [
[[("r0", "c0")], [("r0", "c1")], [("r0", "c2")]],
[[("r1", "c0")], [("r1", "c1")], [("r1", "c2")]],
[[("r2", "c0")], [("r2", "c1")], [("r2", "c2")]],
]
a = Matrix([3, 3], row_then_col)

How to return a copy of an instance of a class?

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())

`xrange(2**100)` -> OverflowError: long int too large to convert to int

xrange function doesn't work for large integers:
>>> N = 10**100
>>> xrange(N)
Traceback (most recent call last):
...
OverflowError: long int too large to convert to int
>>> xrange(N, N+10)
Traceback (most recent call last):
...
OverflowError: long int too large to convert to int
Python 3.x:
>>> N = 10**100
>>> r = range(N)
>>> r = range(N, N+10)
>>> len(r)
10
Is there a backport of py3k builtin range() function for Python 2.x?
Edit
I'm looking for a complete implementation of "lazy" range(), not just a partial implementation of some of its functionality.
I believe there is no backport (Py 3's completely removed the int/long distinction, after all, but in 2.* it's here to stay;-) but it's not hard to hack your own, e.g....:
import operator
def wowrange(start, stop, step=1):
if step == 0:
raise ValueError('step must be != 0')
elif step < 0:
proceed = operator.gt
else:
proceed = operator.lt
while proceed(start, stop):
yield start
start += step
Edit it appears the OP doesn't just want looping (the normal purpose of xrange, and
range in Py3), but also len and the in operator (the latter does work on the above generator, but slowly -- optimizations are possible). For such richness a class
is better...:
import operator
class wowrange(object):
def __init__(self, start, stop=None, step=1):
if step == 0: raise ValueError('step must be != 0')
if stop is None: start, stop = 0, start
if step < 0:
self.proceed = operator.gt
self.l = (stop-start+step+1)//step
else:
self.proceed = operator.lt
self.l = (stop-start+step-1)//step
self.lo = min(start, stop)
self.start, self.stop, self.step = start, stop, step
def __iter__(self):
start = self.start
while self.proceed(start, self.stop):
yield start
start += self.step
def __len__(self):
return self.l
def __contains__(self, x):
if x == self.stop:
return False
if self.proceed(x, self.start):
return False
if self.proceed(self.stop, x):
return False
return (x-self.lo) % self.step == 0
I wouldn't be surprised if there's an off-by-one or similar glitch lurking here, but, I hope this helps!
Edit again: I see indexing is ALSO required. Is it just too hard to write your own __getitem__? I guess it is, so here it, too, is, served on a silver plate...:
def __getitem__(self, i):
if i < 0:
i += self.l
if i < 0: raise IndexError
elif if i >= self.l:
raise IndexError
return self.start + i * self.step
I don't know if 3.0 range supports slicing (xrange in recent 2.* releases doesn't -- it used to, but that was removed because the complication was ridiculous and prone to bugs), but I guess I do have to draw a line in the sand somewhere, so I'm not going to add it;-).
Okay, here's a go at a fuller reimplementation.
class MyXRange(object):
def __init__(self, a1, a2=None, step=1):
if step == 0:
raise ValueError("arg 3 must not be 0")
if a2 is None:
a1, a2 = 0, a1
if (a2 - a1) % step != 0:
a2 += step - (a2 - a1) % step
if cmp(a1, a2) != cmp(0, step):
a2 = a1
self.start, self.stop, self.step = a1, a2, step
def __iter__(self):
n = self.start
while cmp(n, self.stop) == cmp(0, self.step):
yield n
n += self.step
def __repr__(self):
return "MyXRange(%d,%d,%d)" % (self.start, self.stop, self.step)
# NB: len(self) will convert this to an int, and may fail
def __len__(self):
return (self.stop - self.start)//(self.step)
def __getitem__(self, key):
if key < 0:
key = self.__len__() + key
if key < 0:
raise IndexError("list index out of range")
return self[key]
n = self.start + self.step*key
if cmp(n, self.stop) != cmp(0, self.step):
raise IndexError("list index out of range")
return n
def __reversed__(self):
return MyXRange(self.stop-self.step, self.start-self.step, -self.step)
def __contains__(self, val):
if val == self.start: return cmp(0, self.step) == cmp(self.start, self.stop)
if cmp(self.start, val) != cmp(0, self.step): return False
if cmp(val, self.stop) != cmp(0, self.step): return False
return (val - self.start) % self.step == 0
And some testing:
def testMyXRange(testsize=10):
def normexcept(f,args):
try:
r = [f(args)]
except Exception, e:
r = type(e)
return r
for i in range(-testsize,testsize+1):
for j in range(-testsize,testsize+1):
print i, j
for k in range(-9, 10, 2):
r, mr = range(i,j,k), MyXRange(i,j,k)
if r != list(mr):
print "iter fail: %d, %d, %d" % (i,j,k)
if list(reversed(r)) != list(reversed(mr)):
print "reversed fail: %d, %d, %d" % (i,j,k)
if len(r) != len(mr):
print "len fail: %d, %d, %d" % (i,j,k)
z = [m for m in range(-testsize*2,testsize*2+1)
if (m in r) != (m in mr)]
if z != []:
print "contains fail: %d, %d, %d, %s" % (i,j,k,(z+["..."])[:10])
z = [m for m in range(-testsize*2, testsize*2+1)
if normexcept(r.__getitem__, m) != normexcept(mr.__getitem__, m)]
if z != []:
print "getitem fail: %d, %d, %d, %s" % (i,j,k,(z+["..."])[:10])
From the docs:
Note
xrange() is intended to be simple and fast. Implementations may impose restrictions to achieve this. The C implementation of Python restricts all arguments to native C longs (“short” Python integers), and also requires that the number of elements fit in a native C long. If a larger range is needed, an alternate version can be crafted using the itertools module: islice(count(start, step), (stop-start+step-1)//step).
Alternatively reimplement xrange using generators:
def myxrange(a1, a2=None, step=1):
if a2 is None:
start, last = 0, a1
else:
start, last = a1, a2
while cmp(start, last) == cmp(0, step):
yield start
start += step
and
N = 10**100
len(list(myxrange(N, N+10)))
Edit
Issue 1546078: "xrange that supports longs, etc" on the Python issue tracker contains C patch and pure Python implementation of unlimited xrange written by Neal Norwitz (nnorwitz). See xrange.py
Edit
The latest version of irange (renamed as lrange) is at github.
Implementation based on py3k's rangeobject.c
irange.py
"""Define `irange.irange` class
`xrange`, py3k's `range` analog for large integers
See help(irange.irange)
>>> r = irange(2**100, 2**101, 2**100)
>>> len(r)
1
>>> for i in r:
... print i,
1267650600228229401496703205376
>>> for i in r:
... print i,
1267650600228229401496703205376
>>> 2**100 in r
True
>>> r[0], r[-1]
(1267650600228229401496703205376L, 1267650600228229401496703205376L)
>>> L = list(r)
>>> L2 = [1, 2, 3]
>>> L2[:] = r
>>> L == L2 == [2**100]
True
"""
def toindex(arg):
"""Convert `arg` to integer type that could be used as an index.
"""
if not any(isinstance(arg, cls) for cls in (long, int, bool)):
raise TypeError("'%s' object cannot be interpreted as an integer" % (
type(arg).__name__,))
return int(arg)
class irange(object):
"""irange([start,] stop[, step]) -> irange object
Return an iterator that generates the numbers in the range on demand.
Return `xrange` for small integers
Pure Python implementation of py3k's `range()`.
(I.e. it supports large integers)
If `xrange` and py3k `range()` differ then prefer `xrange`'s behaviour
Based on `[1]`_
.. [1] http://svn.python.org/view/python/branches/py3k/Objects/rangeobject.c?view=markup
>>> # on Python 2.6
>>> N = 10**80
>>> len(range(N, N+3))
3
>>> len(xrange(N, N+3))
Traceback (most recent call last):
...
OverflowError: long int too large to convert to int
>>> len(irange(N, N+3))
3
>>> xrange(N)
Traceback (most recent call last):
...
OverflowError: long int too large to convert to int
>>> irange(N).length() == N
True
"""
def __new__(cls, *args):
try: return xrange(*args) # use `xrange` for small integers
except OverflowError: pass
nargs = len(args)
if nargs == 1:
stop = toindex(args[0])
start = 0
step = 1
elif nargs in (2, 3):
start = toindex(args[0])
stop = toindex(args[1])
if nargs == 3:
step = args[2]
if step is None:
step = 1
step = toindex(step)
if step == 0:
raise ValueError("irange() arg 3 must not be zero")
else:
step = 1
else:
raise ValueError("irange(): wrong number of arguments," +
" got %s" % args)
r = super(irange, cls).__new__(cls)
r._start, r._stop, r._step = start, stop, step
return r
def length(self):
"""len(self) might throw OverflowError, this method shouldn't."""
if self._step > 0:
lo, hi = self._start, self._stop
step = self._step
else:
hi, lo = self._start, self._stop
step = -self._step
assert step
if lo >= hi:
return 0
else:
return (hi - lo - 1) // step + 1
__len__ = length
def __getitem__(self, i): # for L[:] = irange(..)
if i < 0:
i = i + self.length()
if i < 0 or i >= self.length():
raise IndexError("irange object index out of range")
return self._start + i * self._step
def __repr__(self):
if self._step == 1:
return "irange(%r, %r)" % (self._start, self._stop)
else:
return "irange(%r, %r, %r)" % (
self._start, self._stop, self._step)
def __contains__(self, ob):
if type(ob) not in (int, long, bool): # mimic py3k
# perform iterative search
return any(i == ob for i in self)
# if long or bool
if self._step > 0:
inrange = self._start <= ob < self._stop
else:
assert self._step
inrange = self._stop < ob <= self._start
if not inrange:
return False
else:
return ((ob - self._start) % self._step) == 0
def __iter__(self):
len_ = self.length()
i = 0
while i < len_:
yield self._start + i * self._step
i += 1
def __reversed__(self):
len_ = self.length()
new_start = self._start + (len_ - 1) * self._step
new_stop = self._start
if self._step > 0:
new_stop -= 1
else:
new_stop += 1
return irange(new_start, new_stop, -self._step)
test_irange.py
"""Unit-tests for irange.irange class.
Usage:
$ python -W error test_irange.py --with-doctest --doctest-tests
"""
import sys
from nose.tools import raises
from irange import irange
def eq_irange(a, b):
"""Assert that `a` equals `b`.
Where `a`, `b` are `irange` objects
"""
try:
assert a.length() == b.length()
assert a._start == b._start
assert a._stop == b._stop
assert a._step == b._step
if a.length() < 100:
assert list(a) == list(b)
try:
assert list(a) == range(a._start, a._stop, a._step)
except OverflowError:
pass
except AttributeError:
if type(a) == xrange:
assert len(a) == len(b)
if len(a) == 0: # empty xrange
return
if len(a) > 0:
assert a[0] == b[0]
if len(a) > 1:
a = irange(a[0], a[-1], a[1] - a[0])
b = irange(b[0], b[-1], b[1] - b[0])
eq_irange(a, b)
else:
raise
def _get_short_iranges_args():
# perl -E'local $,= q/ /; $n=100; for (1..20)
# > { say map {int(-$n + 2*$n*rand)} 0..int(3*rand) }'
input_args = """\
67
-11
51
-36
-15 38 19
43 -58 79
-91 -71
-56
3 51
-23 -63
-80 13 -30
24
-14 49
10 73
31
38 66
-22 20 -81
79 5 84
44
40 49
"""
return [[int(arg) for arg in line.split()]
for line in input_args.splitlines() if line.strip()]
def _get_iranges_args():
N = 2**100
return [(start, stop, step)
for start in range(-2*N, 2*N, N//2+1)
for stop in range(-4*N, 10*N, N+1)
for step in range(-N//2, N, N//8+1)]
def _get_short_iranges():
return [irange(*args) for args in _get_short_iranges_args()]
def _get_iranges():
return (_get_short_iranges() +
[irange(*args) for args in _get_iranges_args()])
#raises(TypeError)
def test_kwarg():
irange(stop=10)
#raises(TypeError, DeprecationWarning)
def test_float_stop():
irange(1.0)
#raises(TypeError, DeprecationWarning)
def test_float_step2():
irange(-1, 2, 1.0)
#raises(TypeError, DeprecationWarning)
def test_float_start():
irange(1.0, 2)
#raises(TypeError, DeprecationWarning)
def test_float_step():
irange(1, 2, 1.0)
#raises(TypeError)
def test_empty_args():
irange()
def test_empty_range():
for args in (
"-3",
"1 3 -1",
"1 1",
"1 1 1",
"-3 -4",
"-3 -2 -1",
"-3 -3 -1",
"-3 -3",
):
r = irange(*[int(a) for a in args.split()])
assert len(r) == 0
L = list(r)
assert len(L) == 0
def test_small_ints():
for args in _get_short_iranges_args():
ir, r = irange(*args), xrange(*args)
assert len(ir) == len(r)
assert list(ir) == list(r)
def test_big_ints():
N = 10**100
for args, len_ in [
[(N,), N],
[(N, N+10), 10],
[(N, N-10, -2), 5],
]:
try:
xrange(*args)
assert 0
except OverflowError:
pass
ir = irange(*args)
assert ir.length() == len_
try:
assert ir.length() == len(ir)
except OverflowError:
pass
#
ir[ir.length()-1]
#
if len(args) >= 2:
r = range(*args)
assert list(ir) == r
assert ir[ir.length()-1] == r[-1]
assert list(reversed(ir)) == list(reversed(r))
#
def test_negative_index():
assert irange(10)[-1] == 9
assert irange(2**100+1)[-1] == 2**100
def test_reversed():
for r in _get_iranges():
if type(r) == xrange: continue # known not to work for xrange
if r.length() > 1000: continue # skip long
assert list(reversed(reversed(r))) == list(r)
assert list(r) == range(r._start, r._stop, r._step)
def test_pickle():
import pickle
for r in _get_iranges():
rp = pickle.loads(pickle.dumps(r))
eq_irange(rp, r)
def test_equility():
for args in _get_iranges_args():
a, b = irange(*args), irange(*args)
assert a is not b
assert a != b
eq_irange(a, b)
def test_contains():
class IntSubclass(int):
pass
r10 = irange(10)
for i in range(10):
assert i in r10
assert IntSubclass(i) in r10
assert 10 not in r10
assert -1 not in r10
assert IntSubclass(10) not in r10
assert IntSubclass(-1) not in r10
def test_repr():
for r in _get_iranges():
eq_irange(eval(repr(r)), r)
def test_new():
assert repr(irange(True)) == repr(irange(1))
def test_overflow():
lo, hi = sys.maxint-2, sys.maxint+3
assert list(irange(lo, hi)) == list(range(lo, hi))
def test_getitem():
r = irange(sys.maxint-2, sys.maxint+3)
L = []
L[:] = r
assert len(L) == len(r)
assert L == list(r)
if __name__ == "__main__":
import nose
nose.main()
Even if there was a backport, it would probably have to be modified. The underlying problem here is that in Python 2.x int and long are separate data types, even though ints get automatically upcast to longs as necessary. However, this doesn't necessarily happen in functions written in C, depending on how they're written.

Categories

Resources