Aug. 24, 2018, 8:20 p.m.

It is useful to be able to optimize the parameters of a function in order to mimimize or maximize some value, while at the same time obeying some constraints on the possible values of the parameter values.

One example of where this comes in useful is for optimizing the allocation of monetary funds to different assets (eg stocks). You have a set of assets you want to consider investing in, and you want to know what portion of your funds to allocate to each one of them in order to maximize your returns. In this case, the constraints are the following:

- You can only allocate a value between 0 to 1.0 for any particular asset.
- because you cannot allocate more money than whats in your fund.

- The sum of allocations must add up to 1.0

We can make use of Scipy's optimization functions to perform optimization with constraints.

Below is a toy example that demonstrates how the minimize function can be used.

The toy problem being addressed is the following:

- The parameters available represent the lengths of the sides of a rectangle.
- We want to find the lengths that will maximize the area of the rectangle, given the following constraints:
- the sum of the sides must be less than 5.
- the length of one of the sides must be exactly twice as big as the other one.

from scipy import optimize def area(w): """ Given the weights (lengths of each side) of a rectangle, it returns the area. """ return w[0] * w[1] def calculate_error(w): """ Since we want the greatest area possible, but are performing a minimization oprtation, we turn the area into a negative as the loss """ return -area(w) def add2history(w): """ Add a new set of weights to the list of historical weight estimates """ historical_weights.append(w) # constraint 1 - the sum of the two sides must be less than 5 def constraint_func1(w): return 5 - w[0] - w[1] # constraint 2 - the length of one side must be twice as long as the other def constraint_func2(w): return w[1] - 2*w[0] constraint = ({"type": "eq", "fun": constraint_func1}, {"type": "ineq", "fun": constraint_func2} ) w0 = [0.1,4.9] # initial guess historical_weights = [w0] # estimates at each iteration of optimization result = optimize.minimize( fun=calculate_error, # func that calculates value we want to optimize x0=w0, # Initial weights method="SLSQP", # Optimization algorithm to use # args=(data), # other positional args to pass to error function # bounds=((0,5), (0,5)), # constrain upper and lower values for weights constraints=constraint, # Constraints callback=add2history, # A function called after every iteration options={"disp":True} # print feedback ) print("Optimal Weights: {}\nOptimal Area: {}\nSum of Lengths: {}".format(result.x, area(result.x), result.x.sum())) print("Historical weights: ", historical_weights)

[OUTPUT] Optimization terminated successfully. (Exit mode 0) Current function value: -5.555555555555555 Iterations: 2 Function evaluations: 8 Gradient evaluations: 2 Optimal Weights: [1.66666667 3.33333333] Optimal Area: 5.555555555555555 Sum of Lengths: 5.0 Historical weights: [[0.1, 4.9], array([1.66666667, 3.33333333]), array([1.66666667, 3.33333333]), array([1.66666667, 3.33333333]), array([1.66666667, 3.33333333])]

Note you can comment without any login by:

- Typing your comment
- Selecting "sign up with Disqus"
- Then checking "I'd rather post as a guest"