Name error: Variable not defined - python

Program calculates the shortest route from point, to line, then to second point. Also I need to say how long is from the start of the line, to where point crosses. My code so far:
from math import sqrt
numbers_total = []
numbers_canal = []
first_triangle_longest_side_length = 300 #defining the variable
def first_triangle(first_triangle_longest_side_length):
calculation_one = sqrt(first_triangle_longest_side_length)
first_triangle_bottom_side_length = calculation_one - sqrt(300)
def second_triangle(canal_length_left):
canal_length_left = canal_length_left ** 2
calculation_two = canal_length_left + 500 ** 2
second_triangle_longest_side_length = sqrt(calculation_two)
while True:
first_triangle_longest_side_length = first_triangle_longest_side_length + 0.01
first_triangle(first_triangle_longest_side_length)
canal_length_left = 1000 - first_triangle_bottom_side_length
second_triangle(canal_length_left)
if first_triangle_longest_side_length == 1044.03:
break
total_distance = first_triangle_longest_side_length + second_triangle_longest_side_length
numbers_total.append(total_distance)
numbers_canal.append(first_trangle_bottom_side_length)
minimal_total_distance = min(numbers_total)
number_on_the_list = numbers_total.index(minimal_total_distance)
print "The distance of the shortest route is: " + "%.1f" % minimal_total_distance
print "The distance of the canal from point A is: " + "%.1f" % numbers_canal[number_on_the_list]
However, it gives the error:
line 19, in <module>
canal_length_left = 1000 - first_triangle_bottom_side_length
NameError: name 'first_triangle_bottom_side_length' is not defined
Any ideas?

For those interested, here is the problem: Cambridge February Computing Competition
You are taking a very brute-force approach; it works, but could be much more efficient Edit: I take it back; your math is incorrect too. I will correct it in a new post.
Here is a more analytic way to do it:
TOWER_DIST = 300
CANAL_LEN = 1000
FIRE_DIST = 500
#
# express the problem as a function
#
def path_length(dist_a):
"""
Given distance in meters from A along the canal,
return total path length
"""
leg_a = (TOWER_DIST**2 + dist_a**2) ** 0.5
leg_b = ((CANAL_LEN - dist_a)**2 + FIRE_DIST**2) ** 0.5
return leg_a + leg_b
#
# find x such that path_length(x) is minimized
#
# (the easy way:
# import scipy.optimize
# result = scipy.optimize.minimize(path_length, CANAL_LEN * 0.5)
# best_dist = result.x[0] # => 375.00092001
# best_length = path_length(best_dist) # => 1280.6248
#
# but because this is a competition we will avoid add-on modules)
def deriv(f, delta=0.01):
"""
Return a function which is a numerical first derivative of f
"""
def df(x):
a, b = f(x - delta), f(x + delta)
return (b - a) / (2. * delta)
return df
def newton_root(f, initial_x, tolerance=0.01, max_tries=1000):
"""
Use Newton-Raphson method to find x such that abs(f(x)) <= tolerance
"""
df = deriv(f)
x = initial_x
for try_ in range(max_tries):
err = f(x)
if abs(err) <= tolerance:
# found a solution
return x
else:
# find next value for x
x -= err / df(x)
else:
raise ValueError(
"newton_root fails to converge for initial_guess = {}"
.format(initial_x)
)
# By inspection, path_length is a smooth upward curve (ie a U-shape)
# on 0 .. CANAL_LEN; minimum occurs where first derivative is 0
best_dist = newton_root(deriv(path_length), CANAL_LEN * 0.5, 0.00001) # => 374.9999
best_length = path_length(best_dist) # => 1280.62484
# return results to nearest 0.1 meter
print("{:0.1f} {:0.1f}".format(best_dist, best_length))
If you play with this and think about it a while, you should realize that the shortest path is always such that the angles between leg_a and the canal and between the canal and leg_b are identical; if you think of the canal as a mirror, the shortest path is to run straight to the fire's reflection in the mirror.
This allows us to reduce the problem to a simple pair of similar triangles,
# TOWER_DIST / best_dist == (TOWER_DIST + FIRE_DIST) / CANAL_LEN
best_dist = TOWER_DIST * CANAL_LEN / (TOWER_DIST + FIRE_DIST)
best_length = ((TOWER_DIST + FIRE_DIST)**2 + CANAL_LEN**2) ** 0.5

After taking another look at your answer I realized your math is also wrong. I will point out your syntax and math errors and add a bit of free advice along the way ;-)
Stepping through your original code:
from math import sqrt
numbers_total = []
numbers_canal = []
first_triangle_longest_side_length = 300 #defining the variable
Really? where_on_earth_did_you_learn_variable_naming? Try tower_dist = 300.
def first_triangle(tower_hypot):
calculation_one = sqrt(tower_hypot)
first_triangle_bottom_side_length = calculation_one - sqrt(300)
First mistake: assigning a value to first_triangle_bottom_side_length doesn't accomplish anything. The variable is of local scope (ie it only exists inside this function) so as soon as the function ends it disappears.
You could be completely evil and make it a global variable, or you could just return calculation_one - sqrt(300) instead.
Second mistake: the math is just wrong. I'll come back to that in a moment.
def second_triangle(canal_length_left):
canal_length_left = canal_length_left ** 2
calculation_two = canal_length_left + 500 ** 2
second_triangle_longest_side_length = sqrt(calculation_two)
Same error again; do return sqrt(calculation_two) instead.
Important note: naming conventions! first_triangle returns a base length, second_triangle returns a hypotenuse. This is more than a bit confusing. Also, both functions have constants hard-wired in, meaning the functions cannot be reused for anything else.
If you instead did:
def side(adjacent, hypotenuse):
"""
Given the side and hypotenuse of a right triangle,
return the other side
"""
return (hypotenuse**2 - adjacent**2) ** 0.5
def hypotenuse(adjacent, opposite):
"""
Given two sides of a right triangle,
return the hypotenuse
"""
return (adjacent**2 + opposite**2) ** 0.5
def tower_base(tower_hypot, tower_height=300):
return side(tower_height, tower_hypot)
def fire_hypot(fire_base, fire_height=500):
return hypotenuse(fire_base, fire_height)
This is general enough to be reusable, has meaningful but not ridiculously long variable names, and can easily be tested for correctness:
assert side(3, 5) == 4
assert side(5, 13) == 12
assert hypotenuse(3, 4) == 5
assert hypotenuse(5, 12) == 13
# a (300, 400, 500) triangle
assert tower_base(500) == 400
# a (500, 1200, 1300) triangle
assert fire_hypot(1200) == 1300
Testing your previous code:
# should make a (300, 400, 500) triangle
assert first_triangle(500) == 400 # 5.04017 ?! FAIL
And there is the math error: first_triangle returns tower_hypot**0.5 - 300**0.5 which is a meaningless quantity; it should be (tower_hypot**2 - 300**2)**0.5.
Unit analysis gives us a quick way to double-check; if tower_hypot is in meters, the first equation returns root-of-meters (which makes no sense) while the second returns meters (which is what we expected). That does not necessarily make it correct - but it makes it not obviously incorrect, which is a good start!
while True:
tower_hypot += 0.01
first_triangle(first_triangle_longest_side_length)
You called the function correctly, but you didn't keep the result anywhere; try a_dist = tower_base(tower_hypot).
canal_length_left = 1000 - a_dist
second_triangle(canal_length_left)
Again you haven't kept the function result; try fire_dist = fire_hypot(1000 - a_dist).
if first_triangle_longest_side_length == 1044.03:
break
Magic numbers! What fun!
it is not clear what this value means (it is (300**2 + 1000**2)**0.5 which is the maximum possible value of tower_hypot, but that is unclear at first glance)
floating point math means == is almost never going to work; you want > or >= instead
you have hard-coded in values for tower_height, canal_length, fire_height - which is bad practice - but you have now also hard-coded in values derived from those values, making your program very difficult to maintain (what happens next week, when the ranger sees a fire 1200 meters away and 200 meters from the canal? Does he have to rewrite the program from scratch to figure out where to go?)
then
total_distance = first_triangle_longest_side_length + second_triangle_longest_side_length
numbers_total.append(total_distance)
numbers_canal.append(first_trangle_bottom_side_length)
minimal_total_distance = min(numbers_total)
number_on_the_list = numbers_total.index(minimal_total_distance)
print "The distance of the shortest route is: " + "%.1f" % minimal_total_distance
print "The distance of the canal from point A is: " + "%.1f" % numbers_canal[number_on_the_list]
With a few more modifications the code looks like
# use named variables!
# - makes it easy to modify the program later
# - makes it easier to figure out what's going on
TOWER_HEIGHT = 300
CANAL_LENGTH = 1000
FIRE_HEIGHT = 500
# generic functions which can be reused!
def side(adjacent, hypotenuse):
"""
Given the side and hypotenuse of a right triangle,
return the other side
"""
return (hypotenuse**2 - adjacent**2) ** 0.5
def hypotenuse(adjacent, opposite):
"""
Given two sides of a right triangle,
return the hypotenuse
"""
return (adjacent**2 + opposite**2) ** 0.5
# work from the most obvious control value
def total_dist(a_dist):
tower_hypot = hypotenuse(a_dist, TOWER_HEIGHT)
b_dist = CANAL_LENGTH - a_dist
fire_hypot = hypotenuse(b_dist, FIRE_HEIGHT)
return tower_hypot + fire_hypot
def main():
step_size = 0.1
# equivalent of
# a_dists = numpy.arange(0.0, CANAL_LENGTH, step_size)
num_steps = int(CANAL_LENGTH / step_size)
a_dists = (k*step_size for k in range(num_steps + 1))
# find minimum distance
best_a_dist = min(a_dists, key=total_dist)
best_total_dist = total_dist(best_a_dist)
print("The distance of the shortest route is: {:0.1f}".format(best_total_dist))
print("The distance of the canal from point A is: {:0.1f}".format(best_a_dist))
main()

It is exactly what the error says. Python works line by line. Look above it inside your function. You never define `first_triangle_bottom_side_length". Define it in the function and the error will go away.

The variable first_triangle_bottom_side_length might be being declared as local inside the function, to solve this you return the variable and set it outside of the function.
(Inside first triangle):
Return first_triangle_bottom_side_length
...
firsttrianglebottomsidelength = first_triangle(first_triangle_longest_side_length)
canal_length_left = 1000 - firsttrianglebottomsidelength

Like Tim Castelijns wrote - it's a matter of scopes.
first_triangle_bottom_side_length is defined within the function first_triangle but you never returns it to your main program.
You should return it:
def first_triangle(first_triangle_longest_side_length):
calculation_one = sqrt(first_triangle_longest_side_length)
return calculation_one - sqrt(300)
[...]
[...]
while True:
first_triangle_longest_side_length = first_triangle_longest_side_length + 0.01
first_triangle_bottom_side_length = first_triangle(first_triangle_longest_side_length)
canal_length_left = 1000 - first_triangle_bottom_side_length
Read about scopes, but more important - learn how to work with functions.
Good luck!

Related

Runtime error - where is the mistake in my code

My former problem (which is solved now) was:
As an input, I have a list of nonnegative whole numbers which are supposed to be the coefficients of a polynomial. But I also want to evaluate the polynomial for a certain number x.
For example:
If we have L=[2,3,1] as an input and x=42 we get 2x^2+3x+1=3655
What I want is for example:
>>>p=polynomial([2,3,1])
>>>p(O)
1
>>>p(42)
>>>3655
The code is
def polynomial(coef):
def poly(x):
result = 0
x_n = 1
for a in reversed(coef):
result += a * x_n
x_n *= x
return result
return poly
What I wanted to do now is to find the inverse , that means the input the is a monotone polynomial and the positive whole number y and I want to find a whole number x such that p(x)=y, and x should be only in [1,10**10], for example:
>>>p=polynomial([2,3,1])
>>>p(O)
1
>>>p(42)
>>>3655
>>>invert(3655,p)
42
This is what I have so far, but what I get is a runtime error:
def polynomial(coef):
def poly(x):
result = 0
xn = 1
for c in reversed(coef):
result += c * xn
xn *= x
return result
return poly
def invert(y,p):
test=10**10
if p(2)>p(1):
if p(test)>y:
test=test//2 +(test%2)
return invert(y,p)
elif p(test)<y:
test=test+(test//2)
return invert(y,p)
else:
return test
if p(2)<p(1):
if p(test)<y:
test=test//2 +(test%2)
return invert(y,p)
elif p(test)>y:
test=test+(test//2)
return invert(y,p)
else:
return test
The error that occurs is
...
File "poly.py", line 17, in invert
return invert(y,p)
File "poly.py", line 14, in invert
if p(2)>p(1):
File "poly.py", line 5, in poly
for c in reversed(coef):
RuntimeError: maximum recursion depth exceeded while calling a Python object
What am I doing wrong?
Your invert function recurses forever because you never modify the arguments that you pass to the next call. You do modify test, but that doesn't do you any good, since the inner call will have its own copy of test.
There are a few ways you could fix the issue. You could pass test as an argument to the invert function, with your initial value a default that will be used the first time:
def invert(y, p, test=10**10):
# ...
# later, when you recurse:
test = test // 2 # or whatever
return invert(y, p, test) # pass on the modified test value
Another (probably better) approach would be to abandon recursion, and use a loop instead. A while loop seems like it would be appropriate here:
def invert(y, p):
test = 10**10
sign = (-1)**(p(2) < p(1))
while True:
if p(test) > y:
test -= sign * (test // 2)
elif p(test) < y:
test += sign * (test // 2)
else:
return test # this is the only case that returns
I've left the overall algorithm the same as what your original code does (just streamlined a bit). That algorithm may not be correct if your polynomial isn't strictly increasing or strictly decreasing. You should really compute the derivative of the polynomial at test to determine which direction to adjust in, but I'll leave that to you to figure out.
I took the liberty to fix the indention of the code you posted. Please verify that the below code is actually what you have, regarding indentions The following code does return your desired output..
def polynomial(coef):
def poly(x):
result = 0
x_n = 1
for a in reversed(coef):
result += a * x_n
x_n *= x
return result
return poly
def invert(y,p,test): # updated
# test=10**10 # This was the problem
# You reset 'test' for every recursive call
# which means you will stand still without
# any progress until the max num of allowed
# recursive calls are reached.
if p(2)>p(1):
if p(test)>y:
test=test//2 +(test%2)
return invert(y,p,test) # updated
elif p(test)<y:
test=test+(test//2)
return invert(y,p,test) # updated
else:
return test
if p(2)<p(1):
if p(test)<y:
test=test//2 +(test%2)
return invert(y,p,test) # updated
elif p(test)>y:
test=test+(test//2)
return invert(y,p,test) # updated
else:
return test
p = polynomial([2,3,1])
t = 10**10
print(invert(3655,p,t))
I wrote the code myself now, limiting everything to the knowledge/skill I only have so far, and it works:
def polynomial(coef):
def poly(x):
result = 0
x_n = 1
for a in reversed(coef):
result += a * x_n
x_n *= x
return result
return poly
def invert(y,p):
x=10**10
while p(x)!=y:
if p(x)>y:
w=x
x=x//2
elif p(x)<y:
x=(x+w)//2
return x

Wting reversed function using bisection method in logarithmic run time

I'm trying to write a function that can take any function and return the a parameter that if put in the function, will return answer close to 0 (close to epsilon), the function will look something like this:
def solve(f, x0=-10000, x1=10000, epsilon=EPSILON):
the x0, x1 are the range in which to look for the answer.
another thing I know is that it applies only to the function that can be both positive and negative ( for example f(X) = x^2+1 is not a good function to solve).
I found an answer here Bisection method
def solve(f, x0=-10000, x1=10000, epsilon=EPSILON):
""" return the solution to f in the range between x0 and x1\
use an algorithm to check if a solution can be found so f(x)<epsilon
iterates in a while loop until either a solution is found or if the abs
the value of the midpoint is smaller than epsilon (return None)"""
# make sure the function is in the type that can be solved
if (f(x1) * f(x0)) >= 0:
return None
while True:
mid = (x0 + x1) / 2
sol = f(mid)
if abs(sol) < epsilon:
return mid
if mid == 0 or (abs(f(x1) - f(x0)) / 2) < epsilon:
return None
elif sol * f(x0) < 0:
x1 = mid
elif sol * f(x1) < 0:
x0 = mid
edit:
so far so good. now I have the main function I need to write - a function that gives the revered value for function. the function itself gets the function that needs to be reversed and an epsilon to which the answer suppose to be close to.
for example, for f(x) = x+2, I want the inverse_func(f(100)) to return 100.
the hint I have is that I can use the prev function that I showed. I tryied doing so like this:
def inverse(g, epsilon=EPSILON):
"""return f s.t. f(g(x)) = x"""
def ret_function(x):
return find_x(x, g, epsilon)
return ret_function
def find_x(x, g, epsilon):
x0, x1 = -10000, 1001
g_sol = x
sent_epsilone = EPSILON
while True:
def f(x):
g_val = g(x)
ans = g_sol - g_val
return ans
sol = solve(f, x0, x1, sent_epsilone)
if sol == None:
pass
else:
return sol
x0, x1 = x0 * 10, x1 * 10
what I tried to give "solve" function to solve the problem for me. I'm giving it a function that calculates the given value from f(x) minus a value that solve function needs to find.
for example for f(x) = x+2, then a call to
minus_func = inverse(g(100)) =inverse(102)
print(minus_func)
is suppos to return
100
because it the function inside "solve" is 102-f(x) and of course "solve" can find the right value for this.
and I tried this in my code, and it work fine, but not good enough. for some functions, it works fine. but for others, it doesn't work at all.
for the functions:
math.e**x
x**-3
and probably others, it doesn't work. does someone has an idea how to solve this?.
p.s - I'm writing the code in python so it'll be great if the answer is also in python. but anything else is ok (I know java also and anything that will explain the logic is, of course, great)
thanks!
The condition
if mid == 0 or (abs(f(x1) - f(x0)) / 2) < epsilon:
return None
does not make sense. Why is 0 excluded as a possible root? With the default initial values the method will fail in the first loop. And if the function values are that close, they either have the same sign, which was excluded, or they represent a root of the function since both values are small enough.
It should be replaced by the missing
if abs(x1-x0) < epsilon:
return mid
Try this implementation of binary search:
def solve(f, x0=-10000, x1=10000, epsilon=EPSILON):
if f(x0) * f(x1) > 0: # predicate of binary search
return None
while x1 - x0 > epsilon: # while search interval is bigger than EPS
mid = (x0 + x1) / 2 # take middle of interval
sol = f(mid) # take function value in mid point
if sol * f(x0) > 0: # one of roots is located in [mid, x1] interval
x0 = mid
else: # one of roots is located in [x0, mid] interval
x1 = mid
return (x0 + x1) / 2
Feel free to ask questions about it.

Return value of if/else recursive function incorrect but print value correct

Trying to use recursion to solve a riemann sum.
def f(x):
import math
return 10*math.e**(math.log(0.5)/5.27 * x)
liTotal = 0
def radExpo(start, stop, step):
global liTotal
x = start
area = f(x)*step
liTotal += area
numOfRects = (stop - start) / step
if start > (stop - (2 *step)):
return liTotal
else:
return radExpo((start+step), stop, step)
radExpo(12, 16, 1)
If I change if return statement to
print liTotal
or if I call the function with
print radExpo
It works correctly but if I call/return it regularly it returns the wrong value so I can't use what is returned.
If this is a question about what is wrong with the code, I would refactor the code to remove the global reference and it will improve its reliability. I was not able to recreate your issue where the print liTotal is different that print radExpo(12,16,1)
A modified version of your code, that removes the global variable reference looks like this:
def f(x):
import math
return 10*math.e**(math.log(0.5)/5.27 * x)
def radExpoNew(start, stop, step):
x = start
area = f(x)*step
if start <= (stop - (2 *step)):
area += radExpoNew((start+step), stop, step)
return area
print radExpoNew(12,16,1) # prints 6.84864583554
Some simplifications:
from math import e, log
# rule #1:
# e ** (a*b) == (e ** a) ** b
# applying rule #1 twice:
# e ** (log(0.5) / 5.27 * x) == ((e ** log(0.5)) ** (1/5.27)) ** x
# rule #2:
# e ** log(k) == k
# applying rule #2:
# e ** log(0.5) == 0.5
# 0.5 ** (1/5.27) == 0.8767556206241964
# so we get:
def fn(x):
return 10 * 0.8767556206241964 ** x
Then radExpo is essentially doing tail recursion (calling itself once at the end of the function), so we can rewrite it non-recursively:
def integrate(fn, start, stop, step):
steps = int((stop - start) / step)
return sum(
fn(start + k*step) # calculate x directly for each step
for k in range(steps) # to prevent error accumulation
) * step
This gets rid of the global variable that was causing issues (because you did not reset it before calling the function again).
Then
>>> integrate(fn, 12, 16, 1)
6.848645835538626
and as a bonus you can integrate whatever function you like:
>>> from math import pi, sin
>>> integrate(sin, 0, pi, 0.001)
1.999999361387437 # pretty darn close to 2.0

Basics of Simulated Annealing in Python [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions concerning problems with code you've written must describe the specific problem — and include valid code to reproduce it — in the question itself. See SSCCE.org for guidance.
Closed 9 years ago.
Improve this question
I have to use simulated annealing for a certain optimization problem. To get a 'feel' of the technique, I wrote a small python code and tried to run it. However, it doesn't seem to be giving satisfactory results.
import random;
import math;
from math import *;
LIMIT=100000;
def update_temperature(T,k):
T1=T/log(k+1);
# print "temp now is " + str(T1);
return T1;
def get_neighbors(i,l):
if(l>1):
if(0<=i and i<l):
if(i==0):
return [1];
if(i==l-1):
return [l-2];
return [i-1,i+1];
return [];
def make_move(x,A,T):
nhbs=get_neighbors(x,len(A));
nhb=nhbs[random.choice(range(0,len(nhbs)))];
delta=A[nhb]-A[x];
if(delta < 0):
return nhb;
else:
r=random.random();
if(r <= (e**(-1*delta)/(T*1.0))):
return nhb;
return x;
def simulated_annealing(A):
l=len(A);
init_pos=random.choice(xrange(0,l));
T=10000**30;
k=1;
x_best=init_pos;
x=x_best;
while(T>0.0000001 ):
x=make_move(x,A,T);
if(A[x] < A[x_best]):
x_best=x;
T=update_temperature(T,k);
k+=1;
return [x,x_best,init_pos];
def isminima_local(p,A):
l=len(A);
if(l==1 and p==0):
return True;
if(l>1):
if(p==0):
if(A[0] <=A[1]):
return True;
if(p==l-1):
if(A[p-1] >=A[p]):
return True;
if(0<=p and p<l and A[p-1]>=A[p] and A[p]<=A[p+1]):
return True;
return False;
def func(x):
F=sin(x);
return F;
def initialize(l):
A=[0]*l;
for i in xrange(0,l):
A[i]=func(i);
return A;
def main():
A=initialize(LIMIT);
local_minima=[];
for i in xrange(0,LIMIT):
if( isminima_local(i,A)):
local_minima.append([i,A[i]]);
sols=simulated_annealing(A);
m,p=A[0],0;
for i in xrange(1,LIMIT):
if(m>A[i]):
m=A[i];
p=i;
print "Global Minima at \n";
print p,m;
print "After annealing\n";
print "Solution is " + str(sols[0]) + " " + str(A[sols[0]]);
print "Best Solution is " + str(sols[1]) + " " + str(A[sols[1]]);
print "Start Solution is " + str(sols[2]) + " " + str(A[sols[2]]);
for i in xrange(0,len(local_minima)):
if([sols[0],A[sols[0]]]==local_minima[i]):
print "Solution in local Minima";
if([sols[1],A[sols[1]]]==local_minima[i]):
print "Best Solution in local Minima";
for i in local_minima:
print i;
main();
I am unable to understand where I am going wrong. Is there something wrong with the implementation or is there something wrong in my understanding about simulated annealing ? Please point out the error..
My rough idea about SA:
Pick a random neighbor
If neighbor improves your condition, move there,
Else, move there with certain probability.
The probability is such that initially bad moves are 'allowed' but they are 'prohibited' later on. Finally you will converge to your solution.
I have found the set of local minima and global minima using brute force. Then I run SA. I was expecting that SA will atleast converge to a local minima but that doesn't seem to be the case always. Also, I am not sure if at every step I choose a neighbor randomly and then try to move or I choose the best neighbor ( even if none of the neighbors improve my condition) and then try to move there.
For the most part, your code seems to work well. The main reason that it's slow to converge is that you only look at the two neighbors on either side of your current point: if you expand your search to include any point in A, or even just a wider neighborhood around your current point, you'll be able to move around the search space much more quickly.
Another trick with simulated annealing is determining how to adjust the temperature. You started with a very high temperature, where basically the optimizer would always move to the neighbor, no matter what the difference in the objective function value between the two points. This kind of random movement doesn't get you to a better point on average. The trick is finding a low enough starting temperature value such that the optimizer will move to better points significantly more often than it moves to worse points, but at the same time having a starting temperature that is high enough to allow the optimizer to explore the search space. As I mentioned in my first point, if the neighborhood that you select points from is too limited, then you'll never be able to properly explore the search space even if you have a good temperature schedule.
Your original code was somewhat hard to read, both because you used a lot of conventions that Python programmers try to avoid (e.g., semicolons at ends of lines), and because you did a few things that programmers in general try to avoid (e.g., using lowercase L as a variable name, which looks very similar to the numeral 1). I rewrote your code to make it both more readable and more Pythonic (with the help of autopep8). Check out the pep8 standard for more information.
In make_move, my rewrite picks one random neighbor from across the whole search space. You can try rewriting it to look in an expanded local neighborhood of the current point, if you're interested in seeing how well that works (something between what you had done above and what I've done here).
import random
import math
LIMIT = 100000
def update_temperature(T, k):
return T - 0.001
def get_neighbors(i, L):
assert L > 1 and i >= 0 and i < L
if i == 0:
return [1]
elif i == L - 1:
return [L - 2]
else:
return [i - 1, i + 1]
def make_move(x, A, T):
# nhbs = get_neighbors(x, len(A))
# nhb = nhbs[random.choice(range(0, len(nhbs)))]
nhb = random.choice(xrange(0, len(A))) # choose from all points
delta = A[nhb] - A[x]
if delta < 0:
return nhb
else:
p = math.exp(-delta / T)
return nhb if random.random() < p else x
def simulated_annealing(A):
L = len(A)
x0 = random.choice(xrange(0, L))
T = 1.
k = 1
x = x0
x_best = x0
while T > 1e-3:
x = make_move(x, A, T)
if(A[x] < A[x_best]):
x_best = x
T = update_temperature(T, k)
k += 1
print "iterations:", k
return x, x_best, x0
def isminima_local(p, A):
return all(A[p] < A[i] for i in get_neighbors(p, len(A)))
def func(x):
return math.sin((2 * math.pi / LIMIT) * x) + 0.001 * random.random()
def initialize(L):
return map(func, xrange(0, L))
def main():
A = initialize(LIMIT)
local_minima = []
for i in xrange(0, LIMIT):
if(isminima_local(i, A)):
local_minima.append([i, A[i]])
x = 0
y = A[x]
for xi, yi in enumerate(A):
if yi < y:
x = xi
y = yi
global_minumum = x
print "number of local minima: %d" % (len(local_minima))
print "global minimum #%d = %0.3f" % (global_minumum, A[global_minumum])
x, x_best, x0 = simulated_annealing(A)
print "Solution is #%d = %0.3f" % (x, A[x])
print "Best solution is #%d = %0.3f" % (x_best, A[x_best])
print "Start solution is #%d = %0.3f" % (x0, A[x0])
main()

How to define codependent functions in Python?

I need to plot the position of a particle at time t, given the following formulae: s(t) = -0.5*g(s)*t^2+v0*t, where g(s) = G*M/(R+s(t))^2 (G, M, and R are constants, s being a value, not the function s(t)). The particle is being shot up vertically, and I want to print its current position every second until it hits the ground. But I can't figure out how to define one function without using the other before it's defined. This is my code so far:
G = 6.6742*10^(-11)
M = 5.9736*10^24
R = 6371000
s0 = 0
v0 = 300
t = 0
dt = 0.005
def g(s):
def s(t):
s(t) = -0.5*g(s)*t^2+v0*t
g(s) = G*M/(R+s(t))^2
def v(t):
v(t) = v(t-dt)-g(s(t-dt))*dt
while s(t) >= 0:
s(t) = s(t-dt)+v(t)*dt
t = t+dt
if t == int(t):
print s(t)
When I run the function, it says that it can't assign the function call.
The error means that you can't write s(t) = x, because s(t) is a function, and assignment on functions is performed with def .... Instead, you'll want to return the value, so you'd rewrite it like this:
def g(s):
def s(t):
return -0.5*g(s)*t^2+v0*t
return G*M/(R+s(t))^2
However, there are other issues with that as well. From a computational standpoint, this calculation would never terminate. Python is not an algebra system and can't solve for certain values. If you try to call s(t) within g(s), and g(s) within s(t), you'd never terminate, unless you define a termination condition. Otherwise they'll keep calling each other, until the recursion stack is filled up and then throws an error.
Also, since you defined s(t) within g(s), you can't call it from the outside, as you do several times further down in your code.
You seem to be confused about several syntax and semantic specifics of Python. If you ask us for what exactly you'd like to do and provide us with the mathematical formulae for it, it might be easier to formulate an answer that may help you better.
Edit:
To determine the position of a particle at time t, you'll want the following code (reformatted your code to Python syntax, use ** instead of ^ and return statements):
G = 6.6742*10**(-11)
M = 5.9736*10**24
R = 6371000
s0 = 0
v0 = 300
t = 0
dt = 0.005
sc = s0 # Current position of the particle, initially at s0
def g(s):
return -G*M/(R+s)**2
def s(t):
return 0.5*g(sc)*t**2 + v0*t + s0
count = 0
while s(t) >= 0:
if count % 200 == 0:
print(sc)
sc = s(t)
count += 1
t = dt*count
Python functions can call each other, but that's not how a function returns a value. To make a function return a particular value, use return, e.g.,
def v(t):
return v(t - dt) - g(s(t - dt)) * dt
Furthermore, I don't really understand what you're trying to do with this, but you'll probably need to express yourself differently:
while s(t) >= 0:
s(t) = s(t-dt)+v(t)*dt
t = t+dt

Categories

Resources