sympy subs in matrix doesn't change the values - numeric

I have a symbolic matrix that I want to differentiate. I have to substitute numeric values to some of the vars and then to solve with respect to 6 unknowns. My problem is that defining the element of matrix A by lambda and subistituting with subs doesn't change any value in the matrix. When I want retrieve the type of matrix in fact it's shown that it's immutable, which seems quite odd. Here's the code:
def optimalF1():
x,y,z=symbols('x y z', Real=True)
phi,theta,psi=symbols('phi theta psi')
b1x,b1y=symbols('b1x b1y')
b2x,b2y=symbols('b2x b2y')
b3x,b3y=symbols('b3x b3y')
b4x,b4y=symbols('b4x b4y')
b5x,b5y=symbols('b5x b5y')
b6x,b6y=symbols('b6x b6y')
bMat=sym.Matrix(([b1x,b2x,b3x,b4x,b5x,b6x],
[b1y,b2y,b3y,b4y,b5y,b6y],[0,0,0,0,0,0]))
mov=np.array([[x],[y],[z]])
Pi=np.repeat(mov,6,axis=1)
sym.pprint(Pi)
print 'shape of thing Pi', np.shape(Pi)
p1x,p1y,p1z=symbols('p1x,p1y,p1z')
p2x,p2y,p2z=symbols('p2x,p2y,p2z')
p3x,p3y,p3z=symbols('p3x,p3y,p3z')
p4x,p4y,p4z=symbols('p4x,p4y,p4z')
p5x,p5y,p5z=symbols('p5x,p5y,p5z')
p6x,p6y,p6z=symbols('p6x,p6y,p6z')
#legs symbolic array
l1,l2,l3,l4,l5,l6=symbols('l1,l2,l3,l4,l5,l6')
piMat=Matrix(([p1x,p2x,p3x,p4x,p5x,p6x],[p1y,p2y,p3y,\
p4y,p5y,p6y],[p1z,p2z,p3z,p4z,p5z,p6z]))
piMat=piMat.subs('p1z',0)
piMat=piMat.subs('p2z',0)
piMat=piMat.subs('p3z',0)
piMat=piMat.subs('p4z',0)
piMat=piMat.subs('p5z',0)
piMat=piMat.subs('p6z',0)
sym.pprint(piMat)
legStroke=np.array([[l1],[l2],[l3],[l4],[l5],[l6]])
'''redefine the Eul matrix
copy values of Pi 6 times by using np.repeat
'''
r1=[cos(phi)*cos(theta)*cos(psi)-sin(phi)*sin(psi),\
-cos(phi)*cos(theta)*sin(psi)-sin(phi)*cos(psi),\
cos(phi)*sin(theta)]
r2=[sin(phi)*cos(theta)*cos(psi)+cos(phi)*sin(psi),\
-sin(phi)*cos(theta)*sin(psi)+cos(phi)*cos(psi),\
sin(phi)*sin(theta)]
r3= [-sin(theta)*cos(psi),sin(theta)*sin(psi),cos(theta)]
EulMat=Matrix((r1,r2,r3))
print(EulMat)
uvw=Pi+EulMat*piMat
print 'uvw matrix is:\n', uvw, np.shape(uvw)
# check thisout -more elegant and compact form
A=Matrix(6,1,lambda j,i:((uvw[0,j]- \
bMat[0,j])**2+(uvw[1,j]-bMat[1,j])**2+\
(uvw[2,j]-bMat[2,j])**2)-legStroke[j]**2)
print'A matrix before simplification:\n ', A
B=simplify(A)
B=B.subs({'x':1.37,'y':0,'z':0,theta:-1.37,phi:0})
print'A matrix form after substituting:\n',B
So comparing A and B leads to the same output. I don't understand why!

When you use subs with variables that have assumptions, you have to use the symbols not strings. Using strings causes a new generic symbol to be created which does not match the symbol having assumptions so the subs fails.
>>> var('x')
x
>>> var('y',real=True)
y
>>> (x+y).subs('x',1).subs('y',2)
y + 1
Note, too, that to make real symbols you should use real=True not Real=True (lower case r).

Related

Strange roots `using numpy.roots`

Is there something wrong in the evaluation of the polinomial (1-alpha*z)**9 using numpy? For
alpha=3/sqrt(2) my list of coefficients is given in the array
psi_t0 = [1.0, -19.0919, 162.0, -801.859, 2551.5, -5412.55, 7654.5, -6958.99, 3690.56, -869.874]
According to numpy documentation, I have to invert this array in order to compute the zeros, i.e.
psi_t0 = psi_t0[::-1]
Thus giving
a = np.roots(psi_t0)
[0.62765842+0.06979364j 0.62765842-0.06979364j 0.52672941+0.14448097j 0.52672941-0.14448097j 0.42775926+0.13031547j 0.42775926-0.13031547j 0.36690056+0.07504044j 0.36690056-0.07504044j 0.34454214+0.j]
which is completely crap since the roots must be all equal to sqrt(2)/3.
As you take the 9th power you'll find that you create a very "wide" zero, indeed, if you step eps away from the true zero and evaluate you'll get something of O(eps^9). In view of that numerical inaccuracies are all but expected.
>>> np.set_printoptions(4)
>>> print(C)
[-8.6987e+02 3.6906e+03 -6.9590e+03 7.6545e+03 -5.4125e+03 2.5515e+03
-8.0186e+02 1.6200e+02 -1.9092e+01 1.0000e+00]
>>> np.roots(C)
array([0.4881+0.0062j, 0.4881-0.0062j, 0.4801+0.0154j, 0.4801-0.0154j,
0.4681+0.0172j, 0.4681-0.0172j, 0.458 +0.011j , 0.458 -0.011j ,
0.4541+0.j ])
>>> np.polyval(C,_)
array([1.4622e-13+6.6475e-15j, 1.4622e-13-6.6475e-15j,
1.2612e-13+1.5363e-14j, 1.2612e-13-1.5363e-14j,
1.0270e-13+1.3600e-14j, 1.0270e-13-1.3600e-14j,
1.1346e-13+9.7179e-15j, 1.1346e-13-9.7179e-15j,
1.0936e-13+0.0000e+00j])
As you can see the roots numpy returns are "good" in that the polynomial evaluates to something pretty close to zero at these points.

How to find most similar numerical arrays to one array, using Numpy/Scipy?

Let's say I have a list of 5 words:
[this, is, a, short, list]
Furthermore, I can classify some text by counting the occurrences of the words from the list above and representing these counts as a vector:
N = [1,0,2,5,10] # 1x this, 0x is, 2x a, 5x short, 10x list found in the given text
In the same way, I classify many other texts (count the 5 words per text, and represent them as counts - each row represents a different text which we will be comparing to N):
M = [[1,0,2,0,5],
[0,0,0,0,0],
[2,0,0,0,20],
[4,0,8,20,40],
...]
Now, I want to find the top 1 (2, 3 etc) rows from M that are most similar to N. Or on simple words, the most similar texts to my initial text.
The challenge is, just checking the distances between N and each row from M is not enough, since for example row M4 [4,0,8,20,40] is very different by distance from N, but still proportional (by a factor of 4) and therefore very similar. For example, the text in row M4 can be just 4x as long as the text represented by N, so naturally all counts will be 4x as high.
What is the best approach to solve this problem (of finding the most 1,2,3 etc similar texts from M to the text in N)?
Generally speaking, the most widely standard technique of bag of words (i.e. you arrays) for similarity is to check cosine similarity measure. This maps your bag of n (here 5) words to a n-dimensional space and each array is a point (which is essentially also a point vector) in that space. The most similar vectors(/points) would be ones that have the least angle to your text N in that space (this automatically takes care of proportional ones as they would be close in angle). Therefore, here is a code for it (assuming M and N are numpy arrays of the similar shape introduced in the question):
import numpy as np
cos_sim = M[np.argmax(np.dot(N, M.T)/(np.linalg.norm(M)*np.linalg.norm(N)))]
which gives output [ 4 0 8 20 40] for your inputs.
You can normalise your row counts to remove the length effect as you discussed. Row normalisation of M can be done as M / M.sum(axis=1)[:, np.newaxis]. The residual values can then be calculated as the sum of the square difference between N and M per row. The minimum difference (ignoring NaN or inf values obtained if the row sum is 0) is then the most similar.
Here is an example:
import numpy as np
N = np.array([1,0,2,5,10])
M = np.array([[1,0,2,0,5],
[0,0,0,0,0],
[2,0,0,0,20],
[4,0,8,20,40]])
# sqrt of sum of normalised square differences
similarity = np.sqrt(np.sum((M / M.sum(axis=1)[:, np.newaxis] - N / np.sum(N))**2, axis=1))
# remove any Nan values obtained by dividing by 0 by making them larger than one element
similarity[np.isnan(similarity)] = similarity[0]+1
result = M[similarity.argmin()]
result
>>> array([ 4, 0, 8, 20, 40])
You could then use np.argsort(similarity)[:n] to get the n most similar rows.

Plotting function with sums

However I try to plot a function including a sum I get errors. CanĀ“t find any example anywhere.
using PyPlot
x = range(0,stop=2,length=10)
f(x) = x + 1
plot(x,f(x))
for example gives me:
MethodError: no method matching +(::StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}}, ::Int64)
The problem is not the plotting. The problem is that you try to add a number (1) to a range (x). This is also what the error message states. You need to do it element-wise. Like in e.g. matlab this is achieved with .-operations.
There are two possibilities for you in this example.
either f(x) = x .+ 1; plot(x,f(x))
or f(x) = x + 1; plot(x,f.(x))
Take a look at https://docs.julialang.org/en/v1/manual/mathematical-operations/#man-dot-operators-1

Solving an underdetermined scipy.sparse matrix using svd

Problem
I have a set of equations with variables denoted with lowercase variables and constants with uppercase variables as such
A = a + b
B = c + d
C = a + b + c + d + e
I'm provided the information as to the structure of these equations in a pandas DataFrame with two columns: Constants and Variables
E.g.
df = pd.DataFrame([['A','a'],['A','b'],['B','c'],['B','d'],['C','a'],['C','b'],
['C','c'],['C','d'],['C','e']],columns=['Constants','Variables'])
I then convert this to a sparse CSC matrix by using NetworkX
table = nx.bipartite.biadjacency_matrix(nx.from_pandas_dataframe(df,'Constants','Variables')
,df.Constants.unique(),df.Variables.unique(),format='csc')
When converted to a dense matrix, table looks like the following
matrix([[1, 1, 0, 0, 0],[0, 0, 1, 1, 0],[1, 1, 1, 1, 1]], dtype=int64)
What I want from here is to find which variables are solvable (in this example, only e is solvable) and for each solvable variable, what constants is its value dependent on (in this case, since e = C-B-A, it is dependent on A, B, and C)
Attempts at Solution
I first tried to use rref to solve for the solvable variables. I used the symbolics library sympy and the function sympy.Matrix.rref, which gave me exactly what I wanted, since any solvable variable would have its own row with almost all zeros and 1 one, which I could check for row by row.
However, this solution was not stable. Primarily, it was exceedingly slow, and didn't make use of the fact that my datasets are likely to be very sparse. Moreover, rref doesn't do too well with floating points. So I decided to move on to another approach motivated by Removing unsolvable equations from an underdetermined system, which suggested using svd
Conveniently, there is a svd function in the scipy.sparse library, namely scipy.sparse.linalg.svds. However, given my lack of linear algebra background, I don't understand the results outputted by running this function on my table, or how to use those results to get what I want.
Further Details in the Problem
The coefficient of every variable in my problem is 1. This is how the data can be expressed in the two column pandas DataFrame shown earlier
The vast majority of variables in my actual examples will not be solvable. The goal is to find the few that are solvable
I'm more than willing to try an alternate approach if it fits the constraints of this problem.
This is my first time posting a question, so I apologize if this doesn't exactly follow guidelines. Please leave constructive criticism but be gentle!
The system you are solving has the form
[ 1 1 0 0 0 ] [a] [A]
[ 0 0 1 1 0 ] [b] = [B]
[ 1 1 1 1 1 ] [c] [C]
[d]
[e]
i.e., three equations for five variables a, b, c, d, e. As the answer linked in your question mentions, one can tackle such underdetermined system with the pseudoinverse, which Numpy directly provides in terms of the pinv function.
Since M has linearly independent rows, the psudoinverse has in this case the property that M.pinv(M) = I, where I denotes identity matrix (3x3 in this case). Thus formally, we can write the solution as:
v = pinv(M) . b
where v is the 5-component solution vector, and b denotes the right-hand side 3-component vector [A, B, C]. However, this solution is not unique, since one can add a vector from the so-called kernel or null space of the matrix M (i.e., a vector w for which M.w=0) and it will be still a solution:
M.(v + w) = M.v + M.w = b + 0 = b
Therefore, the only variables for which there is a unique solution are those for which the corresponding component of all possible vectors from the null space of M is zero. In other words, if you assemble the basis of the null space into a matrix (one basis vector per column), then the "solvable variables" will correspond to zero rows of this matrix (the corresponding component of any linear combination of the columns will be then also zero).
Let's apply this to your particular example:
import numpy as np
from numpy.linalg import pinv
M = [
[1, 1, 0, 0, 0],
[0, 0, 1, 1, 0],
[1, 1, 1, 1, 1]
]
print(pinv(M))
[[ 5.00000000e-01 -2.01966890e-16 1.54302378e-16]
[ 5.00000000e-01 1.48779676e-16 -2.10806254e-16]
[-8.76351626e-17 5.00000000e-01 8.66819360e-17]
[-2.60659800e-17 5.00000000e-01 3.43000417e-17]
[-1.00000000e+00 -1.00000000e+00 1.00000000e+00]]
From this pseudoinverse, we see that the variable e (last row) is indeed expressible as - A - B + C. However, it also "predicts" that a=A/2 and b=A/2. To eliminate these non-unique solutions (equally valid would be also a=A and b=0 for example), let's calculate the null space borrowing the function from SciPy Cookbook:
print(nullspace(M))
[[ 5.00000000e-01 -5.00000000e-01]
[-5.00000000e-01 5.00000000e-01]
[-5.00000000e-01 -5.00000000e-01]
[ 5.00000000e-01 5.00000000e-01]
[-1.77302319e-16 2.22044605e-16]]
This function returns already the basis of the null space assembled into a matrix (one vector per column) and we see that, within a reasonable precision, the only zero row is indeed only the last one corresponding to the variable e.
EDIT:
For the set of equations
A = a + b, B = b + c, C = a + c
the corresponding matrix M is
[ 1 1 0 ]
[ 0 1 1 ]
[ 1 0 1 ]
Here we see that the matrix is in fact square, and invertible (the determinant is 2). Thus the pseudoinverse coincides with "normal" inverse:
[[ 0.5 -0.5 0.5]
[ 0.5 0.5 -0.5]
[-0.5 0.5 0.5]]
which corresponds to the solution a = (A - B + C)/2, .... Since M is invertible, its kernel / null space is empty, that's why the cookbook function returns only []. To see this, let's use the definition of the kernel - it is formed by all non-zero vectors x such that M.x = 0. However, since M^{-1} exists, x is given as x = M^{-1} . 0 = 0 which is a contradiction. Formally, this means that the found solution is unique (or that all variables are "solvable").
To build on ewcz's answer, both the nullspace and pseudo-inverse can be calculated using numpy.linalg.svd. See the links below:
pseudo-inverse
nullspace

Fast way to set diagonals of an (M x N x N) matrix? Einsum / n-dimensional fill_diagonal?

I'm trying to write fast, optimized code based on matrices, and have recently discovered einsum as a tool for achieving significant speed-up.
Is it possible to use this to set the diagonals of a multidimensional array efficiently, or can it only return data?
In my problem, I'm trying to set the diagonals for an array of square matrices (shape: M x N x N) by summing the columns in each square (N x N) matrix.
My current (slow, loop-based) solution is:
# Build dummy array
dimx = 2 # Dimension x (likely to be < 100)
dimy = 3 # Dimension y (likely to be between 2 and 10)
M = np.random.randint(low=1, high=9, size=[dimx, dimy, dimy])
# Blank the diagonals so we can see the intended effect
np.fill_diagonal(M[0], 0)
np.fill_diagonal(M[1], 0)
# Compute diagonals based on summing columns
diags = np.einsum('ijk->ik', M)
# Set the diagonal for each matrix
# THIS IS LOW. CAN IT BE IMPROVED?
for i in range(len(M)):
np.fill_diagonal(M[i], diags[i])
# Print result
M
Can this be improved at all please? It seems np.fill_diagonal doesn't accepted non-square matrices (hence forcing my loop based solution). Perhaps einsum can help here too?
One approach would be to reshape to 2D, set the columns at steps of ncols+1 with the diagonal values. Reshaping creates a view and as such allows us to directly access those diagonal positions. Thus, the implementation would be -
s0,s1,s2 = M.shape
M.reshape(s0,-1)[:,::s2+1] = diags
If you do np.source(np.fill_diagonal) you'll see that in the 2d case it uses a 'strided' approach
if a.ndim == 2:
step = a.shape[1] + 1
end = a.shape[1] * a.shape[1]
a.flat[:end:step] = val
#Divakar's solution applies this to your 3d case by 'flattening' on 2 dimensions.
You could sum the columns with M.sum(axis=1). Though I vaguely recall some timings that found that einsum was actually a bit faster. sum is a little more conventional.
Someone has has asked for an ability to expand dimensions in einsum, but I don't think that will happen.