4
$\begingroup$

I learning how to use packages. I found a big problem. Here is description of the problem is, then a MWE is given.

This small package takes in an ode and y[x] passed from the user.

These are saved in an association. After solving the ode, another function is called to verify the solution found using the standard method of first writing the solution as

 sol = y->Function[{x}, theSolution ]

Where in the above x is extracted from the association.

The problem is that the x above shows as

  mypkg`Private`x$

And not as x as expected. So the verification fail due to this. i.e. when I do

  ode/.sol

It fails. A work around is to use new local symbol in place of x and replace all the x symbols in the solution with this new symbol. But I'd like to learn if there is a better way to do this.

Here is a MWE. This file mypkg.m is the package

BeginPackage["mypkg`"] 
Unprotect @@ Names["mypkg`*"]; 
ClearAll @@ Names["mypkg`*"]; 
 
makeODE::usage = "...." 

Begin["`Private`"] 

makeODE[ode_Equal,y_Symbol[x_Symbol]] :=Module[{ODE},
   ODE= <| "ode"->ode, "x"->x, "y"->y, "sol"->{} |>;
   sol=y[x]/.First@DSolve[ode,y[x],x];
   Print["Solution is ",sol];
   AssociateTo[ODE, "sol" -> sol];
   verifySolution[ODE];
];

verifySolution[ODE_Association]:=Module[{sol,y,x,ode},

    Print["enter verifySolution"];

    y = ODE["y"];
    x = ODE["x"];
    ode = ODE["ode"];
    sol = y->Function[{x},Evaluate[ODE["sol"]]];

    Print["sol=",sol];

    If[Simplify[ode/.sol]===True,
       Print["Verified"]
       ,
       Print["Not Verified!!"]
    ]
    
]

End[]; 
Protect @@ Names["mypkg`*"]; 
EndPackage[];

To reproduce this problem

Get["mypkg.m"]
mypkg`makeODE[y'[x] + y[x] == x, y[x]]

This gives

enter image description here

The argument to the Function is not taken as the same x in the association. It got new context. I was expected the x to be the same Symbol passed in because I did x=ODE["x"] to read that symbol from the association thinking that now the local x will be the one passed in from the user. But this is clearly not the case.

The problem is that I can't write

sol = y->Function[{ODE["x"]},Evaluate[ODE["sol"]]];

ODE["x"] is not valid argument to the function. So I had to first make local x and then assign it the x in the association.

The above all works OK when in notebook itself ofcourse.

ode = y'[x] + y[x] == x;
sol = y[x] /. First@DSolve[ode, y[x], x];
sol = y -> Function[{x}, Evaluate[sol]];
ode /. sol

Mathematica graphics

One workaround for the package is the following. Change the function verifySolution to do this

verifySolution[ODE_Association]:=Module[{sol,y,x,ode,z},

    Print["enter verifySolution"];

    y = ODE["y"];
    x = ODE["x"];
    ode = ODE["ode"];

    (*do not use `x` in the association, use local z *)
    sol = y->Function[{z},Evaluate[ODE["sol"]]/.x->z];

    Print["sol=",sol];

    If[Simplify[ode/.sol]===True,
       Print["Verified"]
       ,
       Print["Not Verified!!"]
    ]
    
]

i.e. add local symbols z and use that as the argument for the Function and then replace all the x in the solution with z. This now works and verifies the solution.

Mathematica graphics

But this solution is ugly. Why can't one use the same x as is done at notebook level? The x symbol is saved in the association and passed along. This only happens when using x in this context (as argument for Function[{x},.... otherwise, I see no other problems.

Is there a better way to handle this so to avoid having to use new variable like in this above? And why exactly is the argument to Function[{x},...] is getting different scope that the x passed in from the user?

$\endgroup$
0

1 Answer 1

6
$\begingroup$

This is caused by the fact that Function is considered a scoping construct. This causes the localization done by Module to ignore the x in the first argument to function, i.e. it will not be localized. This issue has nothing really to do with packages, those just make the issue more apparent since all the x in function is in a different context than the x in the equation (you should see the same effect when using e.g. z for the function argument).

With all that being said, the easiest way to fix this is to hide the Function head from Module, e.g. like this:

Module[
 {x = y},
 Function @@ List[{x}, y]
 ]
(* Function[{y}, y] *)

Compare this to version without the hiding:

Module[
 {x = y},
 Function[{x}, y]
 ]
(* Function[{x}, y] *)

Depending on your needs, you will want to use List or Hold as initial wrapper for the function arguments (e.g, to control which arguments are evaluated)

$\endgroup$
1
  • $\begingroup$ Thanks. This is a nice workaround. I changed the code to be sol = y->Function @@ List[{x},Evaluate[ODE["sol"]]]; and now it worked. I have not seen this syntax for Function before. $\endgroup$
    – Nasser
    Jan 8, 2023 at 10:24

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