How to use scipy.optimize.minimize for function with 3 variables?

Viewed 390

I'm trying to optimise this function:

def voce(strain, sigma_s, sigma_y, epsilon_0):
    stress = sigma_s - (sigma_s - sigma_y)*np.exp(-strain/epsilon_0)
    return stress

by finding the best values for sigma_s, sigma_y and epsilon_0. Strain and stress should be 1 dimensional numpy arrays.

I've tried:

initial_guess = [1, 1, 1]
result = minimize(voce, initial_guess)

but I get "ValueError: can only convert an array of size 1 to a Python scalar"

I'm a bit confused how to use minimise this

1 Answers

The function to be optimized by scipy.optimize.minimize should return a scalar value. Please see here.

That being said, you can implement a for-loop and solve stress for each strain value. Afterward, you can take the sum of the stress values and minimize the sum.

Before that, you need to first re-structure your VOCE function as follows:

import numpy as np

def voce(sigma_s, sigma_y, epsilon_0, strain):
    stress = sigma_s - (sigma_s - sigma_y)*np.exp(-strain/epsilon_0)
    return stress

Now, for optimization, introduce a new function, say fun, where you will pass sigma_s, sigma_y, and epsilon_0 to be minimized and strain (1-D array) as an argument for your VOCE function:

def fun(x, strain):

    sigma_s = x[0]
    sigma_y = x[1]
    epsilon_0 = x[2]

    stress = []
    sum = 0

    for i in strain:
        s = voce(sigma_s, sigma_y, epsilon_0, i)
        stress.append(s)

    sum = np.sum(stress)

    return sum

Now, optimize and print the result as follows:

from scipy.optimize import minimize

# strain = [x,y,z] <-- Assign your strain array here
initial_guess = [1, 1, 1]
res = minimize(fun, initial_guess, method="Nelder-Mead", args=(strain))
print(res)

Furthermore, if you want to fit the VOCE equation to your stress-strain data (meaning you already have measured stress values from an experiment), you can compare the results (calculated stress) of the VOCE equation with the measured stress using mean squared error from sklearn. For this, you could change the fun as:

from sklearn.metrics import mean_squared_error

def fun(x, measured_strain, measured_stress):

    sigma_s = x[0]
    sigma_y = x[1]
    epsilon_0 = x[2]

    calculated_stress = []
    error = 0

    for i in measured_strain:
        s = voce(sigma_s, sigma_y, epsilon_0, i)
        calculated_stress.append(s)

    error = mean_squared_error(measured_stress, calculated_stress)

    return error

where measured_stress is a 1-D array. Now, optimize and print the result as follows:

from scipy.optimize import minimize

# measured_strain = [x,y,z] <-- Assign your measured strain array here
# measures_stress = [a,b,c] <-- Assign your measured stress array here
initial_guess = [1, 1, 1]
res = minimize(fun, initial_guess, method="Nelder-Mead",
               args=(measured_strain, measured_stress))
print(res)
Related