Concatenation/creation of symbolic expressions/statements - python

I want to create something like this programmatically:
a = (_vec, T.set_subtensor(_vec[0], _init[0]))[1]
b = (a, T.set_subtensor( a[1], a[0] * 2))[1]
c = (b, T.set_subtensor( b[2], b[1] * 2))[1]
vec_update = (c, T.set_subtensor(c[3], c[2] * 2))
test_vector = function([], outputs=vec_update)
subt = test_vector()
We got a = (_vec, T.set_subtensor(_vec[0], _init[0]))[1] so a is that whole statement. This is not doing anything yet. Then there is b = (a, T.set_subtensor( a[1], a[0] * 2))[1] which is depending on a and is another statement itself.This goes on until vec_update. I know it looks ugly but it just updates the column of a vector like col[n] = col[n-1] * 2 for col[0] = 1 returning a vector looking like this:
[[ 1. 2. 4. ..., 32. 64. 128.]]
Now Imagine I would want to do this a thousand times.. therefore I am wondering if I could generate such statements since they follow an easy pattern.
These "concatenated" statements are not evaluated until
test_vector = function([], outputs=vec_update)
which is when they are getting compiled to CUDA-code and
subt = test_vector()
does execute everything.

You can use function nesting:
def nest(f, g):
def h(x):
return f(g(x), x)
return h
expr = lambda (a,b) : a
expr = nest((lambda x, (a, b): x + (a - b)), expr)
expr = nest((lambda x, (a, b): x + (a - b)), expr)
print expr((1,2)) # prints -1
Regarding the example code, you could do something like (modifying nest to use no arguments):
def nest(f, g):
def h():
return f(g())
return h
expr = lambda: (_vec, _init)[1]
expr = nest(lambda x: T.set_subtensor(x[1], x[0] * 2)[1], expr)
expr = nest(lambda x: T.set_subtensor(x[2], x[1] * 2)[1], expr)
expr = nest(lambda x: T.set_subtensor(x[3], x[2] * 2)[1], expr)
expr = nest(lambda x: T.set_subtensor(x[4], x[3] * 2)[1], expr)
test_vector = function([], outputs=expr)
subt = test_vector()

Related

How to make this float precision without using print

I'm working on assignment, it's about numerical method regarding to trapezoidal rule
def trapezoidalRule(F,a,b,n):
h = float(b-a)/n
f_sum = 0
for i in range(1, n, 1):
x = a + i * h
f_sum = f_sum + f(x)
return h * (0.5 * f(a) + f_sum + 0.5 * f(b))
def f(x):
return x**3
a = 2
b = 10
n = 512
print('%.16f' %trapezoidalRule(f, a, b, n))
And the output is
2496.0058593750000000
My question is, how do i get a precission like that.. without using print('%.16f' %trapezoidalRule(f, a, b, n)). I want to append the result to the list, with exact value like that..
I already tried to google it, but i found nothing related to this problem, can somebody tell me the solution if i want to it ?
Change your return statement in trapezoidalRule to be formatted with 16 points of precision, do note that this is going to cause it to become a string as if you cast it back to float you'll lose the trailing 0's.
def trapezoidalRule(F,a,b,n):
h = float(b-a)/n
f_sum = 0
for i in range(1, n, 1):
x = a + i * h
f_sum = f_sum + f(x)
return format((h * (0.5 * f(a) + f_sum + 0.5 * f(b))), '.16f')
def f(x):
return x**3
a = 2
b = 10
n = 512
See the return line in trapezoidalRule so now if I print the exact output of trapezoidalRule like so: print(trapezoidalRule(f, a, b, n)) with no formatting I get:
2496.0058593750000000
To increase precision try using decimal module
import decimal
def trapezoidalRule(F,a,b,n):
h = decimal.Decimal(float(b-a)/n)
f_sum = 0
for i in range(1, n, 1):
x = a + i * h
f_sum = f_sum + f(x)
return h * (decimal.Decimal(0.5) * f(a) + f_sum + decimal.Decimal(0.5) * f(b))
def f(x):
return decimal.Decimal(x**3)

Why doesn't SymPy simplify the expression?

I am just looking at the Python module SymPy and try, as a simple (useless) example the fit of a function f(x) by a function set g_i(x) in a given interval.
import sympy as sym
def functionFit(f, funcset, interval):
N = len(funcset) - 1
A = sym.zeros(N+1, N+1)
b = sym.zeros(N+1, 1)
x = sym.Symbol('x')
for i in range(N+1):
for j in range(i, N+1):
A[i,j] = sym.integrate(funcset[i]*funcset[j],
(x, interval[0], interval[1]))
A[j,i] = A[i,j]
b[i,0] = sym.integrate(funcset[i]*f, (x, interval[0], interval[1]))
c = A.LUsolve(b)
u = 0
for i in range(len(funcset)):
u += c[i,0]*funcset[i]
return u, c
x = sym.Symbol('x')
f = 10*sym.cos(x)+3*sym.sin(x)
fooset=(sym.sin(x), sym.cos(x))
interval = (1,2)
print("function to approximate:", f)
print("Basic functions:")
for foo in fooset:
print(" - ", foo)
u,c = functionFit(f, fooset, interval)
print()
print("simplified u:")
print(sym.simplify(u))
print()
print("simplified c:")
print(sym.simplify(c))
The result is the fit function u(x), to be returned, together with the coefficients by functionFit.
In my case
f(x) = 10 * sym.cos(x) + 3 * sym.sin(x)
and I want to fit it according to a linear combination of sin(x), cos(x).
So the coefficients should be 3 and 10.
The result is OK, but for u(x) I get
u(x) = (12*sin(2)**2*sin(4)*sin(x) + 3*sin(8)*sin(x) + 12*sin(2)*sin(x) + 40*sin(2)**2*sin(4)*cos(x) + 10*sin(8)*cos(x) + 40*sin(2)*cos(x))/(2*(sin(4) + 2*sin(2))) :
Function to approximate: 3*sin(x) + 10*cos(x)
Basic functions:
- sin(x)
- cos(x)
Simplified u: (12*sin(2)**2*sin(4)*sin(x) + 3*sin(8)*sin(x) + 12*sin(2)*sin(x) + 40*sin(2)**2*sin(4)*cos(x) + 10*sin(8)*cos(x) + 40*sin(2)*cos(x))/(2*(sin(4) + 2*sin(2)))
Simplified c: Matrix([[3], [10]])
which is indeed the same as 10 * cos(x) + 3 * sin(x).
However I wonder why it is not simplified to that expression. I tried several simplifying function available, but none of it gives the expected result.
Is there something wrong in my code or are my expectations to high?
Don't know if this is a solution for you, but I'd simply use the .evalf method of every Sympy expression
In [26]: u.simplify()
Out[26]: (12*sin(2)**2*sin(4)*sin(x) + 3*sin(8)*sin(x) + 12*sin(2)*sin(x) + 40*sin(2)**2*sin(4)*cos(x) + 10*sin(8)*cos(x) + 40*sin(2)*cos(x))/(2*(sin(4) + 2*sin(2)))
In [27]: u.evalf()
Out[27]: 3.0*sin(x) + 10.0*cos(x)
In [28]:

Calculating derivative of trinomial using the function as a parameter

I need to calculate the derivative of a trinomial function in another function using the former as a parameter. This is my code so far:
def derivative(func):
num=func(a,b,c)
third=func(0,0,c)
first=func(a,0,0)
return (num-third)/x+first/x
def make_quadratic(a, b, c):
return lambda x: x*x*a+b*x+c
I suppose that by using the make_quadratic function I had to add 3 parameters (a,b,c) in func as well. What I am trying to do is remove c, divide with x and then add ax so that the resulting derivative is 2ax+b, I cannot however run the code since for some reason a,b,c are not defined even though I do give them values when I call the function.
Write yourself a class like the following one:
class TrigonomialFunction:
def __init__(self, a, b, c):
self.a, self.b, self.c = a, b, c
def calculate(self, x):
return self.a * x ** 2 + self.b * x + self.c
def derivate(self):
return TrigonomialFunction(0, 2 ** self.a, self.b)
def __str__(self):
return "f(x) = %s x**2 + %s * x + %s" % (self.a, self.b, self.c)
Which can then get used in the following way:
f = TrigonomialFunction(2, 3, -5)
print(f) # will return f(x) = 2 x**2 + 3 * x + -5
print(f.calculate(1)) # returns 0
print(f.calculate(-1)) # returns -6
derivateOfF = f.derivate()
print(derivateOfF) # returns f(x) = 0 x**2 + 4 * x + 3 which can be simplified to f(x) = 4*x + 3
print(derivateOfF.calculate(0)) # returns 3
derivateOfderivateOfF = derivateOfF.derivate()
print(derivateOfderivateOfF) # guess ;)
You can calculate the derivative of any function using sympy.
For your function, you can use:
import sympy
def get_derivative(func, x):
return sympy.diff(func, x)
def make_quadratic(a, b, c):
x = sympy.symbols('x')
func = a * x**2 + b * x + c
return func, x
func, x = make_quadratic(1, 2, 3)
print(get_derivative(func, x))
Which returns 2*x + 2

Python lambda : Reducing typing and invocation

I have written the following code to find the area of triangle in terms of its three sides a, b and c:
s = lambda a, b, c : (a + b + c)/2
area_tri = lambda a, b, c : (s(a,b,c) * (s(a,b,c)-a) * (s(a,b,c)-b) * \
(s(a,b,c)-c)) ** 0.5
area_tri(1, 2, 3)
Output: 7.3484692283495345
I have two questions. First, how to reduce the repeated typing of s(a,b,c) in the lambda function for area_tri? Secondly, conjoined to the first question, how to reduce the number of invocations to lambda s in area_tri?
There is a way:
area_tri = lambda a, b, c: (lambda x=s(a,b,c): (x * (x-a) * (x-b) * (x*c)) ** 0.5)()
Without a lambda but in one line it can be a bit shorter:
def area_tri(a, b, c): x = s(a,b,c); return (x * (x-a) * (x-b) * (x*c)) ** 0.5
However unless you're golfing, this should be properly formatted:
def area_tri(a, b, c):
x = s(a,b,c)
return (x * (x-a) * (x-b) * (x*c)) ** 0.5

How to find reverse of pow(a,b,c) in python?

pow(a,b,c) operator in python returns (a**b)%c . If I have values of b, c, and the result of this operation (res=pow(a,b,c)), how can I find the value of a?
Despite the statements in the comments this is not the discrete logarithm problem. This more closely resembles the RSA problem in which c is the product of two large primes, b is the encrypt exponent, and a is the unknown plaintext. I always like to make x the unknown variable you want to solve for, so you have y= xb mod c where y, b, and c are known, you want to solve for x. Solving it involves the same basic number theory as in RSA, namely you must compute z=b-1 mod λ(c), and then you can solve for x via x = yz mod c. λ is Carmichael's lambda function, but you can also use Euler's phi (totient) function instead. We have reduced the original problem to computing an inverse mod λ(c). This is easy to do if c is easy to factor or we already know the factorization of c, and hard otherwise. If c is small then brute-force is an acceptable technique and you can ignore all the complicated math.
Here is some code showing these steps:
import functools
import math
def egcd(a, b):
"""Extended gcd of a and b. Returns (d, x, y) such that
d = a*x + b*y where d is the greatest common divisor of a and b."""
x0, x1, y0, y1 = 1, 0, 0, 1
while b != 0:
q, a, b = a // b, b, a % b
x0, x1 = x1, x0 - q * x1
y0, y1 = y1, y0 - q * y1
return a, x0, y0
def inverse(a, n):
"""Returns the inverse x of a mod n, i.e. x*a = 1 mod n. Raises a
ZeroDivisionError if gcd(a,n) != 1."""
d, a_inv, n_inv = egcd(a, n)
if d != 1:
raise ZeroDivisionError('{} is not coprime to {}'.format(a, n))
else:
return a_inv % n
def lcm(*x):
"""
Returns the least common multiple of its arguments. At least two arguments must be
supplied.
:param x:
:return:
"""
if not x or len(x) < 2:
raise ValueError("at least two arguments must be supplied to lcm")
lcm_of_2 = lambda x, y: (x * y) // math.gcd(x, y)
return functools.reduce(lcm_of_2, x)
def carmichael_pp(p, e):
phi = pow(p, e - 1) * (p - 1)
if (p % 2 == 1) or (e >= 2):
return phi
else:
return phi // 2
def carmichael_lambda(pp):
"""
pp is a sequence representing the unique prime-power factorization of the
integer whose Carmichael function is to be computed.
:param pp: the prime-power factorization, a sequence of pairs (p,e) where p is prime and e>=1.
:return: Carmichael's function result
"""
return lcm(*[carmichael_pp(p, e) for p, e in pp])
a = 182989423414314437
b = 112388918933488834121
c = 128391911110189182102909037 * 256
y = pow(a, b, c)
lam = carmichael_lambda([(2,8), (128391911110189182102909037, 1)])
z = inverse(b, lam)
x = pow(y, z, c)
print(x)
The best you can do is something like this:
a = 12
b = 5
c = 125
def is_int(a):
return a - int(a) <= 1e-5
# ============= Without C ========== #
print("Process without c")
rslt = pow(a, b)
print("a**b:", rslt)
print("a:", pow(rslt, (1.0 / b)))
# ============= With C ========== #
print("\nProcess with c")
rslt = pow(a, b, c)
i = 0
while True:
a = pow(rslt + i*c, (1.0 / b))
if is_int(a):
break
else:
i += 1
print("a**b % c:", rslt)
print("a:", a)
You can never be sure that you have found the correct modulo value, it is the first value that is compatible with your settings. The algorithm is based on the fact that a, b and c are integers. If they are not you have no solution a likely combination that was the original one.
Outputs:
Process without c
a**b: 248832
a: 12.000000000000002
Process with c
a**b % c: 82
a: 12.000000000000002

Categories