3
$\begingroup$

I want to use Solve[] inside a Module. If I make the variables solved for local to the module, they are treated differently than if I leave them global. For example,

SolveIt[a_, b_] := Module[{x, soln},
   soln = Solve[a x + b == 0, {x}];
   Return[soln]
   ];
SolveIt[3, 4]

returns

{{x$1342 -> -(4/3)}}

If I leave x global, the function returns

{{x -> -(4/3)}}

But if that global x has a value, the Solve[] function says that x is not a valid variable.

Could someone please (a) explain what's going on, and (b) suggest best practice for using Solve[] and similar functions within a module. Thanks!

$\endgroup$
  • 2
    $\begingroup$ Maybe you can consider using a formal variable? Those are guaranteed to never have anything assigned to them… $\endgroup$ – J. M. will be back soon Jul 8 '16 at 12:22
  • $\begingroup$ @J.M. Mma formal variables are new to me. I will have to study to see how to use them in this context. Thanks! $\endgroup$ – Joseph O'Rourke Jul 8 '16 at 13:15
  • 1
    $\begingroup$ Just to start you off: SolveIt[a_, b_] := \[FormalX] /. Solve[a \[FormalX] + b == 0, \[FormalX]]. (It looks gnarly on SE, but should paste fine into Mathematica.) $\endgroup$ – J. M. will be back soon Jul 8 '16 at 13:17
  • 1
    $\begingroup$ The problem is that if x has a value, it will evaluate immediately in an expression such as x -> 1. There is just no good way to return something containing x if x has a value. Do you really need to return x from the function (as part of a larger expression)? Or is it sufficient to return the solution as a number, i.e. return val instead of x -> val? $\endgroup$ – Szabolcs Jul 8 '16 at 21:19
4
$\begingroup$

Do you really need to return x from your function (even as part of a larger expression)? There is simply no good way to do this if x has a global value, as x = 1; x -> 2 immediately evaluates to 1 -> 2.

You could return the solution value only (val instead of x -> val):

SolveIt[a_, b_] :=
  Module[{x, soln},
   soln = Solve[a x + b == 0, {x}];
   x /. soln
  ]

SolveIt[3, 4]
(* {-(4/3)} *)

Using formal variable were mentioned. These have the advantage that they are Protected, therefore they are guaranteed not to have an assigned value. So you might consider

Clear[SolveIt]
SolveIt[a_, b_] := \[FormalX] /. Solve[a \[FormalX] + b == 0, \[FormalX]]

SolveIt[3, 4]
(* {-(4/3)} *)

(Note: you can type \[FormalX] using ESC $x ESC.)

But there is a big problem with this. This works fine:

SolveIt[a, 1]
(* {-(1/a)} *)

But what about this one?

SolveIt[\[FormalX], 1]
(* {-I, I} *)

We passed a symbol into the function, and that symbol happened to be already in use internally ... so we're solving the equation x^2 + 1 == 0 now (compare to a x + 1 ==0 before).

The same problem appears with Block:

Clear[SolveIt]
SolveIt[a_, b_] :=
  Block[{x, soln},
   soln = Solve[a x + b == 0, {x}];
   x /. soln
   ];

SolveIt[y, 1]
(* {-(1/y)} *)

SolveIt[x, 1]
(* {-I, I} *)

Thus it is important to use Module (and not Block or a formal variable) if you want to make it possible to pass symbols into the function. If you only pass in numbers, this is not a problem, but then you may consider defining the function as SolveIt[a_?NumericQ, b_?NumericQ].


In case you are not certain about the difference between Module and Block, see here:

In short, both localize variables, but Module does this by renaming them to a unique name (so that x becomes something like x$123) while Block just temporarily removed any definitions that may be associated with x (but x is still the same symbol as before). Module emulates lexical scoping and Block does dynamic scoping.

$\endgroup$
  • $\begingroup$ Thank you! Indeed, "it [is] sufficient to return the solution as a number." $\endgroup$ – Joseph O'Rourke Jul 8 '16 at 22:13
3
$\begingroup$

As explained in Mathematica help, "Module creates a symbol with name xxx\$nnn to represent a local variable with name xxx. The number nnn is the current value of $ModuleNumber." This variable is not renamed after the module has completed.

If instead the variable x refers to a global variable which already has a value (e.g. x=3), the x in Solve[eqn,x] will be evaluated, so that it becomes Solve[eqn,3] (leading to the message Solve::ivar: 3 is not a valid variable.

@J.M. suggests the use of formal variables which (I think) are intended for precisely this sort of use case.

SolveIt[a_, b_] := Solve[a \[FormalX] + b == 0, \[FormalX]]]
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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