# Fighting Less::nord at the root: Any unintended consequences?

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 InternalInheritedBlock 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.

-
Why not use Quiet@Chop? For instance, Quiet@Chop[0. I < 1] gives True – R. M. Oct 11 '12 at 18:09
@rm-rf: Because (a) I don't like wasting a lot of time finding every single instance of where some numerical "error" of zero(!) causes problems (only to then find out that I missed some instance which triggers only rarely, and start the hunt again). Yes, ultimately it is possible to hunt down every instance. But it is a pain. I would rather avoid it. – celtschk Oct 11 '12 at 18:17
It may be more pain to hunt down for consequences of overloading built-ins. They can be pretty unobvious, from huge performance penalties to weird behavior of some completely unrelated functions. At the very least, I would create local environment and use InternalInheritedBlock to localize the effect of these redefinitions. – Leonid Shifrin Oct 11 '12 at 18:24
@celtschk Ok, but I can't help but notice the contradiction in you mentioning the difficulties of hunting down every instance of numerical errors, yet asking a question on hunting down unintended consequences from modifying built-ins :) – R. M. Oct 11 '12 at 18:27
I will also advocate, strongly, against overloading core things like LessEqual, Min, et al. Things one can do include (1) Use of Quiet (has occasional ill effects, I know). (2) Restriction of inputs via Re or Chop. (3) Careful construction of objective and constraints to NMinimize (possibly via item (2)) so that complex values are appropriately handled. In addition to being a bad idea to modify Min/Max, there is no guarantee that doing so will free you from less::nord et al, because there are several ways a comparison test can be made (using Sign, LessEqual, maybe others). – Daniel Lichtblau Oct 11 '12 at 22:15