1

I have a particular function which calculates average cost of electricity ($/MWh) over the lifetime of a power plant.

An example function looks like this

def calc(a,b,c):
     res = 65*a+74*b+12*c
     return res

Where a b and c are cost parameters, such as operating expenditure, construction cost and insurance.

I could vary a b and c in an infinite number of ways, but I would like to keep the ratios the same as an example data point I have, with a lower result for average cost of electricity.

For example When a=1, b=2 and c=3, res = 249.

However, I would like to find out the optimal values, which keeps the same original ratios, for a b and c when res=600

I have tried to figure out a way to do this using scipy.optimize, but with some difficulty.

I'm not sure how I would program in the ratios for the constraints.

Many thanks.

AKE
  • 111
  • 1
  • 8
  • Possible duplicate of [How do I use a minimization function in scipy with constraints](https://stackoverflow.com/questions/18767657/how-do-i-use-a-minimization-function-in-scipy-with-constraints) – G. Anderson Nov 26 '18 at 21:35
  • Thank you for this. However, I was wondering how to define the constraints of ratios for lots of different parameters. I have 7 parameters in my example. Thanks. – AKE Nov 26 '18 at 21:46
  • Which ratios exactly do you want to be equal? If you have 3 numbers, there are 3 ratios you will have to satisfy - in the end, you can only change your numbers by an overall multiplicative factor (that is the same for every parameter) – rammelmueller Nov 26 '18 at 22:05
  • Thank you for your comment @rammelmueller. Please see my response to Bill M.'s answer. – AKE Nov 26 '18 at 22:26

3 Answers3

1

Let's say you have two sets of values, (a_old, b_old, c_old) and (a_new, b_new, c_new). If you want their respective ratios to be the same (e.g., a_old:c_old is the same as a_new:c_new, and c_old:b_old is the same as c_new:b_new, and so on), then that's the same as saying there exists some constant k such that a_new = k*a_old, b_new = k*b_old, and c_new = k*c_old.

In your example, 65*a_old + 74*b_old + 12*c_old = 249. If you multiply both sides of this equation by k, you get 65(k*a_old) + 74(k*b_old) + 12(k*c_old) = 249*k. This is the same as '65(a_new) + 74(b_new) + 12(c_new) = 249k'.

You want 249*k to be equal to 600. Therefore, k = 600/249 = about 2.4096. You can then use this k value along with a_old, b_old, c_old to find the values of a_new, b_new, c_new. Remember the new values are just k times the old values.

Here's a function that returns the set of scaled parameter values:

def optimize(a,b,c, opt_res):
    res = 65 * a + 74 * b + 12 * c
    k = opt_res/res
    new_vals = [parameter * k for parameter in [a,b,c]]
    return new_vals

print(optimize(1,2,3,600.0))

## output: [2.4096385542168677, 4.819277108433735, 7.2289156626506035]

Note I used "600.0", not "600". This forces Python to use floats instead of doing everything with truncated integers.

Bill M.
  • 1,388
  • 1
  • 8
  • 16
  • 1
    Thank you for your answer. Sorry, I should have made more clear, in this example problem this would be the best solution, but in my actual project I need to use constraints as the calculations to calculate a, b and c are rather complicated. – AKE Nov 26 '18 at 22:26
  • You're welcome, but I'm sorry I don't quite understand what you need here. Maybe you can show where `scipy.optimize()` is giving you errors. – Bill M. Nov 26 '18 at 23:05
  • 1
    it really does not matter, how many parameters you want to have - if the ratios are the same, you'll always have this issue. without knowing the exact form and constraints, it will be challenging to do anything here. – rammelmueller Nov 26 '18 at 23:15
1

From this answer, you can specify the constraints like this:

cons = [{'type':'eq', 'fun': con1},
        {'type':'eq', 'fun': con2}]

and use the minimize function like this:

scipy.optimize.minimize(func, x0, constraints=cons)
Pedro Borges
  • 1,240
  • 10
  • 20
1

I managed to come to a solution which helped my particular use-case, even though it was pointed out that there was a simpler solution for this particular example.

from scipy.optimize import minimize
import numpy as np

a = 1
b = 2
c = 3

def calc(x):
    res = 65*x[0]+74*x[1]+12*x[2]
    return res

cons = [{'type': 'eq', 'fun': lambda x: x[0]/x[1]-a/b},
        {'type': 'eq', 'fun': lambda x: x[1]/x[2]-b/c},
        {'type': 'eq', 'fun': lambda x: calc(x)-600}]

start_pos = np.ones(3)*(1/6.)

print(minimize(calc, x0=start_pos, constraints=cons))

The constraints keep the same ratios, and set the result of calc to equal 600.

AKE
  • 111
  • 1
  • 8