Related
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))
I need to code a 2d physics engine (for the moment without rotation) with a model of a wheel (here : one non-rotating Disc with small discs attached to it with springs in a circle to simulate the tyre).
It worked quite well until now (given that I choose a short enough time step), but now I have to add friction (it can be full friction : no relative speed between the tyre and the floor).
So when I'm computing the collisions, I want to know the speed BEFORE the acceleration due to forces. So instead of (Forces)>(Collisions)>(Change speed from acceleration)>(Update position),
I used (Forces)>(Change speed from acceleration)>(Collisions)>(Update position).
But then, no matter the time step, I have strange results, especially when colliding.
I could maybe have friction with the first order of steps, but it will be more complicated I guess.
In the code here, I tried to focus on the main things (but it's not THAT minimal either), so I removed friction for example, since the problem seems to be in the order of my steps.
In the tkinter window, there are several time steps available if you want to test (for example the first one completely fails).
Thanks in advance
PS : I know the springs are very strong (k = 1e7), that sould be a wheel.
import numpy as np
import math as m
import random as rd
import tkinter as tk
import time
def CC2(coords,size=500,zoom=160,offset=[100,100]):#Change x,y coordinates into canvas coordinates
x = int(coords[0]*zoom+offset[0])
y = int((size-coords[1]*zoom)-offset[1])
return x,y
def CC4(coords):#Change from (x1,y1),(x2,y2)
return CC2(coords[0]),CC2(coords[1])
def normalize(vec):#Normalize the vector
return (1/norm(vec))*vec
def norm(vec):#Norm of the vector
return m.sqrt(sum(vec**2))
def sqnorm(vec):#Square norm
return sum(vec**2)
class Scene:
def __init__(self,objectlist,canvas):
self.can = canvas
self.objects = objectlist
self.time = 0#Scene timer
g = 9.81
self.gravity = np.array([0,-g])
def makeStep(self,dt=0.01,display = True):
#Acceleration from gravity
for obj in self.objects:
if obj.invmass != 0:
obj.accel = self.gravity.copy()
#Get accelerations from other forces (here : spring joints)
for obj in self.objects:
if obj.invmass != 0:
#From special joints i.e. spring joints
for joint in obj.joints:#Joint → Force
j = joint.objId
o1 = self.objects[j]
force = joint.getForce(o1,obj)
o1.accel += o1.invmass*force
obj.accel -= obj.invmass*force
"""
Works quite well when the following loop is AFTER the collisions
But in order to add (full) friction properly I wanted to know the speed AFTER applying the forces hence the acceleration
(I can maybe do otherwise but it's more complicated and might not work either...)
"""
#Change speeds from acceleration
for obj in self.objects:
obj.accelerate(dt)
#Apply collisions and change speeds
self.findCollisions(dt)
#Move objects
for obj in self.objects:
obj.move(dt)
if display:
self.display()
self.time += dt
def play(self,dt=0.0001,total_time=5,get_energies=False):#Play the simulation (dt is the time step)
realtime = time.time()
starting_time=realtime
last_display = realtime
while self.time-starting_time <= total_time:
#Just for display
display = False
if time.time()-last_display >= 0.1:
display = True
last_display = time.time()
#Next step
self.makeStep(dt,display)
def findCollisions(self,dt):#Find all collisions, get normal vectors from getCollision and call resolveCollision
n = len(self.objects)
for i in range(n):
o2 = self.objects[i]
joints = o2.joints
for j in range(i):# j<i
o1 = self.objects[j]#Objects 1 & 2
if o1.classCollide(o2):#Classes compatible for collision
if o1.bboxIntersect(o2):
normal = self.getCollision(o1,o2)
self.resolveCollision(o1,o2,normal)#Resolve collision
def resolveCollision(self,o1,o2,normal):#Change speed and position to resolve collision
if normal.any():#normal is not 0,0 (collision)
depth = norm(normal)
normal = 1/depth*normal
relative_speed = o2.speed - o1.speed
normal_speed = relative_speed # normal#Norm of projection of relative speed
total_invmass = o1.invmass + o2.invmass#Sum of inverse masses
if normal_speed > 0:#Real collision:
e=1
coef = (1+e)*normal_speed
o1.speed += coef*(o1.invmass/total_invmass)*normal
o2.speed += -coef*(o2.invmass/total_invmass)*normal
if 0.001<depth:#Positional correction
correction = 0.2*depth/total_invmass*normal
o1.center += o1.invmass*correction
o2.center -= o2.invmass*correction
def getCollision(self,o1,o2,display=False):#Intersection between objects with intersecting bbox: returns normal vector with norm = penetration depth (directed towards o1)
if o1.type == "box" and o2.type == "box":
delta = o2.center-o1.center
dim_sum = o1.dimensions+o2.dimensions#Sum of half-widths and heights
dsides = [delta[0]+dim_sum[0],-delta[0]+dim_sum[0],delta[1]+dim_sum[1],-delta[1]+dim_sum[1]]#Left, right, bottom, top, bottom, left, right of o1
imin = np.argmin(dsides)
if imin == 0:#Left
normal = np.array([dsides[0],0])#Orientation : right = positive
elif imin == 1:#Right
normal = np.array([-dsides[1],0])
elif imin == 2:#Bottom
normal = np.array([0,dsides[2]])
else:#Top
normal = np.array([0,-dsides[3]])
return normal
if o1.type == "disc":
return o1.getCollisionVector(o2)
if o2.type == "disc":
return -o2.getCollisionVector(o1)
def display(self):#Just display the scene
self.can.delete('all')
for obj in self.objects:
color = "yellow"
if obj.type == "box":
if obj.invmass==0:#Unmoveable
color = "black"
can.create_rectangle(CC4(obj.bbox()),fill=color)
if obj.type == "disc":
can.create_oval(CC4(obj.bbox()),fill="springgreen")
for joint in obj.joints:
can.create_line(CC2(obj.center),CC2(self.objects[joint.objId].center+joint.offset),dash=(3,2))
fen.update()
## Objects
class Object2D:#Abstract class for circles and boxes
def bboxIntersect(self,object2):#Intersection of bounding boxes
bbox1 = self.bbox()
bbox2 = object2.bbox()
if (bbox1[1][0]<bbox2[0][0] or bbox1[0][0]>bbox2[1][0]):#No intersecting on x axis
return False
if (bbox1[1][1]<bbox2[0][1] or bbox1[0][1]>bbox2[1][1]):#No intersecting on y axis
return False
return True
def move(self,dt):
if self.invmass == 0:
return None
self.center += dt*self.speed
def accelerate(self,dt):
if self.invmass == 0:
return None
self.speed += self.accel*dt
def classCollide(self,obj):
if (self.cls == "nc1" or obj.cls == "nc1"):#No collision at all
return False
if (self.cls == "nc2" and obj.cls == "nc2"):#No collision inside this class
return False
return True
class Box(Object2D):
def __init__(self,mass,center,width,height,initspeed=[0.0,0.0],joints=[],cls=""):
self.invmass = 1/mass
self.center = np.array(center,dtype=float)#x,y
self.hheight = height/2#Half height
self.hwidth = width/2
self.dimensions=np.array([self.hwidth,self.hheight])
self.speed = np.array(initspeed,dtype=float)#Initial speed (x,y)
self.accel = np.zeros(2)#x,y acceleration
self.type = "box"
self.joints = joints
self.cls=cls
def bbox(self):
return (self.center[0]-self.hwidth,self.center[1]-self.hheight),(self.center[0]+self.hwidth,self.center[1]+self.hheight)
class Disc(Object2D):
def __init__(self,mass,center,radius,initspeed=[0.0,0.0],joints = [],cls=""):
self.invmass = 1/mass
self.center = np.array(center,dtype=float)#x,y
self.radius = radius
self.speed = np.array(initspeed,dtype=float)#Initial speed (x,y)
self.accel = np.zeros(2)#x,y acceleration
self.type = "disc"
self.joints = joints
self.cls=cls
def bbox(self):
return (self.center[0]-self.radius,self.center[1]-self.radius),(self.center[0]+self.radius,self.center[1]+self.radius)
def getCollisionVector(self,obj):
if obj.type == "box":#VS BOX
box = obj
bbox = box.bbox()
delta = self.center-box.center
if (bbox[0][0] <= self.center[0] <= bbox[1][0]):#Vertical collision
return np.sign(delta[1])*np.array([0,self.radius+box.hheight-abs(delta[1])])
if (bbox[0][1] <= self.center[1] <= bbox[1][1]):#Horizontal collision
return np.sign(delta[0])*np.array([self.radius+box.hwidth-abs(delta[0]),0])
#else find closest corner
if delta[1] > 0:#Top
if delta[0] > 0:#Right
delta_corner = self.center - (box.center+box.dimensions)
else:#Left
delta_corner = self.center - (box.center+np.array([-box.hwidth,box.hheight]))
else:#Bottom
if delta[0] > 0:#Right
delta_corner = self.center - (box.center+np.array([box.hwidth,-box.hheight]))
else:#Left
delta_corner = self.center - (box.center-box.dimensions)
distance = norm(delta_corner)
if distance > self.radius:#No collision
return np.zeros(2)
return (self.radius-distance)/distance*delta_corner
elif obj.type == "disc":#VS DISC
delta = self.center - obj.center
norm_delta = norm(delta)
depth = self.radius + obj.radius - norm_delta
if depth > 0:#Collision
return depth*normalize(delta)
return np.zeros(2)
class Floor(Box):
def __init__(self,y,xmin=-500,xmax=500):
self.invmass = 0#Infinite mass
self.y = y
self.hwidth = (xmax-xmin)/2
self.hheight = 50
self.dimensions=np.array([self.hwidth,self.hheight])
self.center = np.array([(xmin+xmax)/2,y-50])
self.type = "box"
self.accel = np.zeros(2)
self.speed = np.zeros(2)
self.joints = []
self.cls=""
## Forces & joints
class SpringJoint:
def __init__(self,objId,k,l0,damper=10,offset=[0,0]):
self.objId = objId
self.l0 = l0
self.k = k
self.offset = np.array(offset)
self.damper = damper
def getForce(self,o1,o2):
delta = o2.center - (o1.center+self.offset)
normal = normalize(delta)
diff = delta - self.l0*normal
delta_speed = o2.speed - o1.speed
return self.k*diff + self.damper*delta_speed#normal*normal
## Objects definitions
#Test wheel with spring : generates a "wheel" model
def getWheel(Radius,IntRadius,IntMass,ExtMass,kr,ks,x=0,y=0.5,n=14,initspeed=[0,0]):
arc = 2*m.pi*Radius/n
r = 0.35*arc
l0s = 2*(Radius-r)*m.sin(m.pi/n)
R = IntRadius - r
l0r = Radius - r
core = Disc(IntMass,[x,y],R,initspeed=initspeed)
tyre= []
for k in range(n):
a = k/n*2*m.pi
tyre.append(Disc(ExtMass/n,[x+l0r*m.cos(a),y+l0r*m.sin(a)],r,joints=[SpringJoint(0,kr,l0r),SpringJoint(k%n,ks,l0s)],cls="nc2"))
#Discs from the outside don't interact with each other except with the spring joints
tyre[-1].joints.append(SpringJoint(1,ks,l0s))
del tyre[0].joints[1]
return [core] + tyre
#Objects in the scene
#☺Simple wheel with n=5
objects = getWheel(0.5,0.25,500,1,1e7,1e7,y=0.5,initspeed=[5,0],n=5) + [Floor(0)]
## Scene
fen = tk.Tk()
can = tk.Canvas(fen,width = 1000,height=500)
can.pack()
scene = Scene(objects,can)
scene.display()
tk.Button(fen,text="Go quick (10**-3 s)",command = lambda : scene.play(0.001,3,get_energies)).pack()
tk.Button(fen,text="Go medium (10**-4)",command = lambda : scene.play(0.0001,3,get_energies)).pack()
tk.Button(fen,text="Go slowly (3*10**-5)",command = lambda : scene.play(0.00003,1,get_energies)).pack()
tk.Button(fen,text="Go very slowly (10**-5)",command = lambda : scene.play(0.00001,1,get_energies)).pack()
tk.Button(fen,text="Do 0.01s",command = lambda : scene.play(0.0001,0.01,get_energies)).pack()
tk.Button(fen,text="Do 1 step",command = lambda : scene.play(0.01,0.01,get_energies)).pack()
fen.mainloop()
Edit: misunderstood the question.
Would it help to have the move step before the collision step? Movement should happen right after acceleration.
Try to calculate the acceleration before the collision in order to get frictional forces without ever applying it to the objects.
Eventually I kept the original order and found another way to implement friction, so now it works quite well
I'm just trying to simulate a simple path planning problem using an artificial potential field (APF). To do so, I have a Point class each instance of which represents the location of an agent. A bunch of functions to move the agent's location, to find its distance to some other points, and to compute an APF corresponding to that are implemented. An Obstacle class is also defined each instance of which is simply a point in addition to the obstacle's radius.
import autograd.numpy as np
from autograd import grad
import math
class Point(object):
def __init__(self,x,y):
self.X = x
self.Y = y
def distance(self, goal):
dx = self.X - goal.X
dy = self.Y - goal.Y
return math.hypot(dx, dy)
def move(self, value):
self.X += value
self.Y += value
def APF(self, goal, k, obstacles):
num = self.distance(goal)**2
temp = self.distance(goal)**(2*k)
for item in obstacles:
temp = temp + (self.distance(item.point)**2 - item.radius**2)
den = temp**(1/k)
return num/den
class Obstacle(object):
def __init__(self, point, radius):
self.point = point
self.radius = radius
To test the code above, the following main snippet is taken into account. In particular, it is supposed to iteratively compute the gradient of the APF and to add the result to the agent's location (say, moving it) until the gradient vanishes.
if __name__== "__main__" :
start = Point(0.0, 0.0)
goal = Point(15.0, 5.0)
p1 = Point(2.0, 2.0)
p2 = Point(3.0, 3.0)
p3 = Point(4.0, 4.0)
p4 = Point(5.0, 5.0)
obs1 = Obstacle(p1, 1.0)
obs2 = Obstacle(p2, 2.0)
obs3 = Obstacle(p3, 3.0)
obs4 = Obstacle(p4, 4.0)
obstacles = [obs1, obs2, obs3, obs4]
trajectory = [start]
k = 7.0
temp = start
while grad(temp.APF(goal, k, obstacles)) != 0.0:
next = temp.move(grad(temp.APF(goal, k, obstacles)))
trajctory.append(next)
temp = next
However, I've encountered the following error:
Traceback (most recent call last):
File "test_APF.py", line 53, in <module>
next = temp.move(grad(temp.APF(goal, k, obstacles)))
File "test_APF.py", line 17, in move
self.X += value
TypeError: unsupported operand type(s) for +=: 'float' and 'function'
To me APF returns numbers so grad should be okay with it. Then, move will also receive numbers as it argument. Can you please help me spotting the issue?
import autograd.numpy as np
from autograd import grad
import math
def APF(k,start,goal, obstacles):
dist = math.hypot(start[0] - goal[0], start[1] - goal[1])
num = dist**2
temp = dist**(2*k)
for item in obstacles:
temp = temp + (dist ** 2 - item.radius ** 2)
den = temp ** (1 / k)
return num / den
class Point(object):
def __init__(self,x,y):
self.X = x
self.Y = y
def distance(self, goal):
dx = self.X - goal.X
dy = self.Y - goal.Y
return math.hypot(dx, dy)
def move(self, value):
self.X += value
self.Y += value
class Obstacle(object):
def __init__(self, point, radius):
self.point = point
self.radius = radius
if __name__== "__main__" :
start = (0.0, 0.0)
goal = (15.0, 5.0)
p1 = Point(2.0, 2.0)
p2 = Point(3.0, 3.0)
p3 = Point(4.0, 4.0)
p4 = Point(5.0, 5.0)
obs1 = Obstacle(p1, 1.0)
obs2 = Obstacle(p2, 2.0)
obs3 = Obstacle(p3, 3.0)
obs4 = Obstacle(p4, 4.0)
obstacles = [obs1, obs2, obs3, obs4]
trajectory = [start]
k = 7.0
temp = start
print(grad(APF)(k,start,goal, obstacles))
# next = temp.move(grad(temp.APF(goal, k, obstacles)))
# trajctory.append(next)
# temp = next
You have to create two classes to handle this.. See my reference class for info.
According to the Autograd readme, autograd.grad() returns a function.
>>> grad_tanh = grad(tanh) # Obtain its gradient function
>>> grad_tanh(1.0) # Evaluate the gradient at x = 1.0
You're passing the function, not its return value, to your .move() function.
I'm trying to implement a genetic algorithm for solving the Travelling Salesman Problem (TSP).
I have 2 classes, which are City and Fitness.
I have done the code for initialization.
class City:
def __init__(self, x, y):
self.x = x
self.y = y
def distance(self, city):
xDis = abs(self.x - city.x)
yDis = abs(self.y - city.y)
distance = np.sqrt((xDis ** 2) + (yDis ** 2))
return distance
def __repr__(self):
return "(" + str(self.x) + "," + str(self.y) + ")"
class Fitness:
def __init__(self, route):
self.route = route
self.distance = None
self.fitness = None
def routeDistance(self):
if self.distance == None:
pathDistance = 0.0
for i in range(0, len(self.route)):
fromCity = self.route[i]
toCity = None
if i+1 < len(self.route):
toCity = self.route[i+1]
else:
toCity = self.route[0]
pathDistance += fromCity.distance(toCity)
self.distance = pathDistance
return self.distance
def routeFitness(self):
if self.fitness == None:
self.fitness = 1 / float(self.routeDistance())
return self.fitness
def selection(population, size=None):
if size== None:
size= len(population)
matingPool = []
fitnessResults = {}
for i in range(0, size):
fitnessResults[i] = Fitness(population[i]).routeFitness()
matingPool.append(random.choice(population))
return matingPool
The code above just randomly selects a parent in the selection method.
My question is: How to code to select a parent using roulette wheels?
You could try this [1, 2]:
from numpy.random import choice
def selection(population, size=None):
if size== None:
size= len(population)
fitnessResults = []
for i in range(0, size):
fitnessResults.append(Fitness(population[i]).routeFitness())
sum_fitness = sum(fitnessResults)
probability_lst = [f/sum_fitness for f in fitnessResults]
matingPool = choice(population, size=size, p=probability_lst)
return matingPool
Read this
So basically, the higher a fitness value, the higher are its chances to be chosen. But that is when high fitness value means a high fitness. But in TSP a lower value of fitness is better so to implement this, we need to implement the concept where probability is indirectly proportional to the fitness value.
Here is something I had implemented in python with some changes
def choose_parent_using_RWS(genes, S):
P = random.uniform(0, S)
for x in genes:
P += get_fitness_value(x)
if P > S:
return x
return genes[-1]
where S is the sum of the inverse of the fitness values of the current population (i.e, 1/f1 + 1/f2 + 1/f3 + ...)
and
get_fitness_value(x) returns the inverse of the distance, just like your routeFitness() function
TeeHee
I am currently dealing with the above code.
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
from matplotlib import cm
import sympy
import numpy as np
import random
Q = [None, None]
K = [None, None, None]
f = np.zeros((201, 201))
K[2] = f
X = []
d = 2
global_best_position = []
global_best_error = 10000
vec=np.random.rand(d)
R1=np.diag(vec)
vec=np.random.rand(d)
R2=np.diag(vec)
def akley(x, dim, a=20, b=0.2, c=2*np.pi):
sum1 = 0
sum2 = 0
for i in range(2):
sum1 += x[i]**2
sum2 += np.cos(c*x[i])
sum_sq_term = -a * np.exp(-b * np.sqrt(sum1) / 2)
cos_term = -np.exp((sum2) / 2)
Z = a + np.exp(1) + sum_sq_term + cos_term
return Z
def plot_akley_2d():
global d
ymin, ymax, xmin, xmax = -16, 16, -16, 16
for i in range(d):
Q[i] = np.linspace(xmin, xmax, 201)
for i in range(d):
K[0], K[1]= np.meshgrid(Q[0], Q[1])
for i in range(201):
for h in range(201):
u = [K[0][i][h], K[1][i][h]]
u = np.array(u)
K[2][i][h] = akley(u, 2)
d = plt.contourf(K[0], K[1], K[2], 20, cmap=cm.rainbow)
plt.ylim((ymin, ymax)), plt.xlim((ymin, ymax)), plt.colorbar(d);
class Particle():
def __init__(self):
global d
self.position = []
self.velocity = []
self.best_position = []
self.best_error = 0
self.error = 0
for i in range(2):
self.position.append(random.uniform(-16, 16))
self.velocity.append(random.uniform(-1, 1))
self.best_position = self.position
self.error = akley(self.position, d)
self.best_error = self.error
self.evaluate_position()
def evaluate_position(self):
global d, global_best_position, global_best_error
self.error = akley(self.position, d)
if self.error < self.best_error:
self.best_error = self.error
self.best_position = self.position
if self.best_error < global_best_error:
global_best_position = self.best_position
global_best_error = self.best_error
def update_particle(self, w=0.5, c1=2, c2=2):
global R1, R2, global_best_position
self.velocity = self.velocity + c1*(self.best_position-self.position)*R1 + c2*(global_best_position - self.position)*R2
self.position = self.position + self.velocity
self.evaluate_position()
class PSO():
def __init__(self, iterations, num_particles):
self.particle_list = []
self.iterations = iterations
for i in range(num_particles):
self.particle_list.append(Particle())
def optimize(self):
for i in range(self.iterations):
for j in range(len(self.particle_list)):
self.particle_list[j].update_particle()
PSO1 = PSO(100, 10)
PSO1.optimize()
print(global_best_error)
print(global_best_position)
When executing it I receive the following error:
File "<ipython-input-47-e1f278f8304b>", line 84, in update_particle
self.velocity = self.velocity + c1*(self.best_position-self.position)*R1 + c2*(global_best_position - self.position)*R2
TypeError: unsupported operand type(s) for -: 'list' and 'list'
I did some searching already and it seems to me that I am not able to substract two lists from each other. I tried to convert all of them to numpy arrays, but unfortunately it just raises even more errors within my code. I checked the dimensions of all lists and it should work, I am just very unsure about how to approach matrix/list operations here.
Ideally, converting to numpy arrays would be the best course of action. Then you can subtract directly or use np.subtract().
If you want a work-around to prevent the other errors in your code you can use list comprehensions, though they will not be as quick as numpy operations:
e.g.
... c2*[global_best_position[i] - self.position[i] for i in range(len(global_best_position))]...