Plot of a changing matrix ... gone wrong - python

I am trying to plot a matrix in Python. So, my initial thought was using matshow.
However, this particular matrix develop over time via an algorithm (the function sandpile below), so I need to show how the matrix develop over time - but in the same plot. The end result is sort of an animation. Any ideas as to how that is done ? The code below only produce one graph, and that is a picture of the very last updated matrix (matrix called abba, below).
Thank you in advance.
import numpy as np
import matplotlib.pyplot as plt
dimension = 3
abba = np.matrix( [ [2,5,2], [1,1000,4], [2,1,2] ] )
def sandpile(field):
greater3 = np.where(field > 3)
left = (greater3[0], greater3[1]-1)
right = (greater3[0], greater3[1]+1)
top = (greater3[0] - 1, greater3[1])
bottom = (greater3[0]+1 , greater3[1])
bleft = left[0][np.where(left[1] >= 0)], left[1][np.where(left[1] >= 0)]
bright = right[0][np.where(right[1] < dimension)], right[1][np.where(right[1] < dimension)]
btop = top[0][np.where(top[0] >= 0)], top[1][np.where(top[0] >= 0)]
bbottom = bottom[0][np.where(bottom[0] < dimension)], bottom[1][np.where(bottom[0] < dimension)]
field[greater3] -= 4
field[bleft] += 1
field[bright] += 1
field[btop] += 1
field[bbottom] += 1
return (field)
print(abba)
matfig = plt.figure(figsize=(3,3))
plt.matshow(abba, fignum=matfig.number)
n = 0
while (abba < 4).all() == False:
abba = sandpile(abba)
plt.matshow(abba, fignum=matfig.number)
n += 1
print('Exit with',n,'steps')
print(abba)

This is one way you can see an updating plot in a loop:
...
matfig = plt.figure(figsize=(3,3))
ax1 = matfig.add_subplot(1, 1, 1)
ax_image = ax1.imshow(abba)
plt.show(block=False)
n = 0
while (abba < 4).all() == False:
abba = sandpile(abba)
ax_image.remove()
ax_image = ax1.imshow(abba)
matfig.canvas.draw()
matfig.canvas.flush_events()
n += 1
print(n)
print('Exit with',n,'steps')
print(abba)
plt.show(block=True)
In your example changes happens at the very end of the loop.

Related

Octant partition equally a cloud of points in 3D

Is there a better way to do this? Not necessarily prettier, although it would be nice.
P = [N,3] ‘Cloud of points’
P -= np.sum(P, axis=0) / P.shape[0]
Map = [i for i in range(P.shape[0])]
p_0 = Map[P[:,0] <= 0]
p_1 = Map[P[:,0] > 0]
p_0_0 = p_0[P[p_0,1] <= 0]
p_0_1 = p_0[P[p_0,1] > 0]
p_1_0 = p_1[P[p_1,1] <= 0]
p_1_1 = p_1[P[p_1,1] > 0]
p_0_0_0 = p_0_0[P[p_0_0,2] <= 0]
p_0_0_1 = p_0_0[P[p_0_0,2] > 0]
p_0_1_0 = p_0_1[P[p_0_1,2] <= 0]
p_0_1_1 = p_0_1[P[p_0_1,2] > 0]
p_1_0_0 = p_1_0[P[p_1_0,2] <= 0]
p_1_0_1 = p_1_0[P[p_1_0,2] > 0]
p_1_1_0 = p_1_1[P[p_1_1,2] <= 0]
p_1_1_1 = p_1_1[P[p_1_1,2] > 0]
Or in other words, is there a way to compound conditions like,
Oct_0_0_0 = Map[P[:,0] <= 0 and P[:,1] <= 0 and P[:,2] <= 0]
I’m assuming a loop won’t be better than this… not sure.
Thanks in advance.
Instead of repeatedly slicing and keeping lists of the indices, I'd instead recommend creating a single array that maps the index of the point to the octant it belongs to. I'd argue that this is a more natural way of doing it in numpy. So for instance with
octants = (P>0) # 2**np.arange(P.shape[2])
the n-th entry of octants is the index (here in the range 0,1,2,...,7) of the octant that P belongs to. This works by checking each coordinate whether it is positive or not. This gives three boolean values per point which we can interpret as the binary expansion of that index. (In fact, the line above works for indexing the 2^d-ants in any number of dimensions d.)
To demonstrate this solution, following snippet makes a point cloud and colours them according to their quadrant:
import numpy as np
P = np.random.rand(10000, 3)*2-1
quadrants = (P > 0) # 2**np.arange(3)
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(projection='3d')
ax.scatter(*P.T, c=quadrants, cmap='jet')
plt.show()
If you still need to extract an array of the indices of a specific quadrant, say quadrant 0, 1, 1 this corresponds to finding the corresponding decimal number, which is 0*2^0 + 1*2^1 + 1*2^2 = 6, which you can do with e.g.
p_0_1_1 = np.where(quadrants == np.array([0,1,1]) # 2**np.arange(3))
All in all, I’m going with an even messier version,
p_x = P[:,0] < 0
p_0 = np.nonzero(p_x)[0]
p_1 = np.nonzero(~p_x)[0]
p_0_y = P[p_0,1] < 0
p_1_y = P[p_1,1] < 0
p_0_0 = p_0[p_0_y]
p_0_1 = p_0[~p_0_y]
p_1_0 = p_1[p_1_y]
p_1_1 = p_1[~p_1_y]
p_0_0_z = P[p_0_0,2] < 0
p_0_1_z = P[p_0_1,2] < 0
p_1_0_z = P[p_1_0,2] < 0
p_1_1_z = P[p_1_1,2] < 0
p_0_0_0 = p_0_0[p_0_0_z]
p_0_0_1 = p_0_0[~p_0_0_z]
p_0_1_0 = p_0_1[p_0_1_z]
p_0_1_1 = p_0_1[~p_0_1_z]
p_1_0_0 = p_1_0[p_1_0_z]
p_1_0_1 = p_1_0[~p_1_0_z]
p_1_1_0 = p_1_1[p_1_1_z]
p_1_1_1 = p_1_1[~p_1_1_z]
I have a religious belief (I mean based on thin air) that a comparison is fundamentally cheaper than an arithmetic operation.

Animate the evolution of a Markov Chain on a 2 dimensional array with python matplotlib imshow

I am trying to write the Metropolis Algorithm for the Hardcore Model on the 2 dimensional lattice. The algorithm I wrote so far seems to work and is as follows:
-Pick a vertex of the 2-d lattice
-Toss a fair coin
-If the coin comes up heads and all neighbors of the vertex take value 0 then set the vertex to 1 otherwise to 0.
I try to animate the evolution of the lattice. Here is what I wrote so far:
The Algorithm to simulate works. The animation not as I want.
Here is the algorithm:
import random
from matplotlib import animation
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
def Nachbarn(x0,x1,B): #test neighbours
if x1 +1 < len(B) and B[x0][x1+1] == 1 : #top
return True
elif x0 + 1 < len(B) and B[x0+1][x1] == 1 : #right
return True
elif x1 -1 >= 0 and B[x0][x1 -1] == 1 : #down
return True
elif x0 -1 >= 0 and B[x0-1][x1] == 1 : #left
return True
else:
return False
def Eckenwahl(B):
Länge = len(B)
x = random.choices(range(Länge),k=2) #choose a vertex
x0 = x[0]
x1 = x[1]
return x0,x1
def Münzwurf():
value = random.randint(0,1) #Toss a coin
return value
def MCMC(Array): #MCMC i-te Iteration
Ecke = Eckenwahl(Array)
if Münzwurf() == 1 and Nachbarn(Ecke[0],Ecke[1],Array) == False:
Array[Ecke[0]][Ecke[1]] = 1
else:
Array[Ecke[0]][Ecke[1]] = 0
return Array
Now, initialize the lattice:
N = 10 #Initialisierung of empty lattice
A = [[0] * N for i in range(N)]
If I apply the function MCMC on the array "A" a few times and start the animation with a non-empty lattice:
for i in range(5):
A = MCMC(A)
the animation seems to run, otherwise its stuck in the empty lattice and does not move forward. But I want to start it with an empty lattice. Here are two solutions that have these constraints so far:
Solution 1:
fig = plt.figure()
im = plt.imshow(A, animated = True)
def update_fig(*args):
global A
B = MCMC(A)
im.set_data(B)
return im
ani = animation.FuncAnimation(fig, update_fig, interval = 1)
plt.show()
Solution 2:
fps = 300
nSeconds = 10
fig = plt.figure( figsize=(8,8) )
a = A
im = plt.imshow(A)
def animate_func(i):
im.set_array(MCMC(A))
return [im]
anim = animation.FuncAnimation(fig, animate_func, frames = nSeconds *
fps,interval = 1000 / fps,)
The issue is, everything is ready. I want to start with a 2-d array/lattice that is full with 0`s called the empty configuration then, at time t=1 apply the function MCMC on the array, next display it in the animation and so on for t=2,3,...
Thanks for the help!

Demuxing values from numpy array

A device sends an array of multiplexed error codes. You can think of the multiplexed error codes as some sort of FIFO ring buffer that can have a variable length depending on the number of simultaneously active error code. I wish to demultiplex the error codes into individual boolean arrays.
I'm looking for an efficient way (i.e. removing the for loop) to implement the following code:
import numpy as np
def get_error_vector(error_mux_vector, error_id, period):
index = np.where(error_mux_vector == error_id)[0]
error_vector = np.zeros(np.size(error_mux_vector))
for i in range(0, np.size(index) - 1):
if (index[i + 1] - index[i]) <= 1 / period:
error_vector[index[i]:index[i + 1] + 1] = 1
return error_vector
Here are mock values to illustrate the problem. 0 means no error; 1, 2, and 3 are error codes. We assume the error signal has a frequency of 5 Hz (period of 0.2s) :
import matplotlib.pyplot as plt
error_signal = np.array([0,0,0,0,0,1,2,3,1,2,3,2,3,2,3,0,0,0,0,0,1,1,1,2,3,1,3,1,3,1,3,0,0,0,0,0,2,0,2,2])
error_vector_1 = get_error_vector(error_signal, 1, 0.2)
error_vector_2 = get_error_vector(error_signal, 2, 0.2)
error_vector_3 = get_error_vector(error_signal, 3, 0.2)
plt.plot(error_signal)
plt.plot(error_vector_1)
plt.plot(error_vector_2)
plt.plot(error_vector_3)
plt.legend(['array', 'error 1', 'error 2', 'error 3'])
plt.show()
The actual device data can have from 50k to 10M points, with around 100 possible error codes. This means that a for loop is really inefficient for the use case. I would like to improve this code but I haven't found an efficient solution so far.
Here is a vectorized approach that creates all vectors in one go. It comes in two flavors. On my random test case the second is faster but that may depend on the exact stats of your signal.
import numpy as np
# dense strat
def demultiplex(signal,maxdist):
n = signal.max()
aux = np.zeros((n,len(signal)+1),np.int16)
nz = signal.nonzero()[0]
signal = signal[nz]
idx = signal.argsort(kind="stable")
valid = ((nz[idx[1:]]<=nz[idx[:-1]]+maxdist)&
(signal[idx[1:]]==signal[idx[:-1]])).nonzero()[0]
aux[signal[idx[valid]]-1,nz[idx[valid]]] = 1
aux[signal[idx[valid+1]]-1,nz[idx[valid+1]]+1] -= 1
out = (aux[:,:-1].cumsum(1) > 0).view(np.int8)
return out
# sparse strat
def demultiplex2(signal,maxdist):
n = signal.max()
m = signal.size
nz = signal.nonzero()[0]
signal = signal[nz]
idx = signal.argsort(kind="stable")
delta = nz[idx[1:]] - nz[idx[:-1]]
valid = ((delta<=maxdist)&(signal[idx[1:]]==signal[idx[:-1]])).nonzero()[0]
delta = delta[valid]
nz = nz[idx[valid]]
nz[1:] -= nz[:-1] + delta[:-1]
offsets = (delta+1).cumsum()
x = np.ones(offsets[-1],int)
x[0] = nz[0]
x[offsets[:-1]] = nz[1:]
out = np.zeros((n,m),np.uint8)
out[(signal[idx[valid]]-1).repeat(delta+1),x.cumsum()] = 1
return out
# OP
def get_error_vector(error_mux_vector, error_id, period):
index = np.where(error_mux_vector == error_id)[0]
error_vector = np.zeros(np.size(error_mux_vector),np.int8)
for i in range(0, np.size(index) - 1):
if (index[i + 1] - index[i]) <= 1 / period:
error_vector[index[i]:index[i + 1] + 1] = 1
return error_vector
#error_signal = np.array([0,0,0,0,0,1,2,3,1,2,3,2,3,2,3,0,0,0,0,0,1,1,1,2,3,1,3,1,3,1,3,0,0,0,0,0,2,0,2,2])
error_signal = np.random.randint(0,101,1000000)
import time
t=[]
t.append(time.time())
error_vector_1 = get_error_vector(error_signal, 1, 0.02)
error_vector_2 = get_error_vector(error_signal, 2, 0.02)
error_vector_3 = get_error_vector(error_signal, 3, 0.02)
t.append(time.time())
sol = demultiplex(error_signal,50)
t.append(time.time())
sol2 = demultiplex2(error_signal,50)
t.append(time.time())
print("time per error id [OP, pp, pp2]",np.diff(t)/(3,100,100))
print("results equal",end=" ")
print((error_vector_1==sol[0]).all(),end=" ")
print((error_vector_2==sol[1]).all(),end=" ")
print((error_vector_3==sol[2]).all(),end=" ")
print((error_vector_1==sol2[0]).all(),end=" ")
print((error_vector_2==sol2[1]).all(),end=" ")
print((error_vector_3==sol2[2]).all())
Sample run:
time per error id [OP, pp, pp2] [0.02730425 0.00912964 0.00440736]
results equal True True True True True True
BIt of explanation:
we argsort signal to easily identify those error codes which are followed by themselves in close enough time.
we arrange the error vectors in a stack so points that should be set can be addressed by coordinate signal[t],t
to set stretches of time points we set the first to 1 and the one after the last to -1 and form the cumsum - to remedy overlapping stretches we check >0 and cast the resulting boolean to int

Output multiple plots in Python for animation

Im trying to animate a non-dispersive wave packet. My idea is to output the wave function at many different time points and then add individuals photos to make an animation. My Python code kind of the job but it repeats the previous plots in all following plots and i dont know how to fix it.
import matplotlib.pyplot as plt
import numpy as np
k = np.linspace(1,100,1000)
#x = np.linspace(-n<p.pi,np.pi,100)
x = np.linspace(0,70,71)
fac = np.linspace(0,1,1000)
result = []
n = 800
t = np.linspace(-150,150,n)
vp = -1
E0 = 1
w0 = 1
T = 10
def wave(k,x):
return abs(np.sin(x-k))
def packet(E0,t,w0,T,x):
return E0*np.cos(w0*(t+vp*x))*np.exp(-(t+vp*x)**2/T**2)
j = len(fac)-1
#while j >= 0:
#for i in range(len(k)):
# result.append(wave(k[i],x))
#j -= 1
b = np.array(result)
c = np.sum(b,axis = 0)
#plt.plot(x,c)
#plt.show()
counter = 0
i=0
while counter <= 71:
plt.plot(t,packet(E0,t,w0,T,i),label = 't = %d' %(i))
plt.savefig("time%d.pdf" %i)
i += 1
counter += 1
You can rewrite your cycle like this:
while counter <= 71:
plt.cla() # clean current axis
plt.plot(t, packet(E0,t,w0,T,i),label = 't = %d' %(i))
plt.ylim([-1.1, 1.1]) # establish limits for better visualization
plt.savefig("time%d.jpg" %i)
i += 1
counter += 1
also you can create animation according this example

Simulation and Histogram of Random Hopping in Python

I explain you my problem:
Imagine you have a bar, with say s positions. Each position can be counted as position 0, position 1, .. , position s-1.
Now what I want to do is simulate the following : At a certain point in time, a number of particles, say n particles, start in a state of the bar (assume a position in the middle).
At this point with random probabilities pr and pl (pr + pl =1) this particles go right or left respectively. So basically the probability reflects the proportion of particles swapping right or left.
I want to repeat this a number of times and see what is the final position of the particles and plot an histogram of it.
This is my function hop, which I made in order to simulate the hopping of the particles.
def hop(n):
'''
n is the number of particles starting in the middle position.
'''
s = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,n,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
global ls
ls = len(s)
i = 0
while i < 100:
for j in range(0,int(len(s))):
if s[j] != 0 :
pr = random.random()
pl = 1 - pr
if j - 1 < 0:
s[j+1] = s[j+1]+int(s[j]*pr)
s[j] = s[j] - int(s[j]*pr)
elif len(s) <= j+1:
s[j-1] = s[j-1] + int(s[j]*pl)
s[j] = s[j] - int(s[j]*pl)
else:
s[j-1] = s[j-1] + int(s[j]*pl)
s[j+1] = s[j+1] + int(s[j]*pr)
s[j] = s[j] - int(s[j]*pr) - int(s[j]*pl)
j+=1
elif s[j] == 0:
s[j] = 0
j+=1
i+=1
return s
And here is the rest, that I used to plot the histogram:
x = hop(100)
y = sum(x) #This is for debugging purposes, I want to check that I'm left
with the right number of particles
list1 = []
for k in range(0,ls):
list1.append(k)
plt.hist(x,list1)
plt.show()
Where I have imported mathplotlib and specifically I've imported
import matplotlib.pyplot as plt
import random
My problem is that from the histograms that I obtain this is doing something very wrong. Indeed the histograms are all skewed to the left, which wouldn't be possible If the probabilities are taken at random.
Furthermore the histogram doesn't show me the right amounts of particles.
Does anyone understand what is going wrong?
Thanks
I don't know if i got you right, but
do you want to see this instead of a histogram?
xs = np.arange(len(x))
width = 1/1.5
plt.bar(xs,x,width)

Categories