Question about implementing static Arrays class in python - python

Recently I am studying mit cs6.006. In the recitation 2, sattic arrays are implemented as following:
class Array_Seq:
def __init__(self) -> None:
self.A=[]
self.size=0
def __len__(self): # My question here
return self.size
def __iter__(self):
yield from self.A
def build(self,X): # My question here
self.A=[a for a in X]
self.size=len(self.A)
def get_at(self,i):
return self.A[i]
def set_at(self,i,x):
self.A[i]=x
def _copy_forward(self,i,n,A,j):
for k in range(n):
A[j+k]=self.A[i+k]
def _copy_backward(self,i,n,A,j):
for k in range(n-1,-1,-1):
A[j+k]=self.A[i+k]
def insert_at(self, i, x): # O(n)
n = len(self)
A = [None] * (n + 1)
self._copy_forward(0, i, A, 0)
A[i] = x
self._copy_forward(i, n - i, A, i + 1)
self.build(A)
def delete_at(self, i): # O(n)
n = len(self)
A = [None] * (n - 1)
self._copy_forward(0, i, A, 0)
x = self.A[i]
self._copy_forward(i + 1, n - i - 1, A, i)
self.build(A)
return x
def insert_first(self, x): self.insert_at(0, x)
def delete_first(self): return self.delete_at(0)
def insert_last(self, x): self.insert_at(len(self), x)
def delete_last(self): return self.delete_at(len(self) - 1)
My question
I don't know how the function len() and build() work for the size:
when I use build(), this function will let self.size=len(), however, in the definition of len(), it will return self.size. I don't understand how it works.

Related

Is there a way to use __str__ in a class but returning a list?

When I call a class object, I want it to return a list or any other variable other than string
class Vec:
def __init__(self, data) -> None:
self.data = data #THIS IS A LIST
def __str__(self) -> list:
return self.data
a = Vec([0,1,2,3])
print(a)
#TypeError: __str__ returned non-string (type list)
Instead I get a TypeError, if I set the "self.data" to a string it will work
Is there any Magic Method for this? Thanks in advance
here is the full code in case you need to see it all
note: I am not done finishing it yet and my problem is that for example if I want to multiply Mat with a Vec I would have to do Mat * Vec.data or Mat * Vec.T (T stands for transposed) which I dont like and I would just like to type Mat * Vec in case I dont need to transpose the vector. Sorry for my messy code and my bad explanation of my problem
class MatrixMatchError(Exception):
def __init__(self):
self.message = "Matrix sizes do not match"
super().__init__(self.message)
pass
class MatrixRowColError(Exception):
def __init__(self):
self.message = "The number of columns in the first matrix do not equal to the number of rows in the second."
super().__init__(self.message)
pass
class Vec:
def __init__(self, x, identity=1) -> None:
self.x = x
self.data = self.Create(x, 1, identity)
self.T = self.Transpose()
def Create(self, x, y, identity) -> list:
vec = []
for i in range(y):
vec.append([])
for j in range(x):
vec[i].append(identity)
return vec
def Transpose(self) -> list:
vec = self.Create(len(self.data), len(self.data[0]), 0)
for i in range(len(self.data)):
for j in range(len(self.data[0])):
vec[j][i] = self.data[i][j]
return vec
class Mat:
def __init__(self, x, y=0, identity=1) -> None:
self.x = x
if y == 0:
self.y = x
else:
self.y = y
self.data = self.Create(self.x, self.y, identity)
self.T = self.Transpose()
def __add__(self, data):
if not self.CheckSize(data):
raise MatrixMatchError
mat3 = self.data.copy()
for i in range(len(self.data)):
for j in range(len(self.data[0])):
mat3[i][j] = self.data[i][j] + data[i][j]
return mat3
def __sub__(self, data):
if not self.CheckSize(data):
raise MatrixMatchError
mat3 = self.data.copy()
for i in range(len(self.data)):
for j in range(len(self.data[0])):
mat3[i][j] = self.data[i][j] - data[i][j]
return mat3
def __mul__(self, data):
if not self.CheckRowCol(data):
raise MatrixRowColError
mat3 = self.Create(len(data[0]), len(self.data), 0)
for i in range(len(self.data)):
for j in range(len(data[0])):
for k in range(len(self.data[0])):
mat3[i][j] += self.data[i][k] * data[k][j]
return mat3
def Create(self, x, y, identity) -> list:
mat = []
for i in range(y):
mat.append([])
for j in range(x):
mat[i].append(0)
if x == y:
for i in range(x):
mat[i][i] = identity
return mat
def Transpose(self) -> list:
mat = self.Create(len(self.data), len(self.data[0]), 0)
for i in range(len(self.data)):
for j in range(len(self.data[0])):
mat[j][i] = self.data[i][j]
return mat
def CheckSize(self, data):
if len(self.data) != len(data) or len(self.data[0]) != len(data[0]):
return False
return True
def CheckRowCol(self, data):
if len(data) != len(self.data[0]):
return False
return True
def Transform(self):
pass
def Scale(self):
pass
def Rotate(self):
pass
mat1 = Mat(2)
mat1.data = [[4,5, 4],
[9,3, 7],
[7,1, 3]]
vec = Vec(3)
print(mat1 * vec.T)
The builtin str() function will raise a TypeError if it calls your __str__ and gets something that isn't a string, and you won't convince it to do otherwise.
What you probably want is for Vec.__str__ to return the string representation of data, rather than data itself:
class Vec:
def __init__(self, data: list) -> None:
self.data = data
def __str__(self) -> str:
return str(self.data)
The __str__ method has no bearing on your other use case:
for example if I want to multiply Mat with a Vec I would have to do Mat * Vec.data or Mat * Vec.T (T stands for transposed) which I dont like and I would just like to type Mat * Vec in case I dont need to transpose the vector.
This should instead be addressed in Mat.__mul__ (which should operate on a Vec, not the underlying list) and Vec.T/Vec.Transpose (which should return a Vec, not a list):
class Vec:
...
def Transpose(self) -> 'Vec':
vec = self.Create(len(self.data), len(self.data[0]), 0)
for i in range(len(self.data)):
for j in range(len(self.data[0])):
vec[j][i] = self.data[i][j]
return Vec(vec)
class Mat:
...
def __mul__(self, vec: Vec) -> list[list[int]]:
if not self.CheckRowCol(vec.data):
raise MatrixRowColError
mat3 = self.Create(len(vec.data[0]), len(self.data), 0)
for i in range(len(self.data)):
for j in range(len(vec.data[0])):
for k in range(len(self.data[0])):
mat3[i][j] += self.data[i][k] * vec.data[k][j]
return mat3
Now you can indeed do Mat * Vec or Mat * Vec.T and it will do the right thing.
Note that it would probably be better for Mat.__mul__ to return a Mat, but you haven't provided a constructor that makes it easy for me to implement that part -- nevertheless I hope this gives you a better way to think about your class interfaces and how they can make it easier (or harder) to use the class! Ideally the user of the class shouldn't even be able to access the internals, much less be forced to.
Unrelated to this, I'd suggest spending a bit of time learning about Python's list comprehension syntax, because it'll save you a lot of list allocation boilerplate (i.e. your various Create methods). :)

Errors in a class when trying to call it

I have this code consisting of a class and a subclass. The class is Euler forward, while the second one is Eulers midpoint method. These are for solving an ODE (x'=x(1/2-x)). Now it doesn't seem to work because when I am to call the function, by typing:
Euler=H.solve(6)
where the 6 is the amount of steps, I get attributeerror.
AttributeError: 'int' object has no attribute 'size'
Could anyone help me make my code more robust and working so I could plot the values later on, really don't see whats wrong. My code below:
import numpy as np
class H:
def __init__(self, f):
self._f = f
def initial(self, u0):
self._u0 = u0
def solve(self, time_points):
n = time_points.size
self._t = time_points
self._u = np.zeros(n)
self._u[0] = self._u0
for k in range(n-1):
self._k = k
self._u[k+1] = self.advance()
return self._u, self._t
class F(H):
def ad(self):
u = self._u; t = self._t; f = self._f; k = self._k
dt = t[k+1] - t[k]
u_k12 = u[k] + dt/2 * f(u[k], t[k])
return u[k] + dt * f(u_k12, (t[k] + dt/2) )
I think what's wrong is the way you use the class. Initial value is set with initial method (u0), then you give solve method the list of points. You can use np.linscape to generate midpoint.
np.linspace(0, 3, 31) # 30 points evenly spaced between 0 and 3
So it's like this:
def func(x, y):
return x * y
midpoint = np.linspace(0, 3, 31)
F_ = F(func)
F_.initial(6)
F_.solve(midpoint)
Code:
class H:
def __init__(self, f):
self._f = f
def initial(self, u0):
self._u0 = u0
def solve(self, time_points):
n = time_points.size
self._t = time_points
self._u = np.zeros(n)
self._u[0] = self._u0
for k in range(n-1):
self._u[k+1] = self.advance(k)
return self._u, self._t
def advance(self, k):
....
class F(H):
def advance(self, k):
dt = self._t[k+1] + self._t[k]
u_k12 = self._u[k] + dt/2 * self._f(self._u[k], self._t[k])
return self._u[k] + dt * self._f(u_k12, (self._t[k] + dt/2))

Assigning variable = self creating a copy. Need it to be a reference (pointer)

I am working on creating a matrix class for an assignment I have and usually if I assign a variable as x = self, x is a reference to self and thus all operations are in place. I have a function that reduces the matrix, and as an optional parameter I've added inplace=False such that:
if inplace:
self = A
else:
A = self.copy()
Now normally when I do this, if I were to do an operation such as A += B, self would be modified. However, when I run A.reduce(inplace=True), A is not modified. I've included the full class below and am hoping that someone can tell my why the operations are not happening in place. Thanks in advance.
import numpy as np
class matrix:
def __init__(self, A):
self.value = np.array(A, dtype=np.float)
self.indices = np.arange(self.value.shape[0])
self.shape = self.value.shape
def swap_rows(self, r1, r2):
ind = np.arange(self.value.shape[0])
swap = (r1, r2)
ind[swap[0]] = swap[1]
ind[swap[1]] = swap[0]
temp_ind = self.indices[swap[0]]
self.indices[swap[0]] = self.indices[swap[1]]
self.indices[swap[1]] = temp_ind
self.value = self.value[ind]
def add_rows(self, operations):
# operations = [(c, row1, row2)]
# where operation will be:
# c * row1 + row2 -> row2
for c, row1, row2 in operations:
self.value[row2] += c * self.value[row1]
# ... #
def reduce(self, b_ = None, swap=True, normalize=True, return_steps=False, inplace=False, debug=False):
if inplace:
A = self
else:
A = self.copy()
if b_:
b = b_.copy()
if len(b.shape) == 1:
b.reshape((-1, 1), inplace=True)
if return_steps:
steps = []
# Normalize
if normalize:
A_max = A.row_max()
A /= A_max
if debug:
print("A after normalization:")
print(A)
print("")
if return_steps:
steps.append([('normalize', A_max)])
if b_:
b /= A_max
m, n = A.shape
for col in range(n-1):
# Swap
if swap:
# Check for max value
max_ind = np.argmax(np.abs(A[:, col]))
# Check if max is zero
if np.abs(A[max_ind, col]) < 1e-30:
print('Matrix is singular')
if b_:
return A, b
else:
return A
# Swap if necessary
if max_ind > col:
A.swap_rows(col, max_ind)
if return_steps:
steps.append([('swap', col, max_ind)])
if b_:
b.swap_rows(col, max_ind)
# Get constants
cs = -A[col+1:, col] / A[col, col]
operations = [(c, col, i+col+1) for i, c in enumerate(cs)]
if return_steps:
steps.append(operations)
A.add_rows(operations)
if b_:
b.add_rows(operations)
if debug:
print("A after row operations:")
print(A)
print("")
return_vals = np.array([A, None, None])
if b_:
return_vals[1] = b
if return_steps:
return_vals[2] = steps
if inplace:
return_vals = return_vals[1:]
if return_vals.any():
return tuple(return_vals[return_vals != None])
# ... #
def row_max(self):
return np.array([self[row, i] for row, i in enumerate(np.argmax(np.abs(self.value), axis=1))]).reshape(-1, 1)
# ... #
def copy(self):
return matrix(np.copy(self.value))
def T(self):
return matrix(self.value.T)
def inverse(self):
return matrix(np.linalg.inv(self.value))
def flip(self, axis=None, inplace=False):
if inplace:
self.value = np.flip(self.value, axis=axis)
else:
return matrix(np.flip(self.value, axis=axis))
def reshape(self, shape, inplace=False):
if inplace:
self.value = self.value.reshape(*shape)
else:
return matrix(self.value.reshape(*shape))
def __add__(self, x):
if isinstance(x, matrix):
return matrix(self.value + x.value)
else:
return matrix(self.value + x)
def __sub__(self, x):
if isinstance(x, matrix):
return matrix(self.value - x.value)
else:
return matrix(self.value - x)
def __mul__(self, x):
if isinstance(x, matrix):
return matrix(self.value * x.value)
else:
return matrix(self.value * x)
def __truediv__(self, x):
if isinstance(x, matrix):
return matrix(self.value / x.value)
else:
return matrix(self.value / x)
# ... #
def __matmul__(self, A):
if isinstance(A, matrix):
return matrix(self.value # A.value)
else:
return matrix(self.value # A)
def __repr__(self):
return str(self.value)
def __getitem__(self, item):
return self.value[item]
def __setitem__(self, i, v):
self.value[i] = v
A = matrix([ [ 5, 6, 7, 5, -1],
[ 8, -4, -1, 0, -3],
[ 2, 1, -1, 3, 6],
[-9, 10, 1, -4, 6],
[ 9, 5, -5, -8, 4] ])
print("Original A:")
print(A)
print("")
A.reduce(inplace=True, debug=True)
print("A after inplace reduce function:")
print(A)
print("")
EDIT
Here is what I am trying to recreate in a simplistic way:
class obj:
def __init__(self, value):
self.value = value
def copy(self):
return obj(self.value)
def op(self, y, inplace=False):
if inplace:
x = self
else:
x = self.copy()
x.value += y
x.value /= y
if not inplace:
return x
def __repr__(self):
return str(self.value)
x = obj(5)
x.op(3)
print("Copy:", x)
x.op(3, inplace=True)
print("Inplace:", x)
You say that operators like += modify objects in place, but that's not always true. It only happens if the type of the object on the left side of the operator has an __iadd__ method. If it only has an __add__ method, then the Python interpreter translates X += Y to X = X + Y which is generally not an in-place operation.
So the reason your code doesn't do what you expect is because you don't have an __itruediv__ operator, and when you call A /= A_max (if normalize is True), you make a copy, despite your intention to be operating in place.

Multivariable Cumulants and Moments in python

In Mathematica I can convert multivariable moments in cumulants and back using MomentConvert:
MomentConvert[Cumulant[{2, 2,1}], "Moment"] // TraditionalForm
as one can try in wolframcloud.
I would like to do exactly the same in python. Is there any library in python capable of this?
At least the one direction I now programmed by myself:
# from http://code.activestate.com/recipes/577211-generate-the-partitions-of-a-set-by-index/
from collections import defaultdict
class Partition:
def __init__(self, S):
self.data = list(S)
self.m = len(S)
self.table = self.rgf_table()
def __getitem__(self, i):
#generates set partitions by index
if i > len(self) - 1:
raise IndexError
L = self.unrank_rgf(i)
result = self.as_set_partition(L)
return result
def __len__(self):
return self.table[self.m,0]
def as_set_partition(self, L):
# Transform a restricted growth function into a partition
n = max(L[1:]+[1])
m = self.m
data = self.data
P = [[] for _ in range(n)]
for i in range(m):
P[L[i+1]-1].append(data[i])
return P
def rgf_table(self):
# Compute the table values
m = self.m
D = defaultdict(lambda:1)
for i in range(1,m+1):
for j in range(0,m-i+1):
D[i,j] = j * D[i-1,j] + D[i-1,j+1]
return D
def unrank_rgf(self, r):
# Unrank a restricted growth function
m = self.m
L = [1 for _ in range(m+1)]
j = 1
D = self.table
for i in range(2,m+1):
v = D[m-i,j]
cr = j*v
if cr <= r:
L[i] = j + 1
r -= cr
j += 1
else:
L[i] = r // v + 1
r %= v
return L
# S = set(range(4))
# P = Partition(S)
# for x in P:
# print (x)
# using https://en.wikipedia.org/wiki/Cumulant#Joint_cumulants
import math
def Cum2Mom(arr, state):
def E(op):
return qu.expect(op, state)
def Arr2str(arr,sep):
r = ''
for i,x in enumerate(arr):
r += str(x)
if i<len(arr)-1:
r += sep
return r
if isinstance( arr[0],str):
myprod = lambda x: Arr2str(x,'*')
mysum = lambda x: Arr2str(x,'+')
E=lambda x: 'E('+str(x)+')'
myfloat = str
else:
myfloat = lambda x: x
myprod = np.prod
mysum = sum
S = set(range(len(arr)))
P = Partition(S)
return mysum([
myprod([myfloat(math.factorial(len(pi)-1) * (-1)**(len(pi)-1))
,myprod([
E(myprod([
arr[i]
for i in B
]))
for B in pi])])
for pi in P])
print(Cum2Mom(['a','b','c','d'],1) )
import qutip as qu
print(Cum2Mom([qu.qeye(3) for i in range(3)],qu.qeye(3)) )
It's designed to work with qutip opjects and it also works with strings to verify the correct separation and prefactors.
Exponents of the variables can be represented by repeating the variable.

Updating object values in class

I have a question about how to return the update value of an object in a class and then use that in another function in the same class. Here is my old code.
class Vector:
def __init__(self, a):
self.a = a
assert type(self.a) == list
for i in self.a:
assert type(i) == int or type(i) == float
def dim(self):
return len(self.a)
def __getitem__(self, i):
assert i >= 1 and i <= self.dim()
return self.a[i-1]
def __setitem__(self, i, x):
assert i >= 1 and i <= self.dim()
self.a[i-1] = x
return self.a[i-1]
def __str__(self):
return 'Vector: ' + str(self.a)
def __add__(self, other):
assert type(other.a) == list and other.dim() == self.dim()
n = []
for j in range(self.dim()):
n.append(self.a[j]+other.a[j])
self.a = n
return self.a
so when i'm running this test case:
v1 = Vector([2, 3, 4])
v2 = Vector([1, 2, 3])
str(v1 + v2)
my output is '[3, 5, 7]' which means it is only following return self.a and not the __str__ function however i want my output to be 'Vector: [3, 5, 7]' as it should be following the __str__ function. I fixed this by returning Vector(self.a) in the __add__ function but i dont know why this works. Can anyone explain why that works, and why return self.a does not simply update the object value and run the __str__ function instead?
Note: Python uses following equivalent notations:
v[i] == v.__getitem__(i)
v[i] = x == v.__setitem__(i, x)
str(v) == v.__str__()
v + other == v.__add__(other)

Categories