# Why Min cannot be used as constrains of NMinimize?

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?

• Since the first entry in the function result is always the minimum, why not just use it?
– ciao
Jan 20, 2014 at 7:05
• @rasher, the math used in this code is just an easy example, my real problem is much more complex than it :) Jan 20, 2014 at 8:14

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

• @rasher Frankly speaking, warnings of Mathematica are usually discouraging at first sight, but it's really helpful for debugging if we read carefully. Jan 20, 2014 at 11:09
• @xzczd, Your definition of min[x_List] subtly postpones the evaluation of min@f[m1, m2] until the desired state is reached. This is a very nice trick. I was thinking something like to use Hold and ReleaseHold to do the same, but faild to find a way to actually insert these command inside NMinimize... Thank you for your answer! And you have my gratitude for showing the subtle trick. Jan 21, 2014 at 3:06