Let's say I have the following code:
def f(x, y, n = 1):
k = 10
z = x**n + y**n + k
return z
def g(x):
alpha = 10
return f(x, alpha)
I would like to analyze g to extract from it all constants involved in returning its output: alpha, n, and k. Is it possible to do this in python, perhaps by using the ast library?
Further, if the above is possible, is it then possible to edit some of those constants:
h = create_new_function(g, g.alpha = 25, f.k = 50)
It would be quite something if this were possible!
You can use the ast and
inspect modules to do this task
Use inspect.getsource to get the source of the function where you want to extract the variables.
Parse the function source code to an ast node using ast.compile
Go through the child nodes inside the body of the root node and find instances of the class ast.Assign.
For each assignation, fetch the left & right operands using the attributes targets and value of such nodes
You can do something like this (I assume all values in the assignations are numbers):
import ast
from inspect import getsource
from operator import attrgetter
# This is the function we want to know its variables
def foo():
a, b, c = 1, 2, 3
d = 0
# Get the code and parse it to an ast node
code = getsource(foo)
root = ast.parse(code)
# Get all assignations
assigns = root.body[0].body
for assign in assigns:
# For each assignation...
# Find left operands on the assignation
vars = []
for target in assign.targets:
if isinstance(target, ast.Tuple):
vars.extend(target.elts)
else:
vars.append(target)
# Get operand names
varnames = list(map(attrgetter('id'), vars))
# Also get right operands (we assume are all numbers)
if isinstance(assign.value, ast.Tuple):
values = list(map(attrgetter('n'), assign.value.elts))
else:
values = (assign.value.n,)
# Print info
print(', '.join(varnames) + ' = ' + ', '.join(map(str, values)))
The output of the code above is:
a, b, c = 1, 2, 3
d = 0
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 have written a code to compare the solution of sympy and PARI/GP, how ever I am facing a problem to get an array/vector from PARI/GP.
When I try to return the vector res from PARI/GP function nfroots, I get a address like this (see the last line) -
[3, 4]
elements as long (only if of type t_INT):
3
4
<__main__.LP_LP_c_long object at 0x00000000056166C8>
how can I get the res as vector/array from nfroots so I can use that array like normal python vector/array?
The code is given below to download the libpari.dll file, click here-
from ctypes import *
from sympy.solvers import solve
from sympy import Symbol
pari = cdll.LoadLibrary("libpari.dll")
pari.stoi.restype = POINTER(c_long)
pari.cgetg.restype = POINTER(POINTER(c_long))
pari.gtopoly.restype = POINTER(c_long)
pari.nfroots.restype = POINTER(POINTER(c_long))
(t_VEC, t_COL, t_MAT) = (17, 18, 19) # incomplete
pari.pari_init(2 ** 19, 0)
def t_vec(numbers):
l = len(numbers) + 1
p1 = pari.cgetg(c_long(l), c_long(t_VEC))
for i in range(1, l):
#Changed c_long to c_float, but got no output
p1[i] = pari.stoi(c_long(numbers[i - 1]))
return p1
def Quartic_Comparison():
x = Symbol('x')
#a=0;A=0;B=1;C=-7;D=13/12 #PROBLEM 1
a=0;A=0;B=1;C=-7;D=12
#a=0;A=0;B=-1;C=-2;D=1
solution=solve(a*x**4+A*x**3+B*x**2+ C*x + D, x)
print(solution)
V=(A,B,C,D)
P = pari.gtopoly(t_vec(V), c_long(-1))
res = pari.nfroots(None, P)
print("elements as long (only if of type t_INT): ")
for i in range(1, pari.glength(res) + 1):
print(pari.itos(res[i]))
return res #PROBLEM 2
f=Quartic_Comparison()
print(f)
res is an element from the PARI/C world. It is a PARI vector of PARI integers (t_VEC of t_INTs). Python does not know it.
If it is to be processed further on the Python side, it must be converted. This is generally necessary if data needs to be exchanged between Python and the PARI/C world.
So if you have a t_VEC with t_INTs on the PARI/C side, as in this case, you most likely want to convert it to a Python list.
One possible approach might look like this:
...
roots = pari.nfroots(None, P)
result = []
for i in range(1, pari.glength(roots) + 1):
result.append(pari.itos(roots[i]))
return result
I'm working on a Python script that takes a mathematical function as an input and spits out useful information to draw a curve for that function (tangents, intersection points, asymptote, etc), and the firststep is finding the definition domain of that function (when that function is valid eg: 1/x-2 df=]-∞,2[U]2,+∞[) and I need to do it using sympy.
Down bellow is the WIP code
from sympy import *
from fractions import *
x = symbols('x')
f = Function('f')
f = input('type function: ')
fp = diff(f)
sol = solve(f, x)
sol_p = solve(fp, x)
print(f"f(x)={f},f'(x)={fp}")
#print(f"x1={sol[0]},x2={sol[1]},x'={sol_p}")
print(f'{len(sol)}')
psol = {}
limits_at_edges = {}
df = solveset(f, x, domain=S.Reals)
for i in range(1,( len(sol) + 1)): # Prints out every item in sol[] after aplying .evalf()
psol["x" + str(i) ] = sol[i - 1].evalf()
for i in range(1, len(sol) + 1):
limits_at_edges[f'limit x -> x{i} f(x)'] = limit(f, x, sol[i - 1])
print(f'Solution:{sol}')
print(f'Processes solution:{psol}')
print(f'Derivative solution:{sol_p}')
print(limits_at_edges)
print(f'Domain:{df}')
pprint(f, use_unicode=True)
This question is similar to How to know whether a function is continuous with sympy?
This can be done using continuous_domain as explained here.
First and foremost, I'm not a native english speaker (just to make that clear)
I have written a script for my numeric class in which i computed all necessary functions to pass my class. Now I want to implement everything into a GUI to make things easier.
For the whole testing I worked with a 'test' file to see what types the outputs are and so on. The file is called Numerik_Funktionen1 and is imported to every file im working on. Now here is the problem:
This is a class and function i've written:
class LR:
def LR_zerlegung(self, A, b = 0.):
'''
splits Matrix A in L and R
L = lower triangular matrix -> linke untere Dreiecksmatrix
R = upper triangular matrix -> rechte obere Dreiecksmatrix
returns (L, R) as tupel
'''
self.A = np.copy(A) * 1.
self.ALr = A * 1.
self.n = len(self.A[0])
self.b = b
self.L = np.eye(self.n)
for k in range(self.n-1):
for i in range(self.n)[k+1:self.n]:
self.L[i, k] = self.ALr[i, k] / self.ALr[k, k]
self.ALr[i, k] = 0
for j in range(self.n)[k+1:self.n]:
self.ALr[i, j] = self.ALr[i, j] - self.L[i, k] * self.ALr[k, j]
self.R = self.ALr
print('Ax = b')
print('A', '\n', self.A,"\n")
print('b', '\n', self.b,"\n")
print('L', '\n', self.L,"\n")
print('R', '\n', self.R,"\n")
return self.L, self.R
The problem I run into is best described with code (I now it's neither well written nor good looking code, sorry)
import Numerik_Funktionen1
a = input("a eingeben: ")
b = input("b eingeben: ")
array_from_string = [s.split(',') for s in a.split(';')]
array_from_string2 = [s.split(',') for s in b.split(';')]
c = np.asarray(array_from_string)
d = np.asarray(array_from_string2)
A = c.astype(int)
b = d.astype(int)
The input for a looks like this: 2,3;5,4
and for b 2;4
After the proceeding A is an array
(array([[2, 3],
[5, 4]]))
and b as well (array([[2],
[2]]))
The call of the function looks like this: Numerik_Funktionen1.LR.LR_zerlegung(A, b)
So far so good, but if I want to take the previously described function LR_zerlegung, it returns this: 'numpy.ndarray' object has no attribute 'A'
I don't know what I do wrong and why it won't correspond with the Numerik_Funktionen1. I know my question in not well formulated and im sorry for that in advance.
Thank you
I have a number of symbolic expressions in sympy, and I may come to realize that one of the coefficients is zero. I would think, perhaps because I am used to mathematica, that the following makes sense:
from sympy import Symbol
x = Symbol('x')
y = Symbol('y')
f = x + y
x = 0
f
Surprisingly, what is returned is x + y. Is there any way, aside from explicitly calling "subs" on every equation, for f to return just y?
I think subs is the only way to do this. It looks like a sympy expression is something unto itself. It does not reference the pieces that made it up. That is f only has the expression x+y, but doesn't know it has any link back to the python objects x and y. Consider the code below:
from sympy import Symbol
x = Symbol('x')
y = Symbol('y')
z = Symbol('z')
f1 = x + y
f2 = z + f1
f1 = f1.subs(x,0)
print(f1)
print(f2)
The output from this is
y
x + y + z
So even though f1 has changed f2 hasn't. To my knowledge subs is the only way to get done what you want.
I don't think there is a way to do that automatically (or at least no without modifying SymPy).
The following question from SymPy's FAQ explains why:
Why doesn't changing one variable change another that depends it?
The short answer is "because it doesn't depend on it." :-) Even though
you are working with equations, you are still working with Python
objects. The equations you are typing use the values present at the
time of creation to "fill in" values, just like regular python
definitions. They are not altered by changes made afterwards. Consider
the following:
>>> a = Symbol('a') # create an object with name 'a' for variable a to point to
>>> b = a + 1; b # create another object that refers to what 'a' refers to
a + 1
>>> a = 4; a # a now points to the literal integer 4, not Symbol('a')
4
>>> b # but b is still pointing at Symbol('a')
a + 1
Changing quantity a does not change b; you are not working with a set
of simultaneous equations. It might be helpful to remember that the
string that gets printed when you print a variable refering to a sympy
object is the string that was give to it when it was created; that
string does not have to be the same as the variable that you assign it
to:
>>> r, t, d = symbols('rate time short_life')
>>> d = r*t; d
rate*time
>>> r=80; t=2; d # we haven't changed d, only r and t
rate*time
>>> d=r*t; d # now d is using the current values of r and t
160
Maybe this is not what you're looking for (as it was already explained by others), but this is my solution to substitute several values at once.
def GlobalSubs(exprNames, varNames, values=[]):
if ( len(values) == 0 ): # Get the values from the
for varName in varNames: # variables when not defined
values.append( eval(varName) ) # as argument.
# End for.
# End if.
for exprName in exprNames: # Create a temp copy
expr = eval(exprName) # of each expression
for i in range(len(varNames)): # and substitute
expr = expr.subs(varNames[i], values[i]) # each variable.
# End for.
yield expr # Return each expression.
# End for.
It works even for matrices!
>>> x, y, h, k = symbols('x, y, h, k')
>>> A = Matrix([[ x, -h],
... [ h, x]])
>>> B = Matrix([[ y, k],
... [-k, y]])
>>> x = 2; y = 4; h = 1; k = 3
>>> A, B = GlobalSubs(['A', 'B'], ['x', 'h', 'y', 'k'])
>>> A
Matrix([
[2, -1],
[1, 2]])
>>> B
Matrix([
[ 4, 3],
[-3, 4]])
But don't try to make a module with this. It won't work. This will only work when the expressions, the variables and the function are defined into the same file, so everything is global for the function and it can access them.