66

Update

Finally in v13.1 the function DSolveChangeVariables is introduced, try it out! DChange in the answer below is still a good choice, of course.


Original Question

Maple owns an interesting function called dchange which can change the variables of differential equations, but there seems to be no such function in Mathematica.

Has any one ever tried to write something similar? I found this, this and this post related, but none of them attracted a general enough answer.

"So, what have you tried?" - Well, nothing. I decided to ask this question first to see if someone has already implemented the functionality and waited for a chance to make it public. If this question finally elicits no answer, I'll have a try.

The imaginary syntax for the function is

dChange[DE, relation, var]

where DE is the differential equation(s) to be transformed, and relation is the transformation relation(s) expressed as equation(s) i.e. with head Equal, var is the variable(s) to be changed.

Here are some examples for the imaginary behaviour:

Example 1

Originated from this answer implementing stereographic projection.

dChange[1/η D[η D[f[η], η], η] + (1 - s^2/η^2) f[η] - f[η]^3 == 0, 
        η == Sqrt[(1 + z)/(1 - z)], η]
(1/(1 + z)) ((-(1 + s^2 (-1 + z) + z)) f[z] + (1 + z) f[z]^3 + 
    (-1 + z)^2 (1 + z) (2 z f'[z] + (-1 + z^2) f''[z])) == 0

Example 2

Originated from this answer for Stefan's problem.

dChange[D[u[x, t], t] == D[u[x, t], {x, 2}], x == ξ s[t], x]
Derivative[0, 1][u][ξ, t] - (ξ s'[t] 
      Derivative[1, 0][u][ξ, t])/s[t] == Derivative[2, 0][u][ξ, t]/s[t]^2

Example 3

Originated from this answer. This technique is also used in the reduction of d'Alembert's formula.

dChange[D[y[x, t], t] - 2 D[y[x, t], x] == Exp[-(t - 1)^2 - (x - 5)^2],
        {ξ == t + x/2, η == t}, {x, t}]
Derivative[0, 1][y][ξ, η] == E^(-(-1 + η)^2 - (5 + 2 η - 2 ξ)^2)

I'll add more if I recall other representative examples.

xzczd
  • 65,995
  • 9
  • 163
  • 468
  • 1
  • 4
    @m0nhawk Well, as I mentioned above, that's just one of the related questions that are not general enough. – xzczd Apr 18 '15 at 08:33
  • 1
    For a quiet long time of using Mathematica the Replace and ReplaceAll are more than enough and, actually, I found them much powerful than Maple's dchange. – m0nhawk Apr 18 '15 at 08:37
  • 3
    The link has a few examples, and (re: @m0nhawk) I'm not sure that simply RepkaceAll will provide the same functionality. – Sjoerd C. de Vries Apr 18 '15 at 09:12
  • 2
    I don't know much about Maple, but it seems from your examples that it's less "careful" when simplifying expressions: Mathematica leaves expressions unevaluated if it can't get a result that's valid generically or consistent with the given assumptions. So probably one would have to allow an additional Assumptions option in the dChange emulation to tell Mathematica which variables are positive, or complex, etc... so it has a better chance of inverting and simplifying the required relations. Anyway, I like the idea... – Jens Apr 18 '15 at 16:32

2 Answers2

73

I've put this code on a GitHub but I don't know what features are needed or what problems it may give. I'm just not using it.

But I will incorporate incomming suggestions as soon as I have time.

Feedback in form of tests and suggestions very appreciated!

(If[DirectoryQ[#], DeleteDirectory[#, DeleteContents -> True]];
 CreateDirectory[#];
 URLSave[
    "https://raw.githubusercontent.com/" <> 
    "kubaPod/MoreCalculus/master/MoreCalculus/MoreCalculus.m"
    , 
    FileNameJoin[{#, "MoreCalculus.m"}]
 ]
) & @ FileNameJoin[{$UserBaseDirectory, "Applications", "MoreCalculus"}]

https://github.com/kubaPod/MoreCalculus

So this is a package MoreCalculus` with the function DChange inside.


What's new:

DChange automatically takes under consideration range assumptions for built-in transformations: (not heavily tested)

DChange[
  D[f[x, y], x, x] + D[f[x, y], y, y] == 0, 
  "Cartesian" -> "Polar", {x, y}, {r, θ}, f[x, y]
]

enter image description here

Usage:

DChange[expresion, {transformations}, {oldVars}, {newVars}, {functions}]

DChange[expresion, "Coordinates1"->"Coordinates2", ...]   

DChange[expresion, {functionsSubstitutions}] 

You can also skip {} if a list has only one element.

Examples:

Change of coordinates

  • rules accepted by CoordinateTransform are now incorporated, as well as coordinates ranges assumptions associated with them

     DChange[
      D[f[x, y], x, x] + D[f[x, y], y, y] == 0, 
      "Cartesian" -> "Polar", {x, y}, {r, θ}, f[x, y]
    ]
    

    enter image description here

    The transformation is returned too, to check if the canonical (in MMA) order of variables was used.

  • wave equation in retarded/advanced coordinates

    DChange[
     D[u[x, t], {t, 2}] == c^2 D[u[x, t], {x, 2}]
      ,
     {a == x + c t, r == x - c t}, {x, t},  {a, r},  {u[x, t]}  ]
    
    c Derivative[1, 1][u][a, r] == 0
    

  • stereographic projection

    DChange[
     D[η*D[f[η], η], η]/η + (1 - s^2/η^2)*f[η] - f[η]^3 == 0
     , 
     η == Sqrt[(1+z)/(1-z)],  η,  z,   f[η]   ]
    
    ((z-1)^2 (z+1)((z^2-1) f''[z]+2 z f'[z])-f[z] (s^2 (z-1)+z+1)+(z+1)     f[z]^3)/(z+1)==0
    


Example from @Takoda

$$ \begin{pmatrix}\dot{x}\\ \dot{y} \end{pmatrix}=\begin{pmatrix}-y\sqrt{x^{2}+y^{2}}\\ x\sqrt{x^{2}+y^{2}} \end{pmatrix} $$

out = DChange[
  Dt[{x, y}, t] == {-y r^2, x r^2}, "Cartesian" -> "Polar", 
  {x, y}, {r, θ}, {}
]

Solve[out[[1]], {Dt[r, t], Dt[θ, t]}]
{{Dt[r, t] -> 0, Dt[θ, t] -> r^2}}

Functions replacement

  • example on special case separation of Fokker-Planck equation

    DChange[
      -D[u[x, t], {x, 2}] + D[u[x, t], {t}] - D[x u[x, t], {x}]
      ,
      u[x, t] == Exp[-1/2 x^2] f[x] T[t]
    ] // Simplify
    
    % / Exp[-x^2/2] / f[x] / T[t] // Expand
    

    enter image description here

Code: (latest version is on GitHub)

ClearAll[DChange];


DChange[expr_, transformations_List, oldVars_List, newVars_List, functions_List] := 
  Module[ {pos, functionsReplacements, variablesReplacements, arguments,
           heads, newVarsSolved}
    ,
    pos = Flatten[
            Outer[Position, functions, oldVars], 
            {{1}, {2}, {3, 4}}
    ];

    heads = functions[[All, 0]];
    arguments = List @@@ functions;
    newVarsSolved = newVars /. Solve[transformations, newVars][[1]];

    functionsReplacements = Map[
      Function[i,
        heads[[i]] -> (
          Function[#, #2] &[
            arguments[[i]], 
            ReplacePart[functions[[i]], Thread[pos[[i]] -> newVarsSolved]]
          ] )
      ]
      ,
      Range @ Length @ functions
   ];

   variablesReplacements = Solve[transformations, oldVars][[1]];

   expr /. functionsReplacements /. variablesReplacements // Simplify // Normal
];

DChange[expr_, functions : {(_[___] == _) ..}] := expr /. Replace[
  functions, (f_[vars__] == body_) :> (f -> Function[{vars}, body]), {1}]

DChange[expr_, x___] := DChange[expr, ##] & @@ Replace[{x}, 
   var : Except[_List] :> {var}, {1}];

DChange[expr_, coordinates:Verbatim[Rule][__String], oldVars_List,
        newVars_List, functions_    ]:=Module[{mapping, transformation},
        mapping = Check[
            CoordinateTransformData[coordinates, "Mapping", oldVars],
            Abort[]
        ];
        transformation = Thread[newVars == mapping ];
        {
            DChange[expr, transformation, oldVars, newVars, functions],
            transformation
        }
];

TODO:

  • add some user friendly DownValues for simple cases
  • heavy testing needed, feedback appreciated
  • exceptions/errors handling. it is only as powerful as Solve so may brake for more convoluted implicit relations
  • it is not designed as a scoping construct
Kuba
  • 136,707
  • 13
  • 279
  • 740
  • 4
    Great work ;); +1 – Sektor Apr 18 '15 at 19:49
  • 1
    Your design for the syntax is undoubtedly more Mathematica-like and more reasonable. (I admit that when writing the question I haven't deliberated on the syntax design. ) – xzczd Apr 20 '15 at 07:36
  • 1
    Does this excellent code exist in package form? Thanks. – bbgodfrey Jan 10 '16 at 02:19
  • 7
    I would strongly suggest to look at very old package by
    Dr. Boris Rubinstein (can find in Mathsource) http://lt.tabiste.eu/w/library.wolfram.com+6493+05KTT9M3++jikAvB7Z/infocenter/MathSource/4186/ With just adding two semicolons it worked with current versions almost perfectly in most cases.
    – Acus Mar 07 '16 at 13:10
  • @user18792, could you give some details on how to change this packages? Or examples – tanghe2014 Dec 03 '16 at 04:42
  • @tanghe2014 one have to go to the code in .m file and fix it :) there are no guidelines :) the code is written in very naive way so it can only benefit from changes – Kuba Dec 03 '16 at 07:07
  • @tanghe2014 . After trying Get["ReplaceVariables`"] you will get warning at lines 239 and 301: Warning: comma encountered with no adjacent expression; the \ expression will be treated as Null. Go to these lines. You will see here

    If[SameQ[oldvar,newvar,expr],,

    These double comma is the problem. I replace them to

    If[!SameQ[oldvar,newvar,expr],

    (i.e. reverse check condition to Not[SameQ[]] and remove one comma). I think in first versions the If syntax has richer syntax like If[test,,do somthing]. Now I replaced it to If[Not[test],dosomething] and omited "do nothing" argument.

    – Acus Dec 05 '16 at 08:11
  • 2
    @Kuba Your functions is very useful. How about add it to the Function Repository (https://resources.wolframcloud.com/FunctionRepository)? Thank you. – tanghe2014 May 01 '20 at 05:47
  • 2
    @user18792 The link in your comment cannot work well. The old package by Dr. Boris Rubinstein is avilable at https://library.wolfram.com/infocenter/MathSource/4186/. – tanghe2014 May 01 '20 at 05:54
  • @Kuba amazing work! I have been waiting for ages for something like this. This does make a lot of my calculations easier. – TheTwistedSector Jul 26 '20 at 18:59
  • @TheTwistedSector I am glad it works and helps, feel free to add feedback via GitHub issues. – Kuba Jul 29 '20 at 11:54
  • I just wanted to ask whether you are still taking questions on this, since the package is 5 years old? I find it very useful but I have spotted a couple of things I need workarounds for in order for it to do what I want. – epsilonD3LT4 Feb 08 '21 at 14:37
  • 2
    @epsilonD3LT4 I will try to help but I can't promise anything. Feel free to open new issues if there's a specific problem too: https://github.com/kubaPod/MoreCalculus/issues – Kuba Feb 08 '21 at 15:05
  • I think your first example does not work (anymore?) as claimed? Mathematica 13.0. It says ReplaceAll::reps: {r==Sqrt[x^2+y^2],[Theta]==tan^-1(x,y)} is neither a list of replacement rules nor a valid dispatch table, and so cannot be used for replacing. This is from the GitHub version. – chris Jan 21 '22 at 11:50
  • @chris it lasted longer than I anticipated. Thanks for keeping an eye on it. I will try to update it but certainly not before mid March :( But as I said I am happy to collaborate on GH, it would be appropriate since I never used this package :p – Kuba Jan 22 '22 at 04:49
  • Yes, finally. What took WRI this long to do this? :) DSolveChangeVariables This mean 13.1 will be released be soon if docs are already online! – Nasser Jun 27 '22 at 07:46
9

2022 Update: Included in Mathematica 13.1

As pointed out by xzczd in his question update, seven years later, finally Mathematica 13.1 introduced the new function DSolveChangeVariables which specifically addresses his question.

Here below I show how to solve the same examples that were given in Kuba very useful answer, but using the new Mathematica function DSolveChangeVariables instead of his DChange.

Example 1: Change of coordinates

eq = D[f[x, y], {x, 2}] + D[f[x, y], {y, 2}] == 0;
deq = Inactive[DSolve][eq, f, {x, y}];
DSolveChangeVariables[deq, f, {r, theta}, "Cartesian" -> "Polar"] // Simplify

enter image description here

Example 2: wave equation in retarded/advanced coordinates

eq = D[u[x, t], {t, 2}] == c^2 D[u[x, t], {x, 2}];
deq = Inactive[DSolve][eq, u, {x, t}];
DSolveChangeVariables[deq, u, {a, r}, {a == x + c t, r == x - c t}] // Simplify 

enter image description here

Example 3: stereographic projection

eq = D[eta*D[f[eta], eta], eta]/eta + (1 - s^2/eta^2)*f[eta] - f[eta]^3 == 0;
deq = Inactive[DSolve][eq, f, eta];
DSolveChangeVariables[deq, f,  z, eta == Sqrt[(1 + z)/(1 - z)]] // Simplify

enter image description here

Example 4: Functions replacement

eq = -D[u[x, t], {x, 2}] + D[u[x, t], {t}] - D[x u[x, t], {x}];
deq = Inactive[DSolve][eq, u, {x, t}];
DSolveChangeVariables[deq, {f, T}, {x, t}, u[x, t] == Exp[-1/2 x^2] f[x] T[t]] // Simplify

enter image description here

divenex
  • 616
  • 5
  • 12
  • Great. How to achieve the transformation in the following link using DSolveChangeVariables. https://mathematica.stackexchange.com/questions/249778/dchange-fails-to-change-variables-for-1d-schr%c3%b6dinger-equation – RF_1 Jul 02 '22 at 04:45