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)
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)