I am trying to implement scipy.optimmize.minimize on a multivariate scalar function using the nelder-mead method. My function definition when called in a print function works perfectly fine. When I turn on minimize it throws the IndexError.
from scipy.optimize import minimize
import math
import numpy as np
c_ = [1.,1.,1.]
d_ = [1.,1.,1.]
x_=[c_,d_]
def hamiltonian(x_):
N=len(c_)
return np.sum([(1/n**3.0)*(n-i)*i*(x_[0][(n-i)]*x_[0][i]+x_[1][(n-i)]*x_[1][i])-(1/n**3.0/alpha**2.0)*np.sum([x_[0][(n-i-j)]*x_[0][i]*x_[1][j] for j in range(0,(n-i+1))]) for n in range(1,N) for i in range(0,(n+1))])
print hamiltonian(x_) #no problem here
res = minimize(hamiltonian, x_, method='nelder-mead') #problem here
That is because x_ is a list of lists:
>>> x_
[[1.0, 1.0, 1.0], [1.0, 1.0, 1.0]]
Thus replace the line x_=[c_,d_] with:
>>> x_ = c_ + d_
You'll also have to modify your hamiltonian for a 1xN or Nx1 list/array.
The simplest hack that I can think of is:
def hamiltonian(x_):
N=len(c_)
if type(x_[0]) == np.array:
x_ = np.concatenate((x_[0:N], x_[N:]), axis=0)
return np.sum([(1/n**3.0)*(n-i)*i*(x_[0][(n-i)]*x_[0][i]+x_[1][(n-i)]*x_[1][i])-(1/n**3.0/alpha**2.0)*np.sum([x_[0][(n-i-j)]*x_[0][i]*x_[1][j] for j in range(0,(n-i+1))]) for n in range(1,N) for i in range(0,(n+1))])
Related
I am trying to integrate a function on a 2d polygon described by its vertices as follow
import numpy as np
import quadpy
def f(x):
return x[0]
poly = np.array([[0.0, 0.0], [1.0, 0.0], [0, 1], [1,1]])
scheme = quadpy.t2.get_good_scheme(10)
val = scheme.integrate(f, poly)
But I get
QuadpyError: Wrong domain shape.
I really appreciate any kind of help
quad.t2 is for triangles, three points are expected. Your polygon is a square, you have to use quad.c2.
import quadpy
scheme = quadpy.c2.get_good_scheme(7)
val = scheme.integrate(
lambda x : x[0],
[ [[0.0, 0.0], [1.0, 0.0]], [[0.0, 1.0], [1.0, 1.0]] ]
)
val
This gives 0.5, which is easy to get mathematically.
See the link for the way to specify the quadrilateral.
Lets say I have a 2D numpy array, like
arr = array([[0, 0.001 , 0.002], [0.03, 0.04, 0.05], [0.01, 0.002, 0.5], [0.05, 0.8, 0.003]])
and I want to perform a piecewise function on it, say
def gammacor(x):
return np.piecewise(x, [x <= 0.00313, x > 0.00313], [12.92*x, 1.055*x**(1/2.4)-0.055])
gcarr = gammacor(arr)
When I do this, I get an error:
TypeError: NumPy boolean array indexing assignment requires a 0 or 1-dimensional input, input has 2 dimensions
If I try to run the function on the flattened array (with the plan to reshape back to n x 3 after running the function), I get the error:
ValueError: NumPy boolean array indexing assignment cannot assign 3 input values to the 0 output values where the mask is true
Is there an easy way to apply a piecewise function to all elements of a 2D (or ND) array?
The third parameter of np.piecewise is a funclist.
They should be callables:
import numpy as np
arr = np.array([[0, 0.001, 0.002], [0.03, 0.04, 0.05], [0.01, 0.002, 0.5],
[0.05, 0.8, 0.003]])
p = np.piecewise(arr, [arr <= 0.00313, arr > 0.00313],
[lambda v: 12.92 * v,
lambda v: 1.055 * v ** (1 / 2.4) - 0.055])
print(p)
Output:
[[0. 0.01292 0.02584 ]
[0.18974828 0.22091636 0.24780053]
[0.09985282 0.02584 0.73535698]
[0.24780053 0.90633175 0.03876 ]]
def gammacor(x):
return np.piecewise(x, [x <= 0.00313, x > 0.00313],
[lambda v: 12.92 * v,
lambda v: 1.055 * v ** (1 / 2.4) - 0.055])
gcarr = gammacor(arr)
Is there anyway we can use Scipy's leastsq solver (or any other function in python) to find an array instead of a vector?
Basically, I want to find C that minimizes function my_func. I think one way is to convert C to a vector and rewrite the function my_func such that the unknowns are a vector. But, is there a better way?
import numpy as np
from scipy.optimize import leastsq
def my_func(C, x, y):
return y - C.dot(x)
x_data = np.array([2, 3, 4])
y_data = np.array([20, 30])
starting_guess = np.ones((2, 3))
data = (x_data, y_data)
result = leastsq(my_func, starting_guess, args=data)
print result
solution = result[0]
print solution
You can use flatten() and reshape() from the numpy library to go back and forth between 1d and 2d arrays. As for the minimization itself, I suggest scipy.optimize.minimize().
Please note that the difference between two vectors is a vector (I am referring to v = y - C.x here), therefore you need the norm to convert this into a metric that you can minimize. Below, you find a good example on how to code this:
import numpy as np
from scipy.optimize import minimize
def my_func(C_flat, x, y):
# print(np.linalg.norm(y - np.dot(C_flat.reshape(2, 3), x), 2))
return np.linalg.norm(y - np.dot(C_flat.reshape(2, 3), x), 2)
x_data = np.array([2, 3, 4])
y_data = np.array([20, 30])
C0 = np.ones((2, 3))
data = (x_data, y_data)
result = minimize(my_func, C0.flatten(), args = data)
print(result)
solution = result["x"].reshape(2,3)
print(solution)
I am using quad from scipy.integrate to get an integral in a limited range from an object. suppose the target object is in the blow:
∫expm(A*X).expm(B*X)dx
which both A and B are numpy matrix.
To solve this I have used blow code:
from scipy.integrate import quad
from scipy.linalg import expm
import numpy as np
def integrand(X, A, B):
return np.dot(expm(A*X),expm(B*X))
A = np.array([[1, 2], [3, 4]])
B = np.array([[1, 2], [3, 4]])
I= quad(integrand, 0, 1, args=(A,B))
But for the result I get this error:
TypeError: only length-1 arrays can be converted to Python scalars
I know that The error "only length-1 arrays can be converted to Python scalars" is raised when the function expects a single value but you pass an array instead. but my problem is based on array. so how can I fix it.
As pointed in the comments, quad expects a scalar function. You can always pass the function to a scalar by adding the index as an output:
def integrand(X, A, B, ix=None):
""" pass ix=None to return the matrix, ix = 0,1,2,3 to return an element"""
output = np.dot(expm(A*X),expm(B*X))
if ix is None:
return output
i, j = ix//2, ix%2
return output[i,j]
I= np.array([quad(integrand, 0, 1, args=(A,B, i))[0]
for i in range(4)]).reshape(2,2)
I
>>array([[1031.61668602, 1502.47836021],
[2253.71754031, 3285.33422634]])
Note that this is very inefficient since you are calculating the integral 4 times, as long as this doesn't bother you.
Alternatively, use trapz:
x_i = np.linspace(0,1,60)
np.trapz([integrand(x, A, B) for x in x_i], x=x_i, axis=0)
>>array([[1034.46472361, 1506.62915374],
[2259.94373062, 3294.40845422]])
quadpy does vectorized computation. The fact that expm only works on square matrices (and not on lists of square matrices) requires a bit of juggling with the matrix shapes, though.
from quadpy import quad
import numpy as np
from scipy.linalg import expm
A = np.array([[1, 2], [3, 4]])
B = np.array([[1, 2], [3, 4]])
def integrand(X):
expAX = np.array([expm(A * x) for x in X])
expAX = np.moveaxis(expAX, 0, -1)
#
expBX = np.array([expm(B * x) for x in X])
expBX = np.moveaxis(expBX, 0, -1)
return np.einsum("ij...,jk...->ik...", expAX, expBX)
val, err = quad(integrand, 0, 1)
print(val)
[[1031.61668602 1502.47836021]
[2253.71754031 3285.33422633]]
I have an array of element probabilities, let's say [0.1, 0.2, 0.5, 0.2]. The array sums up to 1.0.
Using plain Python or numpy, I want to draw elements proportional to their probability: the first element about 10% of the time, second 20%, third 50% etc. The "draw" should return index of the element drawn.
I came up with this:
def draw(probs):
cumsum = numpy.cumsum(probs / sum(probs)) # sum up to 1.0, just in case
return len(numpy.where(numpy.random.rand() >= cumsum)[0])
It works, but it's too convoluted, there must be a better way. Thanks.
import numpy as np
def random_pick(choices, probs):
'''
>>> a = ['Hit', 'Out']
>>> b = [.3, .7]
>>> random_pick(a,b)
'''
cutoffs = np.cumsum(probs)
idx = cutoffs.searchsorted(np.random.uniform(0, cutoffs[-1]))
return choices[idx]
How it works:
In [22]: import numpy as np
In [23]: probs = [0.1, 0.2, 0.5, 0.2]
Compute the cumulative sum:
In [24]: cutoffs = np.cumsum(probs)
In [25]: cutoffs
Out[25]: array([ 0.1, 0.3, 0.8, 1. ])
Compute a uniformly distributed random number in the half-open interval [0, cutoffs[-1]):
In [26]: np.random.uniform(0, cutoffs[-1])
Out[26]: 0.9723114393023948
Use searchsorted to find the index where the random number would be inserted into cutoffs:
In [27]: cutoffs.searchsorted(0.9723114393023948)
Out[27]: 3
Return choices[idx], where idx is that index.
You want to sample from the categorical distribution, which is not implemented in numpy. However, the multinomial distribution is a generalization of the categorical distribution and can be used for that purpose.
>>> import numpy as np
>>>
>>> def sampleCategory(p):
... return np.flatnonzero( np.random.multinomial(1,p,1) )[0]
...
>>> sampleCategory( [0.1,0.5,0.4] )
1
use numpy.random.multinomial - most efficient
I've never used numpy, but I assume my code below (python only) does the same thing as what you accomplished in one line. I'm putting it here just in case you want it.
Looks very c-ish so apologies for not being very pythonic.
weight_total would be 1 for you.
def draw(probs)
r = random.randrange(weight_total)
running_total = 0
for i, p in enumerate(probs)
running_total += p
if running_total > r:
return i
use bisect
import bisect
import random
import numpy
def draw(probs):
cumsum=numpy.cumsum(probs/sum(probs))
return bisect.bisect_left(cumsum, numpy.random.rand())
should do the trick.