Search code examples
pythonoptimizationscipysurface

Minimize multivariable function


I have two quadric surfaces in 3-dimensional space.

  • circular hyperboloid of one sheet
    • described by xt, yt, zt, rt
  • circular paraboloid
    • described by xs, ys, zs, rs

I would like to minimize the distance between the two objects. Function distance() takes 4 variables - alpha, beta, zt and zs. The goal is to find such values of these 4 variables that the function returns the minimal possible value.

Consider the code below.

import numpy as np
from scipy.optimize import minimize

A = 1; B = 1; C = 1; D = 1; Z = 0;

def distance(alpha,beta,zt,zs):
    """distance between points in 2 quadric surfaces in 3D space"""
    rt = (A/B) * np.sqrt(B**2 + (zt-C)**2)
    xt = rt * np.cos(alpha)
    yt = rt * np.sin(alpha)

    rs = D * np.sqrt(zs-Z)
    xs = rs * np.cos(beta)
    ys = rs * np.sin(beta)

    return (xt-xs)**2 + (yt-ys)**2 + (zt-zs)**2

x0 = np.array([0, 0, 0, 0])
res = minimize(distance,
               x0,
               method='nelder-mead')

The code gives me the following error.

TypeError: distance() missing 3 required positional arguments: 'beta', 'zt', and 'zs'

All documentation I have found uses only univariate (one variable) functions (like the Rosenbrock function) despite it saying it minimizes "multivariate scalar functions".

How do I make my code find the optimal values for the 4 parameters to minimize the value of a function?


Solution

  • Looks like you want to vary all four arguments. You pass their initial values as x0, a 4 element array. That's what minimize will pass to distance. Here's a change to distance that should work with that:

    def distance(x):
        """distance between points in 2 quadric surfaces in 3D space"""
        alpha,beta,zt,zs = x    # unpack x into these 4 variables
        rt = (A/B) * np.sqrt(B**2 + (zt-C)**2)
        xt = rt * np.cos(alpha)
        yt = rt * np.sin(alpha)
    
        rs = D * np.sqrt(zs-Z)
        xs = rs * np.cos(beta)
        ys = rs * np.sin(beta)
    
        return (xt-xs)**2 + (yt-ys)**2 + (zt-zs)**2
    

    The args suggestion would vary alpha and hold the other 3 constant. That doesn't sound like what you want. You already use A, B, C as global constants.

    1115:~/mypy$ python3 stack55751317.py 
     final_simplex: (array([[-1.21456543, -1.21455458,  0.99997997,  0.99997757],
           [-1.21457508, -1.21458998,  0.9999941 ,  1.00000714],
           [-1.21461728, -1.21460427,  1.00002695,  1.00001266],
           [-1.21456081, -1.2145312 ,  0.99996329,  0.99996864],
           [-1.2146315 , -1.21462741,  1.00002628,  1.00004968]]), array([2.49380001e-10, 4.04824635e-10, 4.13486388e-10, 1.15131206e-09,
           1.18130671e-09]))
               fun: 2.4938000073954824e-10
           message: 'Optimization terminated successfully.'
              nfev: 295
               nit: 172
            status: 0
           success: True
                 x: array([-1.21456543, -1.21455458,  0.99997997,  0.99997757])
    

    x looks like the result, which you can access with res['x'].

    Most of the items in this res dictionary are explained at:

    https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.OptimizeResult.html#scipy.optimize.OptimizeResult

    final_simplex is a special output for this minimization method.