subs() for large number of variables - python

I am working on optimization, and I want to code a functions where I can sub() larger number of variables.
for this, I generated a m functions with n variables. For example, let say 3 functions and 2 variables.
num_var=2
x=symbols('x0:num_var')
I generated this functions:
f=[5*x0 + 4*x1 + 6, -4*x0 - 5*x1 - 6, -8*x0 - 10]
and I have a point like:
point=[-2.8,1.74]
If I want to sub() in each function, what is the fastest way to do this?
f[0].subs([(x[0],point[0]),(x[1],point[1])])
(this will give evaluation of first function in point)
And I get three function evaluations in list:
fun_eval=[some number,some number, ... (in size of number of functions)]
The question is how can I write a code to do evaluations for larger number of variables in large number of functions?

>>> from sympy import *
>>> x0, x1 = x = symbols('x:2')
>>> f = [5*x0+4*x1+6, -4*x0-5*x1-6, -8*x0-10]
>>> point = (-2.8, 1.74)
>>> [fi.subs(zip(x, point)) for fi in f]
[-1.04000000000000, -3.50000000000000, 12.4000000000000]

See http://docs.sympy.org/latest/tutorial/basic_operations.html#lambdify. If you want to efficiently evaluate a function at a large number of points, you should use lambdify.
>>> func = lambdify(x, f)
>>> func(*point)
[-1.04, -3.5, 12.399999999999999]
By default, it uses subs, but if you have numpy installed, you can use it for much faster evaluation, by passing module="numpy" to lambdify.
By the way, your symbols('x0:num_var') does not actually use the num_var variable. You need to use symbols('x0:%d' % num_var).

Related

How to not double count sympy numbers

I need for a project to calculate some sort of simplicity of some expressions and among other things I need to assign to each number appearing in the expression a complexity (something like log2(x), such that the complexity is measured in bits, i.e. log2(4)=2 bits). Anyway, for a give sympy expression I have this piece of code:
is_atomic_number = lambda expr: expr.is_Atom and expr.is_number
eq_numbers = [subexpression for subexpression in preorder_traversal(expr) if is_atomic_number(subexpression)]
It mainly works, but if I have something of the form 2*(x+y), that returns as the numbers appearing in the expression (2,2) instead of just 2, which is what I want. Is there a way to make sure that when something gets factored out, I count it only once? Thank you!
The atoms method might be what you want:
>>> (2*(x+y)).atoms(Number)
{2}
The replace method has the option too keep track of the mapping used, but it doesn't handle identical items that were replaced by different values
>>> (2*x + x**2).replace(lambda x:x.is_Number, lambda x: Dummy(), map=True)
(Dummy_1*x + x**Dummy_2, {2: Dummy_2})
So the following might work:
>>> def flatargs(expr):
... if expr.is_Atom: yield expr
... else:
... for a in expr.args:
... for i in flatargs(a): yield i
>>> list(flatargs(2*x+x**2))
[x, 2, 2, x]
>>> from sympy.utilities.iterables import multiset
>>> multiset([i for i in flatargs(2*x+x**2+y/2) if i.is_Number)
{1/2: 1, 2: 2}
If you want the numerator and denominator separately from flatargs you would have to handle the Rational atom as a special case and deal with the cases when numerator or denomitor is 1. But hopefully what is here will get you on your way.

How to use `scipy.optimize.linprog` for a more complicated objective function?

Is there a way to solve a more complicated objective function using scipy.optimize.linprog? The problem takes the form
c^T * x / ((d^T * x)^T*e)
where x = N x 1, c = N x 1, d = N x M and e = M x 1.
Using an explicit function in scipy.optimize.minimize takes too long.
So it turns out this is actually a well know problem in optimization called linear fractional programming. There is a way to transform the variables and solve it with some additional constraints. I am not sure whether scipy linprog will work, but at least there is a path I can see.

How to check if complex number is a whole number

Given a value numerical value x, you can just do this float(x).is_integer() to check if it's an integer. Is there a way to do this for complex numbers?
I'm trying to use list comprehension to take only the integer roots of a polynomial over a finite field which are integers.
[r for r in solve(f,domain=FiniteField(p)) if float(r).is_integer()]
but if the solve function returns complex roots, this doesn't work.
Does anyone know how to either: check if a given (possibly complex number) is an integer OR know if there's a SymPy function to get the roots of a polynomial over a finite field which are integers?
Use the ground_roots function along with the modulus keyword argument. This returns the roots modulo p, with multiplicities. Here's an example:
>>> from sympy import Symbol, ground_roots
>>> x = Symbol('x')
>>> f = x**5 + 7*x + 1
>>> p = 13
>>> ground_roots(f, modulus=p)
{-5: 1, 4: 2}
That says that the roots of poly modulo 13 are -5 and 4, with the root 4 having multiplicity 2.
By the way, it looks to me as though complex numbers are a red herring here: the roots of an integral polynomial over a finite field can't naturally be regarded as complex numbers. The call to solve in your original post is ignoring the domain argument and is simply giving algebraic numbers (which can reasonably be interpreted as complex numbers) as its results, which is probably why you ended up looking at complex numbers. But these aren't going to help when trying to find the roots modulo a prime p.
float.is_integer(z.real) tells you if the real part is integer
If you want to check if imaginary part is zero, you can do this:
In [17]: a=2 + 2j
In [18]: bool(a.imag)
Out[18]: True
In [19]: b=2 + 0j
In [20]: bool(b.imag)
Out[20]: False
Using a list comprehension as in your original question, one could do:
roots = [complex(r) for r in solve(f, domain=FiniteField(p))]
int_roots = [z.real for z in roots if z.real.is_integer() and not z.imag]

Equation to give a number of outputs based on change of one paramater

Here is my equation:
import math
import pandas as pd
import numpy as np
ha = 8.14
k = 0.0187
Do = 0.1738
Di = 0.0138
L = 3
F = 20
Ta = 293
Ts = 113
pi = 3.14159265
Q = (pi*(Ta-Ts))/(((1/ha*Do))+(1/(2*k))*math.log(Do/Di)) * L
h = (Q*3600)/F
Basically, I want the outputs for when F = np.arange(20,100,10)
is this possible?
I tried
a = np.arange(20,100,10)
F = 20 + i
for i in range(a):
print h
not sure why this doesn't work?
any ideas?
When you use numpy you should try to not use explicit loops, as numpy's main virtue is to execute implicitly the loops with the speed of a compiled language.
To come to our problem, it is simply
F = np.arange(20,100,10)
h = Q*3600/F
print h
where I use the array F just as a normal Python variable, and it's up to numpy magics to recognize the need for a loop.
Remember: you can use numpy's arrays in your expressions just like you would use a scalar and, as far as your usage is a sensible one, Python will compute an array-zed result.
Side Note use np.pi instead of 3.14159...
You need to define a function that takes in parameters. then you can call in that function based on a set of parameters. Bellow I show you an example with a sin(x) function because I'm sure you can figure out hwo to adapt it to your needs
import math
points = np.arange(20, 100, 10)
def sine(x, a=1.0, b=2.0):
return a*math.sin(b*x)
for i in points:
print(sine(i,b=i), end=" ")
-0.8509193596391765 0.9978032744219705 -0.8012247906768953 -0.6501275235748956 -0.2620839590180966 -0.7736233386803075 -0.5444763096196569 0.8272184413093554
this will call your sin function with various parameter b and will calculate it in different points x
The way you're doing it should always print out exactly the same number. Since you've already calculated everything. You don't have a function anywhere, and by putting it all in a for loop won't automagically change anything.
Unless it's some for of pandas magic in interpreter I don't know about. (In that case sorry, I'm not a big user of pandas)
Currently your code is giving Q and h a specific value, and never changes that value again.
Explanation:
In the code below
Q = 4
W = Q
print(Q)
print(W)
you are saying: name Q refers to value 4 and name W refers to the current value of Q(that is 4, regardless whether Q changes later on or not).
If you type Q = 10, W will not change:
Q = 10
print(Q)
print(W)
Therefore you need functions that will be calculated each time you call them:
def Q():
return (pi*(Ta-Ts))/(((1/ha*Do))+(1/(2*k))*math.log(Do/Di)) * L
def h():
return (Q()*3600)/F
for i in np.arange(20,100,10):
F = 20 + i
print h()
The above explanation is a bit oversimplified, so you might want to check this link.

How to generate random variables and sum all them in python

My problem explicitly is
Z=sum_(i)^12 (x_i).
where i is indices and x_i's are random number...
I need an explicit code in Python to produce 12 random variables and sum all them.
I tried write code using if, while loop, but I could not get it.
I need your help...
In order to be able to use arbitrary variable just structure it as a function.
You can structure it similar to l82Munch, but this may be more readable for you since your just starting. Note that range is a generator function that returns a list up to the last call. So range(1,3) returns [1,2]
import random
def rand_sum(i, j):
sum_list = []
for rand_num in range(i, j+1):
sum_list.append(random.random()) # Check random docs for a function that returns
return sum(sum_list) # a different set of randoms if this isn't
# appropriate
import random
rand_sum = sum( random.random() for x in range(12) )
See the random documentation for more info.
In probabilistic modelling, you can define distributions then sum them.
Personally, I use OpenTURNS platform for that.
import openturns as ot
x1 = ot.Normal(0, 2) # Normal distribution mean = 0, std = 2
x2 = ot.Uniform(3, 5) # Uniform distribution between 3 and 5
sum = x1 + x2
That's it.
If x1,..., x12 are 12 distributions identically distributed you can write:
sum_12 = sum([x1] * 12)

Categories