I met a strange problem when trying to do an optimization.
Initialization code,
goal = 2.;
vector = {1.0, 1.1, 1.2, 1.3, 1.4, 1.5};
f[m1_Real, m2_Real] := Block[{v},
v = Map[# + m1*Sin[Norm[{m1, m2}]] &, vector];
Clip[v, {0, goal}]];
g[m1_Real, m2_Real] := Max[(goal - f[m1, m2])/goal] + Norm[{m1, m2}];
where f is a vector function and g is a scaler function. I want to minimize g[m1,m2] with constrains 0.8 <= Min[f[m1,m2]] <= 1.0 and 0 <= m1 <= 2 && 0 <= m2 <=2. Despite this seems straightforward, a direct implementation is problematic,
counter = 0;
NMinimize[{
g[m1, m2],
And[0.8 <= Min[f[m1, m2]]/goal <= 1.0, 0 <= m1 <= 2, 0 <= m2 <= 2]
}, {m1, m2},
Method -> {"NelderMead", "Tolerance" -> .001},
StepMonitor :> (Print[++counter, ":\t", {m1, m2}])]
If we execute the code, an error message NMinimize::bcons will be shown, which tells us that the constrains are not in the valid format.
After a few tests I found out that the the problem is related to the command Min used in the first constrain 0.8 <= Min[f[m1, m2]]/goal <= 1.0. If we do not use Min then there will be no problem, i.e., 0.8 <= Evaluate[Norm[f[m1, m2]/goal]] <= 1.0 can be evaluated without any problem.
So it seems that Min is not valid for using as constrains with NMizimize (yet we can use Norm, Plus, etc.). But this is really inconvenient because I do need to use Min as part of the constrains. I wonder if we can fix this problem?
Answer
To solve your problem, let's first take a good look at the warning generated by your code:
NMinimize::bcons: The following constraints are not valid:
{0 <= m1, 0 <= m2, 0.8 <= 0.5 f[m1, m2], m1 <= 2, m2 <= 2, 0.5 f[m1, m2] <= 1.}.
Constraints should be equalities, inequalities, or domain
specifications involving the variables. >>
What have you found? Our constraints are not valid, yeah, we know that, but what else? Oh, we see the constraints have been displayed by Mathematica and the form of them have been slightly changed, um, that's understandable, and we can easily find the correspondence between them and their original, 0 <= m1 and 0 <= m2 and m1 <= 2 and m2 <= 2 are for 0 <= m1 <= 2 and 0 <= m2 <= 2, 0.8 <= 0.5 f[m1, m2] and 0.5 f[m1, m2] <= 1. are…… wait, where's the Min? Where did it go? Mathematica don't like it so she deleted it?
Of course not, Min disappeared because it has been calculated before f[m1, m2] become number lists. What happened here is just same as:
Clear[m1, m2]
Min@f[m1, m2]
f[m1, m2]
This can be approved by Trace:
NMinimize[{g[m1, m2],
And[0.8 <= Min@f[m1, m2]/goal <= 1.0, 0 <= m1 <= 2, 0 <= m2 <= 2]},
{m1, m2},
Method -> {"NelderMead", "Tolerance" -> .001}] // Trace
{{{0.8 <= Min[f[m1, m2]]/goal <= 1. && 0 <= m1 <= 2 &&
0 <= m2 <= 2, {{{Min[f[m1, m2]], f[m1, m2]}…………
To fix your code, you can definite a new function that calculates only if its argument is a list:
min[x_List] := Min[x]
counter = 0;
(* I removed your Method option since it seems to be unnecessary. *)
NMinimize[{g[m1, m2],
And[0.8 <= min@f[m1, m2]/goal <= 1.0, 0 <= m1 <= 2, 0 <= m2 <= 2]},
{m1, m2},
StepMonitor :> (Print[++counter, ":\t", {m1, m2}])]
{1.02036, {m1 -> 0.820357, m2 -> 2.34313*10^-8}}
Comments
Post a Comment