5
$\begingroup$

Please can someone explain to me why the first of the following fails to run (just spitting back the input without warnings/errors), while the second executes successfully?

NSolve[{x Tan[x 5 10^-3] == -5 y Tan[y 0.001], x^2 - y^2 == 10000, 
  0 <= x <= 10000}, {x, y}]
NSolve[{x Tan[x 5 10^-3] == -5 y Tan[y 1 10^-3], x^2 - y^2 == 10000, 
  0 <= x <= 10000}, {x, y}]

They should give the same output. Thanks for any help.

$\endgroup$
2
  • 5
    $\begingroup$ it is exact vs. not issue. Tan[y 0.001] is not exact but Tan[y 1 10^-3] is. As to why this makes difference, I do not know. But if one is exact and the other is not, then different code path is used internally and that what made the difference. Should it have made difference? probably not. It could be when it is exact some simplification or some other logic was applicable, but not when it is not exact? Notice that if just one term is not exact, then the whole expression becomes not exact. Not just that one part. $\endgroup$
    – Nasser
    Commented Jul 25, 2022 at 11:52
  • 5
    $\begingroup$ The current arrangement seems like it's a scaling problem waiting to happen. If you rescale variables, and also arrange things in terms of sine and cosine instead of tangent, you get something better: {200 x, 1000 y} /. NSolve[{200. x Sin[x] Cos[y] == -5000. y Sin[y] Cos[x], x^2 - 25. y^2 == 0.25, 0 <= x <= 50}, {x, y}] $\endgroup$ Commented Jul 25, 2022 at 11:56

1 Answer 1

4
$\begingroup$

@Nasser seems to be correct about different code paths. Personally, I will argue it's a bug, and it definitely should be reported to WRI tech support for them to consider. NSolve and Solve have at least one way to handle inexact numbers in input, namely, "direct rationalization." It turns out that both fail unless the user does "direct rationalization." Really, it does not seem too much to ask that these superfunction solvers do it for the user.

The road not taken

The exact input gets to go down the internal solver pathway, Reduce`TranscendentalReduce; the inexact does not:

exactRed = Trace[
  NSolve[
   x Tan[x/200] == -5 y Tan[10^-3 y] && x^2 - y^2 == 10000 &&
    0 <= x <= 10000, {x, y}],
  tr_Reduce`TranscendentalReduce :> Return[Hold[tr], Trace],
  TraceInternal -> True]
(*
Hold[Reduce`TranscendentalReduce[{{}, 
   x Tan[x/200] + 5 y Tan[y/1000] == 0 && -10000 + x^2 - y^2 == 
     0 && -x <= 0 && -10000 + x <= 0, {}, {x, 
    y}, {}, {Dispatch[{x -> {Reals}, y -> {}}], {x -> {Reals}, 
     y -> {}}}, Automatic, 
   True, {Backsubstitution -> False, Cubics -> Automatic, 
    Quartics -> Automatic, WorkingPrecision -> 16.857679757182947`}}, 
  Automatic]]
*)

Trace[
 NSolve[x Tan[x/200] == -5 y Tan[0.001 y] && x^2 - y^2 == 10000 && 
   0 <= x <= 10000, {x, y}],
 tr_Reduce`TranscendentalReduce :> Return[Hold[tr], Trace],
 TraceInternal -> True]

(*  {}  *)

Workaround #1

We can try the Reduce`TranscendentalReduce road on the inexact by substituting Tan[0.001 y] into the exact code. Evaluating it gives multiple warnings that the equations derived from the input are being subject to "direct rationalization", but you get the same answers at machine precision.

approxRed = exactRed /. Tan[y/1000] -> Tan[0.001 y]

exactRed = ReleaseHold@exactRed
approxRed = ReleaseHold@approxRed (* multiple Reduce::ratnz warnings *)

N@exactRed === N@approxRed

(*  True  *)

Using Solve instead of NSolve

Solve produces more solutions than NSolve, which was a surprise to me. That may be a bug or not (Solve is solving "exactly" and NSolve is solving "approximately" arguably). What is a bug is that if you switch NSolve to Solve in the inexact code, it complains it tried "direct rationalization" and it didn't work. However, should the user suspect that Solve bungled it somehow and directly rationalize the system themselves, Solve now works and returns six more solutions,

OP's exact NSolve for comparison:

sols1 = NSolve[
   x Tan[x/200] == -5 y Tan[10^-3 y] && x^2 - y^2 == 10000 && 
    0 <= x <= 10000, {x, y}];
sols1 // Length
(*  34  *)

The Solve that claims "direct rationalization" is of no use:

Solve[x Tan[x/200] == -5 y Tan[0.001 y] && x^2 - y^2 == 10000 && 
  0 <= x && x <= 10000, {x, y}]

Solve::inex: Solve was unable to solve the system with inexact coefficients or the system obtained by direct rationalization of inexact numbers present in the system....

User direct rationalization (whether 'tis nobler to use Rationalize[#,0]& or Rationalize, see note below if it's a concern):

sols2 = N@ Solve[
    x Tan[x/200] == -5 y Tan[0.001 y] && x^2 - y^2 == 10000 && 
      0 <= x <= 10000 // Rationalize, {x, y}];
sols2 // Length
(*  40  *)

If for whatever reason you wish to reproduce the deficient NSolve result, you can use the WorkingPrecision option to Solve:

sols3 = N@ Solve[
    x Tan[x/200] == -5 y Tan[0.001 y] && x^2 - y^2 == 10000 && 
      0 <= x <= 10000 // Rationalize, {x, y}, 
    WorkingPrecision -> 16.857679757182947`];
sols1 === sols3

(*  True  *)

Note: Rationalize[a] and Rationalize[a, 0] can produce different rational approximations to an inexact number a. It this case, Rationalize[0.001] and Rationalize[0.001, 0] both produce 1/1000. When they produce different rational numbers (e.g. a = 0.3333333333333), Rationalize[a] will have a smaller denominator. Sometimes Rationalize[a] does not produce a rational number (e.g. a = 0.333333333333), and so one sometimes uses Rationalize[Rationalize[a], 0] to always get rational numbers that have low denominators when possible. One may also use SetPrecision[a, Infinity] to get the binary floating-point fraction, which might be desired in numerical applications but probably to be avoided in symbolic ones.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.