Failed to show calculation steps with sympy and markdown - python

Question
I want to show the steps of calculation (for example, in text book) in markdown file which is created by a python code. here is what I need in original python code
from sympy import *
angle = 60 # this will be changed to created different markdown files
theta = symbols('ss')
x = symbols('xx')
a = Matrix([[cos(theta), -sin(theta), 0], [sin(theta), cos(theta), 0], [0, 0, 1]])
b = Matrix([[x, 0, 0], [0, x, 1], [0, 0, 0]])
print(
'$$',
latex(a), latex(b), '=', # step 1
latex(a).replace('ss', latex(rad(angle))), latex(b).replace('xx', '2'), '=', # step 2
latex(a.subs('ss', rad(60))), latex(b.subs('xx', '2')), '=', # step 3
latex((a*b).subs({'ss': rad(60), 'xx':2}).evalf(2)), # step 4
'$$'
)
you may find that step 1 lists the common matrix, step 2 substitutes the element of matrix by given value, step 3 calculates/simplifies the matrix and step 4 evaluates the matrix elements to float form.
There are too many calls of latex which make the code too long and hard to read.
First try
I write
from sympy import *
class T_Rotate(object):
def __init__(self, theta):
self.eq = Matrix([[cos(theta), -sin(theta), 0], [sin(theta), cos(theta), 0], [0, 0, 1]])
self.str = latex(self.eq)
def __mul__(self, mat):
return self.eq * mat
def __rmul__(self, mat):
return mat * self.eq
a = T_Rotate(60)
b = Matrix([[1, 0, 0], [0, 1, 1], [0, 0, 0]])
print('$$a*b = %s * %s = %s$$' % (a.str, latex(b), latex(a*b)))
print('$$b * a = %s * %s = %s$$' % (latex(b), a.str, latex(b*a)))
but above a * b is a wrong answer which is a 3*3 matrix but whose elements are all 3*3 matrix!
so, what is the problem?
Further thought
In case the above be fixed, there are still call of latex function. Any hints to wrap sympy expression so that the python code can be more terse?
thanks

Now I wrote https://github.com/retsyo/expand_expression to answer the post partly. But I am also still seeking for a more common method without supplying every functions by the user
I released it for it can help others. Here is a example:
If you have defined a function, then you can use it like
T1 = T_Rotate('pi/6 + pi/2', useRad=True)
fOut.write(latexExpression('T1'))
Is it easy? I think so.

Related

Fail to overwrite a 2D numpy.ndarray in a loop

I found my program failed to overwrite an np.ndarray (the X variable) in the for loop by assignment statement like "X[i] = another np.ndarray with matched shape". I have no idea how this could happen...
Codes:
import numpy as np
def qr_tridiagonal(T: np.ndarray):
m, n = T.shape
X = T.copy()
Qt = np.identity(m)
for i in range(n-1):
ai = X[i, i]
ak = X[i+1, i]
c = ai/(ai**2 + ak**2)**.5
s = ak/(ai**2 + ak**2)**.5
# Givens rotation
tmp1 = c*X[i] + s*X[i+1]
tmp2 = c*X[i+1] - s*X[i]
print("tmp1 before:", tmp1)
print("X[i] before:", X[i])
X[i] = tmp1
X[i+1] = tmp2
print("tmp1 after:", tmp1)
print("X[i] after:", X[i])
print()
print(X)
return Qt.T, X
A = np.array([[1, 1, 0, 0], [1, 1, 1, 0], [0, 1, 1, 1], [0, 0, 1, 1]])
Q, R = qr_tridiagonal(A)
Output (the first 4 lines):
tmp1 before: [1.41421356 1.41421356 0.70710678 0. ]
X[i] before: [1 1 0 0]
tmp1 after: [1.41421356 1.41421356 0.70710678 0. ]
X[i] after: [1 1 0 0]
Though X[i] is assigned by tmp1, the values in the array X[i] or X[i, :] remain unchanged. Hope somebody help me out....
Other info: the above is a function to compute QR factorization for tridiagonal matrices using Givens Rotation.
I did check that assigning constant values to X[i] work, e.g. X[i] = 10 then the printed results fit this statement. But if X[i] = someArray then in my codes it would fail. I am not sure whether this is a particular issue triggered by the algorithm I was implementing in the above codes, because such scenarios never happen before.
I did try to install new environments using conda to make sure that my python is not problematic. The above strange outputs should be able to re-generate on other devices.
Many thanks to #hpaulj
It turns out to be a problem of datatype. The program is ok but the input datatype is int, which results in intermediate trancation errors.
A lesson learned: be aware of the dtype of np.ndarray!

Constrained regression in Python with multiple constraints

I am currently working on setting up a constrained regression in Python using
import statsmodels.api as sm
model = sm.GLM(Y,X)
model.fit_constrained
'''Setting the restrictions on parameters in the form of (R, q), where R
and q are constraints' matrix and constraints' values, respectively. As
for the restriction in the aforementioned regression model, i.e.,
c = b - 1 or b - c = 1, R = [0, 1, -1] and q = 1.'''
function from StatsModel but running into some issues when I try to set it up with multiple constraints. I have seven coefficients, including a constant. I want to set it up so that a weighted sum of dummy 1 and dummy 2 equals zero and a weighted sum of dummy 3 and dummy 4 equals zero. To use a single constraint example,
results = model.fit_constrained(([0, 0, 0, a, b, 0, 0], 0))
where a and b are the weights on dummy 3 and dummy 4 and are variables I've predefined.
If I didn't have the a and b variables, and the dummies were equally weighted, I could just use the syntax
fit_constrained('Dummy1 + Dummy2, Dummy3 + Dummy4')
but when I try to use a similar syntax using
results = model.fit_constrained(([0, 0, 0, a, b, 0, 0], 0),([0, c, d, 0, 0, 0, 0], 0))
I get the error
ValueError: shapes (2,) and (7,6) not aligned: 2 (dim 0) != 7 (dim 0)
Does anyone have any ideas? Thanks so much!
I am still not sure which model you are running (posting a Minimal, Complete, and Verifiable example would certainly help), but the following should work for GLMs. From the docs, we have,
constraints (formula expression or tuple) – If it is a tuple, then the constraint needs to be given by two arrays (constraint_matrix, constraint_value), i.e. (R, q). Otherwise, the constraints can be given as strings or list of strings. see t_test for details.
This implies the function call should be along the following lines,
R = [[0, 0, 0, a, b, 0, 0],
[0, c, d, 0, 0, 0, 0]]
q = [0, 0]
results = model.fit_constrained((R, q))
This should work, but since we do not have your model I do not know for sure if R * params = q, which must hold according to the documentation.

Why SymPy didn't show me the inverse matrix result in the book?

According to the book I'm reading, the inverse matrix of
is
.
Where
a = e^(π*(2/3)*j), like the complex number j, only that the phase of j is 90°, but that of a is 120°.
So I tried this in SymPy:
from sympy import *
a = symbols('a')
T = Matrix([
[1, 1, 1],
[1, a**2, a],
[1, a, a**2]
])
simplify(T.inv())
This is the result in IPython:
which doesn't seem like the inverse matrix in the book at all.
Why did I get this?
And how can I get the result in the book using SymPy?
After your edit, it is clear that a is not a parameter, but rather it has a precise value, that is, -0.5 + i*sqrt(3)/2. If you don't tell SymPy what that value is, it will treat it as a parameter, and the inverted matrix looks like that. But if you give a the right value, then everything works:
from sympy import *
a = -0.5 + I*sqrt(3)/2
T = Matrix([
[1, 1, 1],
[1, a**2, a],
[1, a, a**2]
])
invT = Matrix([
[1, 1, 1],
[1, a, a**2],
[1, a**2, a]
])
simplify(1/3*(T*invT))
and this gives the identity matrix as expected.
This was my original answer:
You can't get the result given by your book, because it's wrong.
Emathelp.net confirms that the result found by SymPy is correct, and symbolab.com shows that the result provided by your book is wrong, because if you multiply A * A-1 you don't get the identity matrix.

Randomize part of an array

I'm working on a project involving binary patterns (here np.arrays of 0 and 1).
I'd like to modify a random subset of these and return several altered versions of the pattern where a given fraction of the values have been changed (like map a function to a random subset of an array of fixed size)
ex : take the pattern [0 0 1 0 1] and rate 0.2, return [[0 1 1 0 1] [1 0 1 0 1]]
It seems possible by using auxiliary arrays and iterating with a condition, but is there a "clean" way to do that ?
Thanks in advance !
The map function works on boolean arrays too. You could add the subsample logic to your function, like so:
import numpy as np
rate = 0.2
f = lambda x: np.random.choice((True, x),1,p=[rate,1-rate])[0]
a = np.array([0,0,1,0,1], dtype='bool')
map(f, a)
# This will output array a with on average 20% of the elements changed to "1"
# it can be slightly more or less than 20%, by chance.
Or you could rewrite a map function, like so:
import numpy as np
def map_bitarray(f, b, rate):
'''
maps function f on a random subset of b
:param f: the function, should take a binary array of size <= len(b)
:param b: the binary array
:param rate: the fraction of elements that will be replaced
:return: the modified binary array
'''
c = np.copy(b)
num_elem = len(c)
idx = np.random.choice(range(num_elem), num_elem*rate, replace=False)
c[idx] = f(c[idx])
return c
f = lambda x: True
b = np.array([0,0,1,0,1], dtype='bool')
map_bitarray(f, b, 0.2)
# This will output array b with exactly 20% of the elements changed to "1"
rate=0.2
repeats=5
seed=[0,0,1,0,1]
realizations=np.tile(seed,[repeats,1]) ^ np.random.binomial(1,rate,[repeats,len(seed)])
Use np.tile() to generate a matrix from the seed row.
np.random.binomial() to generate a binomial mask matrix with your requested rate.
Apply the mask with the xor binary operator ^
EDIT:
Based on #Jared Goguen comments, if you want to change 20% of the bits, you can elaborate a mask by choosing elements to change randomly:
seed=[1,0,1,0,1]
rate=0.2
repeats=10
mask_list=[]
for _ in xrange(repeats):
y=np.zeros(len(seed),np.int32)
y[np.random.choice(len(seed),0.2*len(seed))]=1
mask_list.append(y)
mask = np.vstack(mask_list)
realizations=np.tile(seed,[repeats,1]) ^ mask
So, there's already an answer that provides sequences where each element has a random transition probability. However, it seems like you might want an exact fraction of the elements to change instead. For example, [1, 0, 0, 1, 0] can change to [1, 1, 0, 1, 0] or [0, 0, 0, 1, 0], but not [1, 1, 1, 1, 0].
The premise, based off of xvan's answer, uses the bit-wise xor operator ^. When a bit is xor'd with 0, it's value will not change. When a bit is xor'd with 1, it will flip. From your question, it seems like you want to change len(seq)*rate number of bits in the sequence. First create mask which contains len(seq)*rate number of 1's. To get an altered sequence, xor the original sequence with a shuffled version of mask.
Here's a simple, inefficient implementation:
import numpy as np
def edit_sequence(seq, rate, count):
length = len(seq)
change = int(length * rate)
mask = [0]*(length - change) + [1]*change
return [seq ^ np.random.permutation(mask) for _ in range(count)]
rate = 0.2
seq = np.array([0, 0, 1, 0, 1])
print edit_sequence(seq, rate, 5)
# [0, 0, 1, 0, 0]
# [0, 1, 1, 0, 1]
# [1, 0, 1, 0, 1]
# [0, 1, 1, 0, 1]
# [0, 0, 0, 0, 1]
I don't really know much about NumPy, so maybe someone with more experience can make this efficient, but the approach seems solid.
Edit: Here's a version that times about 30% faster:
def edit_sequence(seq, rate, count):
mask = np.zeros(len(seq), dtype=int)
mask[:len(seq)*rate] = 1
output = []
for _ in range(count):
np.random.shuffle(mask)
output.append(seq ^ mask)
return output
It appears that this updated version scales very well with the size of seq and the value of count. Using dtype=bool in seq and mask yields another 50% improvement in the timing.

build matrix from blocks

I have an object which is described by two quantities, A and B (in real case they can be more than two). Objects are correlated depending on the value of A and B. In particular I know the correlation matrix for A and for B. Just as example:
a = np.array([[1, 1, 0, 0],
[1, 1, 0, 0],
[0, 0, 1, 1],
[0, 0, 1, 1]])
b = np.array([[1, 1, 0],
[1, 1, 1],
[0, 1, 1]])
na = a.shape[0]
nb = b.shape[0]
correlation for A:
so if an element has A == 0.5 and the other equal to A == 1.5 they are fully correlated (red). Otherwise if an element has A == 0.5 and the second item has A == 3.5 they are uncorrelated (blue).
Similarly for B:
Now I want multiply the two correlation matrixes, but I want to obtain as final matrix a matrix with two axis, where the new axes are a folded version of the original axes:
def get_folded_bin(ia, ib):
return ia * nb + ib
here what I am doing:
result = np.swapaxes(np.tensordot(a, b, axes=0), 1, 2).reshape(na* nb, na * nb)
visually:
and in particular this must hold:
for ia1 in xrange(na):
for ia2 in xrange(na):
for ib1 in xrange(nb):
for ib2 in xrange(nb):
assert(a[ia1, ia2] * b[ib1, ib2] == result[get_folded_bin(ia1, ib1), get_folded_bin(ia2, ib2)])
actually my problem is to do it with more quantities (A, B, C, ...) in a general way. Maybe there is also a simpler function within numpy to do that.
np.einsum lets you simplify the tensordot expression a bit:
result = np.einsum('ij,kl->ikjl',a,b).reshape(-1, na * nb)
I don't think there's a way of eliminating the reshape.
It may also be easier to generalize to more arrays, though I wouldn't get carried away with too many iteration variables in one einsum expression.
I think finally I have found a solution:
np.kron(a,b)
and then I can compose with
np.kron(np.kron(a,b), c)

Categories