After being plagued too much by Less::nord
and companions, I've decided to try to fight it at the root: The comparison operators. I noticed that typically, the bad value contains + 0. I
. And indeed, evaluating 0. I < 1
gives Less::nord
and an unevaluated equation. Interestingly, 0. I == 0
gives True
, but 0. I <= 0
again gives LessEqual::nord
and doesn't evaluate (despite the "or equal" part which should give true).
Therefore I've now written the following code, which according to a few tests seems to completely eliminate Less::nord
and companions from NMinimize
, despite the fact that it only handles the special case of two-element comparison (the Min
and Max
definitions are there because after eliminating (comparison)::nord
, I got Max::nord
from NMinimize
; again, the two-argument definitions seem to suffice):
(Unprotect[#];
Module[{op, rep = True},
#[a_?NumericQ, b_?NumericQ]/;rep :=
Block[{rep = False},
If[Im@a == 0 && Im@b == 0, #[Re@a, Re@b], #[a, b]]]];
Protect[#])& /@ {Less, LessEqual, Greater, GreaterEqual};
Unprotect[Min, Max];
Min[a_?NumericQ, b_?NumericQ] := If[a<b, a, b];
Max[a_?NumericQ, b_?NumericQ] := If[a<b, b, a];
Protect[Min, Max];
After that, I can use complex expressions in NMinimize
without problems, at least in my (admittedly few) test calls, such as:
NMinimize[{(a + I b)(a - I b), a + b == 5}, {a, b}]
(*
==> {12.5\[VeryThinSpace]+0. I,{a->2.5,b->2.5}}
*)
Now, apart from the performance cost this undoubtedly has (I didn't measure, though), and of course the fact that not all possible cases are covered (so I might one day get a non-prevented ::nord
— but in that case, I would have gotten it anyway), could those definitions have some unexpected consequences (especially, wrong results)?
Of course, also improvements are welcome.
Edit:
Unfortunately most of the reactions are basically a general "don't override built-ins", which of course is a good general guideline, but doesn't really answer my question. Let me remind you of another rule of thumb: All absolutes are wrong. I didn't ask "is it in general a good or bad idea to overload built-in functions" (in general is is indeed a bad idea), I asked "are there unintended consequences in this case, apart from what I already mentioned in my question?"
By asking my question I already explicitly acknowledged that there might be unintended consequences. If I blindly assumed that there are none, I would not have asked my question. On the other hand, given the very limited nature of my overload, I cannot imagine any way this might lead to wrong results (and wrong results are what I'm concerned about). I asked to make sure that this is not only my lack of imagination. However from the reactions I conclude that nobody even had a closer look on my code (otherwise it would, for example, have been obvious that LessEqual
is already covered by my code).
The only concrete points which were raised were points I already had acknowledged in my question:
It gives a performance hit. On the other hand, if the performance hit saves me working time, it may well be worth it (especially given the fact that why Mathematica works, I can work on something different, but while I tweak the code, I can't). After all, most of the code I write (especially the code using optimization routines) is not production code to be reused, but one-time code to get a single result, which generally won't ever be run again.
Of course the overload is nothing which belongs into the init file. It is to be applied selectively.
It might not catch all cases. Indeed, I already acknowledged that it does not catch all cases. And that's intentional: It catches exactly as many cases to stop the problem really occurring. By catching as few cases as possible, it also reduces both the risk of introducing bugs, and the performance hit: Every calculation which does not go through my code cannot be affected by it, and the performance hit for that case will be reduced to determining that my code isn't to be used. Also, it is merely a limitation of the code. Any calculation which doesn't get caught by my overload and still gives a
::nord
would also have gotten a::nord
without it, and therefore with my overload I'm no worse off in that case than without it (and I then might find another specific overload which catches that one as well).Note, BTW, that the
Sign
method of comparison works correctly out of the box, unless you test the result with inequality operators (which is what my overload is about).
And about the suggestion to paper over the problem with Quiet
: I'm not concerned about the messages as such, I'm concerned about the wrong results which often occur in that case. Quieting the message doesn't fix that; instead it removes a sign that something bad happens. And there's indeed some irony to suggest a "solution" which is known to sometimes give wrong results over a solution which might possibly give wrong results.
However, there was one constructive suggestion by Leonid Shifrin, which is unfortunately buried in his comment, therefore I quote it here so it doesn't get lost, because it actually is a very good advice:
At the very least, I would create local environment and use Internal`InheritedBlock to localize the effect of these redefinitions.
Leonid, if you make that one a separate answer, I'll accept it (assuming you don't also suggest in that answer to use Quiet
— you don't fight a fire by deactivating the fire alarm). Yes, I'll do so even if you re-state there that in general overloading built-in functions is a bad idea.
Quiet@Chop
? For instance,Quiet@Chop[0. I < 1]
givesTrue
$\endgroup$Internal`InheritedBlock
to localize the effect of these redefinitions. $\endgroup$