On 25 Oct 2006, at 14:39, AES wrote:
> I want to define two functions at once, where both of their values
> depend on the same two variables, and both of their values come out
> of a
> single two-dimensional FindRoot which I'd rather not evaluate twice.
>
> The following approach seems to work fine for a *single* function
>
> f[x_, y_] := Module[{},
> myEqns = {f1 - g1 == x1 + 3y1, f1 + g1 == 2x1 - 7y1};
> myValues = {x1 -> x, y1 -> y};
> mySolns = FindRoot[ myEqns /. myValues, {f1, 0} , {g1, 0}];
> f1 /. mySolns]
>
> But if I try to define two functions at once by replacing the first
> and
> last lines with
>
> {f[x_, y_], g[x_,y_]} := Module[{},
> myEqns = {f1 - g1 == x1 + 3y1, f1 + g1 == 2x1 - 7y1};
> myValues = {x1 -> x, y1 -> y};
> mySolns = FindRoot[ myEqns /. myValues, {f1, 0} , {g1, 0}];
> {f1, g1} /. mySolns]
>
> I get a message about "shapes not being the same". In fact, if I just
> make the first and last lines even a single element list, e.g.
>
> {f[x_, y_]} := Module[{},
> myEqns = {f1 - g1 == x1 + 3y1, f1 + g1 == 2x1 - 7y1};
> myValues = {x1 -> x, y1 -> y};
> mySolns = FindRoot[ myEqns /. myValues, {f1, 0} , {g1, 0}];
> {f1} /. mySolns]
>
> this doesn't work either.
>
> If a module supposedly returns the result of its (compound)
> expression,
> and the final term in that expression is a list, shouldn't the final
> example work? More important (to me anyway): Is there a simple way to
> define two functions that use a shared FindRoot evaluation in a way
> that
> (a) evaluates the FindRoot only once, and (b) involves only a single
> compound expression of some sort?
>
I know this is not really relevant here, but I feel I should mention
the fact that there is absolutely no point in using Module without
any local variables; if you do not need local variables you will do
better to enclose the right hand side in parentheses (or use explicit
Composition[....,....,...]).
Now to answer your question. You can only use lists with SetDelayed
with lists if both sides have the head List *before evaluation* and
the lists have the same shape. However, before evaluation your right
hand side has Head Module, before Evaluation so your approach can't
work. This problem would disappear if you used Set (=) instead of
SetDelayed but another would take its place: the right hand side
would be evaluated immediately, but then FindRoot would produce an
error since it would no be able to solve the equations without
numerical values for x and y.
The simplest way do deal with your problem requires a lot of typing
(or at east copying and pasting):
{f[x_, y_], g[x_, y_]} := {Module[{myEqns, myValues, mySolns, x1, y1,
f1, g1},
myEqns = {f1 - g1 == x1 + 3*y1, f1 + g1 == 2*x1 - 7*y1};
myValues = {x1 -> x, y1 -> y}; mySolns = FindRoot[myEqns /. myValues,
{f1,0},{g1,0}];f1 /. mySolns],Module[{myEqns, myValues, mySolns},
myEqns = {f1 - g1 == x1 + 3*y1, f1 + g1 == 2*x1 - 7*y1};myValues =
{x1 -> x, y1 -> y};mySolns = FindRoot[myEqns /. myValues, {f1, 0},
{g1, 0}]; g1 /. mySolns]}
This long-winded method will indeed define two functions in "one go":
In[3]:=
Through[{f,g}[1,2]]
Out[3]=
{-2.5,-9.5}
Actually, I can see another way, which uses only one Module, but as I
do not think it is any better than the above one, I will only
describe it in words and leave implementation to anyone who sees more
value in it than I do. Basically the idea is first, to replace
SetDelayed by Set, so that the right hand side is evaluated
immediately and then to re-write the right hand side so that it will
evaluate to a list with two elements. In order to do this you would
need to prevent FindRoot and Replace from trying to evaluate
prematurely. This could be done, for example, by using a list of two
conditional statements of the form {If[Element[Complexes, x|y],
FindRoot....., If[Element[Complexes,x|y],FindRoot.....}. In other
words the right hand side should immediately evaluate to a list of
two conditional expressions, which will return the values of f[x,y]
and g[x,y] when x and y are numerical.
Andrzej Kozlowski