A + B = 8
B + D = 8
A + C = 13
C - D = 6
How to find the values of A, B, C and D?
I assumed the values would be integers and positive and did this:
a = range(0,14)
b = c = d = a
for i in a:
for x in b:
for y in c:
for z in d:
if (a[i] + b[x] == 8 and a[i] + c[y] == 13 and b[x] + d[z] == 8 and c[y]-d[z]==6):
print(a[i],b[x],c[y],d[z])
But that does not work. Even then I extend range to a = range(-100,100).
After solving the equation by hand (with Google's help) I know that floats are involved, e.g. A = 3.5 etc.
But then how to solve it with Python.
If you know linear algebra, you can frame the question as a system of equations, which is then trivial to solve using a freely-available and popular library called numpy (hat tip #Griboullis):
import numpy as np
A = [[1, 1, 0, 0],
[0, 1, 0, 1],
[1, 0, 1, 0],
[0, 0, 1, -1]]
b = [8, 8, 13, 6]
answer = np.linalg.solve(A, b)
If you want a refresher at the matrix math/linear algebra behind this python solution, you can check out https://www.mathsisfun.com/algebra/systems-linear-equations-matrices.html.
There's no need to learn matrix theory (at least not for this).
>>> from sympy import *
>>> var('A B C D')
(A, B, C, D)
>>> solve([A+B-8,B+D-8,A+C-13,C-D-6])
{B: 9/2, D: 7/2, C: 19/2, A: 7/2}
You just need to express each equation such as A+B=8 in the form A+B-8=0 and then omit the '=0' part.
Related
I am trying to add two arrays of different dimensions. Array A has shape (20,2,2,2,2,3) and array B has shape (20). Currently, I am using np.newaxis 5 times, so B gets the same shape as A and then I add them. In my actual code A is much bigger and this forces me to write np.newaxis many times. Is there a way to avoid repeating the np.newaxis and just tell python to give B the same shape as A?
A = np.zeros([20,2,2,2,2,3])
B = np.arange(1,21)
B = B[:,np.newaxis,np.newaxis,np.newaxis,np.newaxis,np.newaxis]
C = A + B
If you are broadcasting, this will work:
A= np.zeros([20,2,2,2,2,3])
B = np.arange(1,21)
B = B.reshape([20,1,1,1,1,1])
C = A + B
In a more dynamic way:
shape_a = [20,2,2,2,2,3]
A= np.zeros(shape_a)
B = np.arange(1,21)
shape_b = [shape_a[0]] +(len(shape_a)-1)*[1]
B = B.reshape(shape_b)
C = A + B
With no broadcasting:
A = np.zeros([20,2,2,2,2,3])
B = np.arange(1,21)
C = A.copy()
C[:,0,0,0,0,0] += B
And if you don't care about A, just the result:
C = np.zeros([20,2,2,2,2,3])
B = np.arange(1,21)
C[:,0,0,0,0,0] += B
What you are doing is broadcasting to the number of dimensions of A, but if you look carefully, this operation you did does not make B have the same shape as A. Indeed they are still different:
>>> B[:, None, None, None, None, None].shape
(20, 1, 1, 1, 1, 1)
So this is basically applying np.expand_dims consequently five times. An alternative way is to reshape the array with extra singletons:
>>> B.reshape((-1, *(1,)*(A.ndim-1))).shape
(20, 1, 1, 1, 1, 1)
Which will reshape (*,) to (*, 1, 1, 1, 1, 1).
This has the same effect as placing the np.newaxis manually.
For your case, you can also do:
C = (A.reshape(A.shape[0], -1) + B[:,None]).reshape(A.shape)
Or swap the common axis to the end:
C = (A.swapaxes(0,-1) + B).swapaxes(0,-1)
Equivalent transform for vectorized solution
For a given symmetric 4x4 matrix Q and a 3x4 matrix P the 3x3 matrix C is obtained through
C=P # Q # P.T
It can be shown that the output C will be symmetric again. The same problem can be formulated using only the unique elements in Q and C exploiting their symmetry. To do so, the matrices are vectorized as seen below.
I want to construct a matrix B that maps the vectorized matrices onto each other like so:
c = B # q
B must be a 6x10 and should be constructable from P only. How can I get B from P?
I tried this, but it doesnt seem to work. Maybe someone has experienced a similar problem?
import numpy as np
def vectorize(A, ord='c'):
"""
Symmetric matrix to vector e.g:
[[1, 2, 3],
[2, 4, 5],
[3, 5, 6]] -> [1, 2, 3, 4, 5, 6] (c-order, row-col)
-> [1, 2, 4, 3, 5, 6] (f-order, col-row)
"""
# upper triangle mask
m = np.triu(np.ones_like(A, dtype=bool)).flatten(order=ord)
return A.flatten(order=ord)[m]
def B(P):
B = np.zeros((6, 10))
counter = 0
# the i,j entry in C depends on the i, j columns in P
for i in range(3):
for j in range(i, 3):
coeffs = np.outer(P[i], P[j])
B[counter] = vectorize(coeffs)
counter += 1
return B
if __name__ == '__main__':
# original transform
P = np.arange(12).reshape((3, 4))
# calculated transform for vectorized matrix
_B = B(P)
# some random symmetric matrix
Q = np.array([[1, 2, 3, 4],
[2, 5, 6, 7],
[3, 6, 8, 9],
[4, 7, 9, 10]])
# if B is an equivilant transform to P, these should be similar
C = P # Q # P.T
c = _B # vectorize(Q)
print(f"q: {vectorize(Q)}\n"
f"C: {vectorize(C)}\n"
f"c: {c}")
Output:
q: [ 1 2 3 4 5 6 7 8 9 10]
C: [ 301 949 2973 1597 4997 8397]
c: [ 214 542 870 1946 3154 5438] <-- not the same
import numpy as np
def vec_from_mat(A, order='c'):
"""
packs the unique elements of symmetric matrix A into a vector
:param A: symmetric matrix
:return:
"""
return A[np.triu_indices(A.shape[0])].flatten(order=order)
def B_from_P(P):
"""
returns a 6x10 matrix that maps the 10 unique elements of a symmetric 4x4 matrix Q on the 6 unique elements of a
3x3 matrix C to linearize the equation C=PTQP to c=Bv
:param P: 3x4 matrix
:return: B with shape (6, 10)
"""
n, m = P.shape
b1, b2 = (n * (n + 1) // 2), (m * (m + 1) // 2)
B = np.zeros((b1, b2))
for a, (i, j) in enumerate(zip(*np.triu_indices(n))):
coeffs = np.outer(P[i], P[j])
# collect coefficients from lower and upper triangle of symmetric matrix
B[a] = vec_from_mat(coeffs) + vec_from_mat(np.triu(coeffs.T, k=1))
return B
Consider it:
X = [1, 2, 3]
p = np.poly1d(X)
print('x: ', X, 'y: ', p(X))
output >> x: [1, 2, 3] y: [ 6 11 18]
what if I want to find x based on y?
x: [?, ?, ?] y: [ 6 11 18]
np.poly1d(X) means you create a polynomial of the required degree where X are its coefficients. Let's call them a, b, and c. So practically you have the expression
a*x**2 + b*x + c
When you then pass these three values for x, you get the following 3 equations
a**3 + b*a + c = 6
a*b**2 + b**2 + c = 11
a*c**2 + b*c + c = 18
There might be an algebraic way you can solve them yourself, but after a quick think I didn't come up with anything. However, sympy will happily solve this system of equations for you.
import numpy as np
import sympy as sym
def generate_y(X):
return np.poly1d(X)(X)
def solve_x(Y):
a, b, c = sym.symbols('a b c')
e1 = sym.Eq(a**3 + b*a + c, Y[0])
e2 = sym.Eq(a*b**2 + b**2 + c, Y[1])
e3 = sym.Eq(a*c**2 + b*c + c, Y[2])
return sym.solvers.solve([e1, e2, e3], [a, b, c])
For example
>>> solve_x(generate_y([1, 2, 3]))
[(1, 2, 3)]
>>> solve_x(generate_y([-5, 105, 2]))
[(-5, 105, 2)]
You could generalise this for nth order polynomials by creating the symbolic expressions dynamically, but for higher order you'll run into problems (such is life) and for 1st order you'll have multiple solutions.
def solve_x(Y):
symbols = sym.symbols('a:z')[:len(Y)]
X = sym.symbols('X')
expr = sum(s*X**i for i, s in enumerate(symbols[::-1]))
eqns = [sym.Eq(expr.subs({X: s}), y) for s, y in zip(symbols, Y)]
return sym.solvers.solve(eqns, symbols)
Usage
>>> solve_x(generate_y([1, 2]))
[(1, 2), (-1 + sqrt(2), 2*sqrt(2)), (-sqrt(2) - 1, -2*sqrt(2))]
>>> solve_x(generate_y([1, 2, 3, 4]))
# still computing...
Hey so I'm working on this code for a material analysis. I have a matrix generated for each layer of the material and I want to save each of these matrices as their own element. The way I was doing this was by saving it to a dictionary. I then form one matrix by summing all the values of the dictionary. Now I do this for three different conditions which leaves me with 3 matrices: A, B, and D. I want to make a matrix of all of these so that it looks like:
| A B |
| B D |
However I can't get it to print properly as it always says matrix: then one of the matrices such as A. It prints the second matrix, B, on the third line where A ended instead of being next to A. I also need to perform future operations on this massive matrix so I'm wondering what the best way to go about that would be. This is a part of my code:
Qbars = {}
for i in plies:
Qbar11 = Q11 * math.cos(float(thetas[j]))**4 + Q22 *math.sin(float(thetas[j]))**4 + \
2 * (Q12 + 2 * Q66) * math.sin(float(thetas[j]))**2 * math.cos(float(thetas[j]))**2
Qbar22 = Q11 * math.sin(float(thetas[j]))**4 + Q22 *math.cos(float(thetas[j]))**4 + \
2 * (Q12 + 2 * Q66) * math.sin(float(thetas[j]))**2 * math.cos(float(thetas[j]))**2
Qbar12 = (Q11 + Q22 - 4 * Q66) * math.sin(float(thetas[j]))**2 * \
math.cos(float(thetas[j]))**2 + Q12 * (math.cos(float(thetas[j]))**4 + \
math.sin(float(thetas[j]))**4)
Qbar66 = (Q11 + Q22 - 2 * Q12 - 2 * Q66) * math.sin(float(thetas[j]))**2 * \
math.cos(float(thetas[j])) **2 + Q66 * (math.sin(float(thetas[j]))**4 + \
math.cos(float(thetas[j]))**4)
Qbar16 = (Q11 - Q12 - 2 * Q66) * math.cos(float(thetas[j]))**3 * \
math.sin(float(thetas[j])) - (Q22 - Q12 - 2 * Q66) * math.cos(float(thetas[j])) * \
math.sin(float(thetas[j]))**3
Qbar26 = (Q11 - Q12 - 2 * Q66) * math.cos(float(thetas[j])) * \
math.sin(float(thetas[j]))**3 - (Q22 - Q12 - 2 * Q66) * \
math.cos(float(thetas[j]))**3 * math.sin(float(thetas[j]))
Qbar = np.matrix ([[Qbar11, Qbar12, Qbar16], [Qbar12, Qbar22, Qbar26], \
[Qbar16, Qbar26, Qbar66]])
Qbars[i] = Qbar
if len(thetas) == 1:
j = 0
else:
j = j + 1
k=0
Alist = {}
for i in plies:
Alist[i] = Qbars[i].dot(h[k])
if len(h) == 1:
k = 0
else:
k = k + 1
A = sum(Alist.values())
ABD = ([A, B],[B, D])
print ABD
One of the next operations I intend to perform would be to multiply the matrix by a 6x1 array that would look like such:
| Nx | | A A A B B B |
| Ny | | A A A B B B |
| Nxy| | A A A B B B |
------ * ----------------
| Mx | | B B B D D D |
| My | | B B B D D D |
| Mxy| | B B B D D D |
What would be the best way to go about doing this?
EDIT: I made this shorter code to reproduce what I'm dealing with, I couldn't think of how to make it even smaller.
import os
import numpy as np
import math
os.system('cls')
ang = raw_input("ENTER 0 (SPACE) 45 ")
thetas = [int(i) for i in ang.split()]
x = 40
h = [3, 5]
y = [1,2]
j = 0
Qbars = {}
for i in y:
theta = [thetas[j] * math.pi / 180]
Q = math.sin(float(thetas[j]))
Qbar = np.matrix ([[Q, Q, Q], [Q, Q, Q], [Q, Q, Q]])
Qbars[i] = Qbar
if len(thetas) == 1:
j = 0
else:
j = j + 1
print Qbars
k=0
Alist = {}
for i in y:
Alist[i] = Qbars[i].dot(h[k])
if len(h) == 1:
k = 0
else:
k = k + 1
A = sum(Alist.values())
AAAA = ([A, A], [A, A])
print AAAA
test = raw_input("Press ENTER to close")
As others have noted, the matrix class is pretty much deprecated by now. They are more limited than ndarrays, with very little additional functionality. The main reason why people prefer to use numpy matrices is that linear algebra (in particular, matrix multiplication) works more naturally for matrices.
However, as far as I can tell you're using np.dot rather than the overloaded arithmetic operators of the matrix class to begin with, so you would not see any loss of functionality from using np.array instead. Furthermore, if you would switch to python 3.5 or newer, you could use the # matrix multiplication operator that would let you write things such as
Alist[i] = Qbars[i] # h[k]
In the following I'll use the ndarray class instead of the matrix class for the above reasons.
So, your question has two main parts: creating your block matrix and multiplying the result with a vector. I suggest using an up-to-date numpy version, since there's numpy.block introduced in version 1.13. This conveniently does exactly what you want it to do:
>>> import numpy as np
>>> A,B,C = (np.full((3,3),k) for k in range(3))
>>> A
array([[0, 0, 0],
[0, 0, 0],
[0, 0, 0]])
>>> B
array([[1, 1, 1],
[1, 1, 1],
[1, 1, 1]])
>>> C
array([[2, 2, 2],
[2, 2, 2],
[2, 2, 2]])
>>> np.block([[A,B],[B,C]])
array([[0, 0, 0, 1, 1, 1],
[0, 0, 0, 1, 1, 1],
[0, 0, 0, 1, 1, 1],
[1, 1, 1, 2, 2, 2],
[1, 1, 1, 2, 2, 2],
[1, 1, 1, 2, 2, 2]])
Similarly, you can concatenate your two 3-length vectors using np.concatenate or one of the stacking methods (these are available in older versions too).
Now, the problem is that you can't multiply a matrix of shape (6,1) with a matrix of shape (6,6), so the question is what you're really trying to do here. In case you want to multiply each element of your matrix with the corresponding row of your vector, you can just multiply your arrays (of class np.ndarray!) and make use of array broadcasting:
>>> Q = np.block([[A,B],[B,C]]) # (6,6)-shape array
>>> v = np.arange(6).reshape(-1,1) # (6,1)-shape array
>>> v * Q
array([[ 0, 0, 0, 0, 0, 0],
[ 0, 0, 0, 1, 1, 1],
[ 0, 0, 0, 2, 2, 2],
[ 3, 3, 3, 6, 6, 6],
[ 4, 4, 4, 8, 8, 8],
[ 5, 5, 5, 10, 10, 10]])
The other option is that you want to do matrix-vector multiplication, but then either you have to transpose your vector (in order to multiply it with the matrix from the right) or swap the order of the matrix and the vector (multiplying the vector with the matrix from the left). Example for the former:
>>> v.T # Q # python 3.5 and up
array([[12, 12, 12, 27, 27, 27]])
>>> v.T.dot(Q)
array([[12, 12, 12, 27, 27, 27]])
Another benefit of arrays (rather than matrices) is that arrays can be multidimensional. Instead of putting numpy arrays inside a dict and summing them that way, you could define a 3d array (a collection of 2d arrays along a third axis), then you could sum along the third dimension. One huge benefit of numpy is its efficient memory need and performance, and these aspects are strongest if you use numpy objects and methods all through your code. Mixing native python objects (such as dicts, zips, loops) typically hinders performance.
Assume I have the following arrays:
N = 8
M = 4
a = np.zeros(M)
b = np.random.randint(M, size=N) # contains indices for a
c = np.random.rand(N) # contains random values
I want to sum the values of c according to the indices provided in b, and store them in a. Writing a loop for this is trivial:
for i, v in enumerate(b):
a[v] += c[i]
Since N can get quite big in my real-world problem I'd like to avoid using python loops, but I can't figure out how to write it as a numpy-statement. Can anyone help me out?
Ok, here some example values:
In [27]: b
Out[27]: array([0, 1, 2, 0, 2, 3, 1, 1])
In [28]: c
Out[28]:
array([ 0.15517108, 0.84717734, 0.86019899, 0.62413489, 0.24357903,
0.86015187, 0.85813481, 0.7071174 ])
In [30]: a
Out[30]: array([ 0.77930596, 2.41242955, 1.10377802, 0.86015187])
import numpy as np
N = 8
M = 4
b = np.array([0, 1, 2, 0, 2, 3, 1, 1])
c = np.array([ 0.15517108, 0.84717734, 0.86019899, 0.62413489, 0.24357903, 0.86015187, 0.85813481, 0.7071174 ])
a = ((np.mgrid[:M,:N] == b)[0] * c).sum(axis=1)
returns
array([ 0.77930597, 2.41242955, 1.10377802, 0.86015187])