Hey so I guess I will start with my code then go into my problem:
def trapezoidal(a, b, deltax, func = None):
func = lambda x: (raw_input("Enter a function to have a trapezoidal approximation taken
of. Enter it in the form a*x^m + b*x^n + c*x, a*x^m +c, etc. (ex. 3*x^3 + 4*x^2...) ")
h = float(b - a) / deltax
s = 0.0
s += func(a)/2.0
for i in range(1, deltax):
s += func(a + i*h)
s += func(b)/2.0
return s * h
Basically I am trying to make this user friendly. What I need to do is replace the ^ in a raw_input to ** so a lambda can evaluate it. Ultimately I want to plug in values for a, b, and delta x, press enter, enter the function using close to normal notation (using ^ instead of **). I know this may seem silly and pointless but user friendliness is a must. I would even like to get rid of having to make the user put in a * between the coefficient and the variable ((preferably 3x^2 would be evaluated as 3*x**2) then plugged into the lambda and then the rest of it run. I realize I can simply put in
def trapezoidal(a, b, deltax, func):
h = float(b - a) / deltax
s = 0.0
s += func(a)/2.0
for i in range(1, deltax):
s += func(a + i*h)
s += func(b)/2.0
return s * h
trapezoidal(5, 10, 100, lambda x: 3*x**2 + 2*x)
and it be evaluated fine. But that is not user friendly.
func is just a string. You have to eval it to execute it as a Python expression. Before you do that, you can perform any string manipulation you like on it.
func = raw_input()
func.replace('^', '**')
# TODO: valudation here
f = eval(func)
Before you eval anything you should verify that it matches your expected input format; otherwise, you have a giant security vulnerability in your code.
At the beginning of your source file:
import re
Change the raw_input call to:
re.sub(r'([\d])x', r'\1*x', raw_input("Enter a function...")).replace('^', '**')
Explanation:
re.sub does a regular-expression replacement. In this case, you're:
replacing a digit [\d]
capturing the digit in group 1 by enclosing it in parenthesis ([\d])
followed by x
with
that same digit captured in group 1 \1
followed by an asterisk *
followed by x
Finally, we replace all instances of ^ with ** with a simple str.replace call.
However, as tripleee says, this is not sufficient to actually evaluate the function; this only performs the text-manipulation problem described in your title and in the first part of your question. Actually evaluating the text as a function is a separate problem.
Here is how I solved the problem:
def trapezoidal(a, b, deltax, func = None):
#first we find the height using our range divided by deltax
h = float(b - a) / deltax
'''
next we find start to calculate the sum; set s to 0 to make it start at 0
we divide func(a) by two, because of the area of trapezoid 1/2(b1 + b2)h
next use a for loop to evaluate the b1 + b2
s is basically the 1/2(b1 + b2), then we multiply it by h, the height.
tr '''
s = 0.0
s += func(a)/2.0
for i in range(1, deltax):
s += func(a + i*h)
s += func(b)/2.0
return s * h
'''
next we are going to get our values for a, b, and deltax
we must use eval(func) to use the lambda x:.
it then runs through trapezoidal()
'''
def userexp():
a = int(raw_input("Enter your a "))
b = int(raw_input("enter your b "))
deltax = int(raw_input("enter your deltax "))
func = raw_input("Enter your function as a*x**n + b*x**m.. ex 3*x**3 + 5*x**2 ")
return trapezoidal(a, b, deltax, lambda x: eval(func))
Related
I would like to find an approximate value for the number pi = 3.14.. by using the Newton method. In order to use it also for some other purpose and thus other function than sin(x), the aim is to implement a generic function that will be passed over as an argument. I have an issue in passing a function as an argument into an other function. I also tried lambda in different variations. The code I am showing below produces the error message: IndexError: list index out of range. I will appreciate your help in solving this issue and eventually make any suggestion in the code which may not be correct. Thanks.
from sympy import *
import numpy as np
import math
x = Symbol('x')
# find the derivative of f
def deriv(f,x):
h = 1e-5
return (lambda x: (f(x+h)-f(x))/h)
def newton(x0,f,err):
A = [x0]
n = 1
while abs(A[n]-A[n-1])<=err:
if n == 1:
y = lambda x0: (math.f(x0))
b = x0-y(x0)/(deriv(y,x0))
A.append(b)
n += 1
else:
k = len(A)
xk = A[k]
y = lambda xk: (math.f(xk))
b = newton(A[k],y,err)-y(newton(A[k],y,err))/deriv(y,k)
A.append(b)
n += 1
return A, A[-1]
print(newton(3,math.sin(3),0.000001))
I don't know why you use sympy because I made it without Symbol
At the beginning you have to calculate second value and append it to list A and later you can calculate abs(A[n]-A[n-1]) (or the same without n: abs(A[-1] - A[-2])) because it needs two values from this list.
Other problem is that it has to check > instead of <=.
If you want to send function sin(x) then you have to use math.sin without () and arguments.
If you want to send function sin(3*x) then you would have to use lambda x: math.sin(3*x)
import math
def deriv(f, x, h=1e-5):
return (f(x+h) - f(x)) / h
def newton(x0, f, err):
A = [x0]
x = A[-1] # get last value
b = x - (f(x) / deriv(f, x)) # calculate new value
A.append(b) # add to list
while abs(A[-1] - A[-2]) > err: # it has to be `>` instead of `<=`
x = A[-1] # get last value
b = x - (f(x) / deriv(f, x)) # calculate new value
A.append(b) # add to list
return A, A[-1]
# sin(x)
print(newton(3, math.sin, 0.000001)) # it needs function's name without `()`
# sin(3*x)
print(newton(3, lambda x:math.sin(3*x), 0.000001))
# sin(3*x) # the same without `lambda`
def function(x):
return math.sin(3*x)
print(newton(3, function, 0.000001))
Result:
([3, 3.1425464414785056, 3.1415926532960112, 3.141592653589793], 3.141592653589793)
([3, 3.150770863559604, 3.1415903295877707, 3.1415926535897936, 3.141592653589793], 3.141592653589793)
EDIT:
You may write loop in newton in different way and it will need <=
def newton(x0, f, err):
A = [x0]
while True:
x = A[-1] # get last value
b = x - (f(x) / deriv(f, x)) # calculate new value
A.append(b) # add to list
if abs(A[-1] - A[-2]) <= err:
break
return A, A[-1]
I am working on a project where I need to change all variables that are named 'a' to a new variable ai, where i is the order of the variable a in the expression. For instance if we use the expression: 1 + x + a + a ** 2, the output should be: 1 + x + a0 + a1 ** 2. Here is a code that I've written to solve this but it doesn't work, the expression remains unchanged.
import sympy.parsing.sympy_parser as sp1
import sympy as sp
I=sp1.parse_expr('1 + x + a + a**2', evaluate=False)
a,x=sp.symbols('a x')
def pre(expr):
i=0
for arg in sp.postorder_traversal(expr):
if arg==a:
tmp=sp.symbols('a'+str(i))
arg=tmp
print(arg)
i=i+1
pre(I)
print(I)
One way to achieve that is:
from sympy import Pow, Mul, Symbol, degree
def change_symbol(expr, a):
"""expr: the expression to modify
a: the symbol to look for and substitute
"""
# define a wild symbol to look for Symbols, Multiplications and Powers
# containing the specified symbol
w = Wild("w", properties=[
lambda t: isinstance(t, (Pow, Mul, Symbol)) and ((a in t.args) or (t == a))
])
# find the terms that satisfy the above criteria
terms = list(expr.find(w))
terms.sort(key=lambda t: degree(t), reverse=True)
# loop over those terms and performs the substitution with new symbols
name = a.name
for t in terms:
o = degree(t)
s = Symbol(name + "%s" % (o - 1))
expr = expr.subs(t, s**o)
return expr
change_symbol(I, a)
# out: a0 + a1**2 + x + 1
Your code did not work because you never changed the expression. When you say arg = tmp that assigns a value of tmp to arg but this does not update expr. #Davide_sd shows a way to recreate an expression with pieces that have been modified. You can also let replace do the traversal and let it replace a as it encounters it.
suffix = [0] #mutable suffix
def na():
rv = Symbol('a%s'%suffix[0])
suffix[0]+=1 # modify for next time
return rv
>>> a,x=var('a x')
>>> (1 + x + 2*a + a**2).replace(lambda x: x==a, lambda x: na())
a0**2 + 2*a1 + x + 1
Note that you said "order in expression" and coded as though you meant "order encountered" but in the polynomial sense, "higher order" terms will not necessarily appear later in the ordered terms. Note that a**2 appears before 2*a and that is why the replace gave it a value of a0:
>>> (1 + x + 2*a + a**2).args
(1, x, a**2, 2*a)
def f(x):
f='exp(x)-x-2'
y=eval(f)
print(y)
return y
def bissection(f,f_line,f_2lines,a,b,epsilon1,epsilon2):
x=a
result_a=f(a)
x=b
result_b=f(b)
if (f.evalf(a)*f.evalf(b)>=0):
print("Interval [a,b] does not contain a zero ")
exit()
zeta=min(epsilon1,epsilon2)/10
x=a
while(f_line(x)>0):
if(x<b or x>-b):
x=x+zeta
else:
stop
ak=a
bk=b
xk=(ak+bk)/2
k=0
if (f(xk)*f(ak)<0):
ak=ak
bk=xk
if (f(xk)*f(bk)<0):
ak=xk
bk=bk
k=k+1
from sympy import *
import math
x=Symbol('x')
f=exp(x)-x-2
f_line=f.diff(x)
f_2lines=f_line.diff(x)
print("Derivative of f:", f_line)
print("2nd Derivative of f:", f_2lines)
a=int(input('Beginning of interval: '))
b=int(input('End of interval: '))
epsilon1=input('1st tolerance: ')
epsilon2=input('2nd tolerance: ')
bissection(f,f_line,f_2lines,a,b,epsilon1,epsilon2)
This program is an attempt to implement the Bissection Method. I've tried writing two functions:
The first one, f, is supposed to receive the extremes of the interval that may or may not contain a root (a and b) and return the value of the function evaluated in this point.
The second one, bissection, should receive the function, the function's first and second derivatives, the extremes of the interval (a,b) and two tolerances (epsilon1,epsilon2).
What I want to do is pass each value a and b, one at a time, as arguments to the function f, that is supposed to return f(a) and f(b); that is, the values of the function in each of the points a and b.
Then, it should test two conditions:
1) If the function values in the extremes of the intervals have opposite signs. If they don't, the method won't converge for this interval, then the program should terminate.
if(f.evalf(a)*f.evalf(b)>=0)
exit()
2)
while(f_line(x)>0): #while the first derivative of the function evaluated in x is positive
if(x<b or x>-b): #This should test whether x belongs to the interval [a,b]
x=x+zeta #If it does, x should receive x plus zeta
else:
stop
At the end of this loop, my objective was to determine whether the first derivative was strictly positive (I didn't do the negative case yet).
The problem: I'm getting the error
Traceback (most recent call last):
File "bissec.py", line 96, in <module>
bissection(f,f_line,f_2lines,a,b,epsilon1,epsilon2)
File "bissec.py", line 41, in bissection
result_a=f(a)
TypeError: 'Add' object is not callable
How can I properly call the function so that it returns the value of the function (in this case, f(x)=exp(x)-x-2), for every x needed? That is, how can I evaluate f(a) and f(b)?
Ok, so I've figured it out where your program was failing and I've got 4 reasons why.
First of all, and the main topic of your question, if you want to evaluate a function f for a determined x value, let's say a, you need to use f.subs(x, a).evalf(), as it is described in SymPy documentation. You used in 2 different ways: f.evalf(2) and f_line(a); both were wrong and need to be substituted by the correct syntax.
Second, if you want to stop a while loop you should use the keyword break, not "stop", as written in your code.
Third, avoid using the same name for variables and functions. In your f function, you also used f as the name of a variable. In bissection function, you passed f as a parameter and tried to call the f function. That'll fail too. Instead, I've changed the f function to f_calc, and applied the correct syntax of my first point in it.
Fourth, your epsilon1 and epsilon2 inputs were missing a float() conversion. I've added that.
Now, I've also edited your code to use good practices and applied PEP8.
This code should fix this error that you're getting and a few others:
from sympy import *
def func_calc(func, x, val):
"""Evaluate a given function func, whose varible is x, with value val"""
return func.subs(x, val).evalf()
def bissection(x, f, f_line, f_2lines, a, b, epsilon1, epsilon2):
"""Applies the Bissection Method"""
result_a = func_calc(f, x, a)
result_b = func_calc(f, x, b)
if (result_a * result_b >= 0):
print("Interval [a,b] does not contain a zero")
exit()
zeta = min(epsilon1, epsilon2) / 10
x_val = a
while(func_calc(f_line, x, a) > 0):
if(-b < x_val or x_val < b):
x_val = x_val + zeta
else:
break # the keyword you're looking for is break, instead of "stop"
print(x_val)
ak = a
bk = b
xk = (ak + bk) / 2
k = 0
if (func_calc(f, x, xk) * func_calc(f, x, ak) < 0):
ak = ak
bk = xk
if (func_calc(f, x, xk) * func_calc(f, x, bk) < 0):
ak = xk
bk = bk
k = k + 1
def main():
x = Symbol('x')
f = exp(x) - x - 2
f_line = f.diff(x)
f_2lines = f_line.diff(x)
print("Derivative of f:", f_line)
print("2nd Derivative of f:", f_2lines)
a = int(input('Beginning of interval: '))
b = int(input('End of interval: '))
epsilon1 = float(input('1st tolerance: '))
epsilon2 = float(input('2nd tolerance: '))
bissection(x, f, f_line, f_2lines, a, b, epsilon1, epsilon2)
if __name__ == '__main__':
main()
I cannot seem to get an output when I pass numbers to the function. I need to get the computed value and subtract it from the exact. Is there something I am not getting right?
def f1(x):
f1 = np.exp(x)
return f1;
def trapezoid(f,a,b,n):
'''Computes the integral of functions using the trapezoid rule
f = function of x
a = upper limit of the function
b = lower limit of the function
N = number of divisions'''
h = (b-a)/N
xi = np.linspace(a,b,N+1)
fi = f(xi)
s = 0.0
for i in range(1,N):
s = s + fi[i]
s = np.array((h/2)*(fi[0] + fi[N]) + h*s)
print(s)
return s
exactValue = np.full((20),math.exp(1)-1)
a = 0.0;b = 1.0 # integration interval [a,b]
computed = np.empty(20)
E=np.zeros(20)
exact=np.zeros(20)
N=20
def convergence_tests(f, a, b, N):
n = np.zeros(N, 1);
E = np.zeros(N, 1);
Exact = math.exp(1)-1
for i in range(N):
n[i] = 2^i
computed[i] = trapezoid(f, a, b, n[i])
E = abs(Exact - computed)
print(E, computed)
return E, computed
You have defined several functions, but your main program never calls any of them. In fact, your "parent" function convergence_test cannot be called, because it's defined at the bottom of the program.
I suggest that you use incremental programming: write a few lines; test those before you proceed to the next mini-task in your code. In the posting, you've written about 30 lines of active code, without realizing that virtually none of it actually executes. There may well be several other errors in this; you'll likely have a difficult time fixing all of them to get the expected output.
Start small and grow incrementally.
I am trying to solve an equation for variable 'X' in python where some of the variables in the equation ('ABC, PQR') are output from a function 'calculations'. The problem is, in order to get an output from the function, I need to pass variable X as an argument itself. I am kind of stuck in a loop here. I tried two different approaches but didn't get any success. Is there a way I can solve the equation?
Any help/direction is really appreciated.
My first approach is to start with a small value and run a loop. I tried to use math.isclose() but receive 'math bound error' once the values go off the range and it runs into an infinite loop.
The second approach is to write the complete expression and use scipy.optimize fsolve() but I am unable to understand how to properly implement it.
# function
def calculations(X, a, b, c):
ABC = X*a*b + c
XYZ = X*b*c + a
PQR = X*a*c + b
return ABC, XYZ, PQR
# ABC, PQR is the output from a function which uses X as input
# solve for X
func = 2*ABC + sqrt(PQR*ABC) + X*100*0.5
# Approach 1
X = 0.001
step = 0.001
while True:
# function call
Var_ABC, Var_XYZ, Var_PQR = calculations(X, a, b, c)
func = 2*Var_ABC + math.sqrt(Var_PQR * Var_ABC) + X*100*0.5
if (math.isclose(func, 0.0, rel_tol=0.1) == True):
break
else:
X = X + step
# Approach 2
# Since I don't know the value of X, how can I get the output from the function and then solve it again?
func_output[0] = calculations(X, a, b, c) # ABC
func_output[2] = calculations(X, a, b, c) # PQR
func = 2* func_output[0] + math.sqrt (func_output[0] * func_output[2] ) + X*100*0.5
from scipy.optimize import fsolve
desired_output_X = fsolve(func, [0.01, 1])
This may help you getting started with fsolve:
# function of X
def func(X):
func_output = calculations(X, a, b, c)
func = 2* func_output[0] + math.sqrt (func_output[0] * func_output[2]) + X*100*0.5
return func
# extra arguments for calculations function, dummy values used: set them as desired
a,b,c = 1,2,6
# initiating X = 0.01 and solve for X
desired_output_X = fsolve(func, x0 = 0.01)