Search code examples
pythonoptimizationcomplex-numbers

Optimization to find minimum of function containing complex numbers


I'm trying to use Python to find the value of a vector z that corresponds to the minimum of a function. z could be either rectangular, z = a+b*j or polar form, z = magnitude,phase. I've tried to input it a few ways and currently have it as z = complex(cwx,cwy) in the code block below. I used meshgrid in an attempt to create the space of possible vectors over which to evaluate the function.

The function contains z and other complex numbers (vectors in polar form that I'm converting to rectangular). The function evaluates to a scalar so there should be some vector that results in a minima. Hoping someone can point me in the right direction as I'm new to optimization and floundering so far :| Is there an optimization function that plays nicely with complex numbers?

from math import radians
from cmath import rect
import numpy as np
import scipy.optimize as opt

# input variables, to be obtained via GUI
w1 = 0.5
w2 = 0.5
a1m = 53
a1p = 280
a2m = 46
a2p = 245
s11m = 1.8
s11p = 338
s21m = 1.1
s21p = 332
cwm_max = 500

f = lambda z: (w1*abs(rect(a1m,radians(a1p)) + complex(cwx,cwy) / rect(s11m,radians(s11p))) +
                w2*abs(rect(a2m,radians(a2p)) + complex(cwx,cwy) / rect(s21m,radians(s21p))))

# create a grid of potential vectors wrapping from 0 to 360 degress with max amplitude = cwm_max:
cwx, cwy = np.meshgrid(np.linspace(-cwm_max,cwm_max,200),np.linspace(-cwm_max,cwm_max,200))

# opt.minimze requires a guess argument
guess = np.array([0, 200])
sol = opt.minimize(f, guess)
print(sol)

It doesn't like my use of complex(cwx,cwy) in the function. Thinks I want to input a scalar?

f = lambda z: (w1*abs(rect(a1m,radians(a1p)) + complex(cwx,cwy) / rect(s11m,radians(s11p))) +
                                                   ^^^^^^^^^^^^^^^^
TypeError: only length-1 arrays can be converted to Python scalars

and opt.minimize() doesn't look to be the best tool for the job as that is throwing a variety of Traceback issues.


Brute force alternative with nested loops did the trick:

cwm_max = 500 
dim = 1200 
mag_sum = 1000*np.ones((dim,dim)) 

cwx = np.linspace(-cwm_max,cwm_max,dim)
cwy = np.linspace(-cwm_max,cwm_max,dim)

for p in range(dim):
    for q in range(dim):
        mag_sum[p,q] = (w1 * abs(rect(a1m, radians(a1p)) + complex(cwx[p],cwy[q]) / rect(s11m, radians(s11p)))+ w2 * abs(rect(a2m, radians(a2p)) + complex(cwx[p],cwy[q]) / rect(s21m, radians(s21p))))

# rectangular to polar
def polar_deg(z):
    """expects a single complex number in cartesian form (a+bj),
    returns mag & phase (degrees, 0 to 360)"""
    mag, phase_r = cmath.polar(z)
    phase_d = math.degrees(phase_r)
    if phase_d < 0:
        phase_d += 360
    return mag, phase_d

a,b = np.unravel_index(mag_sum.argmin(), mag_sum.shape)
cwz = complex(cwx[a],cwy[b])
cwm, cwp = polar_deg(cwz)

Solution

  • Brute force alternative with nested loops did the trick:

    cwm_max = 500 
    dim = 1200 
    mag_sum = 1000*np.ones((dim,dim)) 
    
    cwx = np.linspace(-cwm_max,cwm_max,dim)
    cwy = np.linspace(-cwm_max,cwm_max,dim)
    
    for p in range(dim):
        for q in range(dim):
            mag_sum[p,q] = (w1 * abs(rect(a1m, radians(a1p)) + complex(cwx[p],cwy[q]) / rect(s11m, radians(s11p)))+ w2 * abs(rect(a2m, radians(a2p)) + complex(cwx[p],cwy[q]) / rect(s21m, radians(s21p))))
    
    # rectangular to polar
    def polar_deg(z):
        """expects a single complex number in cartesian form (a+bj),
        returns mag & phase (degrees, 0 to 360)"""
        mag, phase_r = cmath.polar(z)
        phase_d = math.degrees(phase_r)
        if phase_d < 0:
            phase_d += 360
        return mag, phase_d
    
    a,b = np.unravel_index(mag_sum.argmin(), mag_sum.shape)
    cwz = complex(cwx[a],cwy[b])
    cwm, cwp = polar_deg(cwz)