8
$\begingroup$

I have lots of differential equations, that I save to file (along with output and other things), as "strings", to process later in Latex and make a document of them.

I save each input differential equation, by first converting it from expression to String, then write it to the file (using WriteString command).

The problem is that when converting say eq = y'[x] == 1/y[x] to String, using ToString it becomes

Mathematica graphics

Which ofcource does not work, when saved to file, since it messes up the lines in the text file. So I use InputForm like this ToString[eq,InputForm] and now it works, the string is flat and on one line:

Mathematica graphics

The above is a string, and I can use that with no problem.

What I like however is to have the string look like the original expression, since it is easier to read (these will later show as verbatim in Latex), i.e. I need to convert expression to

   y'[x] == 1/y[x] 

to same as above, but as string

   "y'[x] == 1/y[x]"

I do not use 2D math at all in my input. All my original Mathematica expressions are flat, read from plain text file, read them, and process them, then need to save them back as strings with other things for post-processing.

But I'd like to keep the same looking expression used, but as string.

Question: How to to convert y'[x] == 1/y[x] to string "y'[x] == 1/y[x]" ?

For example of one Latex output, here is a link to help explain what I mean.

Mathematica 11.0.1.

Update:

To answer comments, I have the ODE's in a list. Then I use a loop to process them. Here is a MWE, a very simplified version. The process is completely non-interactive.

SetDirectory[NotebookDirectory[]];
lst = {{y'[x] == a*f[x]}, {y'[x] == x + Sin[x] + y[x]}, {y'[x] == 
     x^2 + 3*Cosh[x] + 2*y[x]}};
fileName = "result.txt";
file = OpenWrite[fileName, PageWidth -> Infinity];
Do[
  s = ToString[First@lst[[n]], InputForm];
  WriteString[file, s];
  WriteString[file, "\n"]
  , {n, 1, Length@lst}
  ];
Close[file]

The text file where these are saved to now looks like

Mathematica graphics

$\endgroup$
  • $\begingroup$ In what form do you have the equations? In a list in an input cell? E.g.: Cases[#, BoxData[b_] :> StringJoin @@ Flatten[b /. RowBox -> List], -1] &@ Cell[BoxData[ RowBox[{RowBox[{RowBox[{"y", "'"}], "[", "x", "]"}], "\[Equal]", RowBox[{"1", "/", RowBox[{"y", "[", "x", "]"}]}]}]], "Input"] $\endgroup$ – Rolf Mertig Dec 26 '16 at 23:09
  • $\begingroup$ returns {"y'[x]==1/y[x]"} . You could extend this to a button a script to get all input cells in a certain notebook and you are done. $\endgroup$ – Rolf Mertig Dec 26 '16 at 23:15
  • $\begingroup$ If you have (*In[100]=*) y'[x] == 1/y[x], then ToString[DisplayForm[ToExpression[InString[100]]]] returns : "y '[x] == 1/y[x]" (with a blank between y and ') . $\endgroup$ – andre314 Dec 26 '16 at 23:45
  • $\begingroup$ @RolfMertig thanks. I added an MWE. These are in a list. I do not know what each ODE looks like, there are 1,000's of them in the list. So I can't hardcode things. I just showed one example. $\endgroup$ – Nasser Dec 27 '16 at 0:06
  • $\begingroup$ @andre thanks. Can I use your method if the equations are in a list? as in the MWE I showed? $\endgroup$ – Nasser Dec 27 '16 at 0:10
7
$\begingroup$

My approach for this sort of thing is to define conditioned Format rules for the problematic symbols, and then to Block the condition true when using ToString. In addition, I like to use SequenceForm as a substitute for HoldForm. In your example, I would do:

Format[Derivative[n_?Positive][f_], InputForm] /; $Nasser :=
SequenceForm[f, OutputForm@StringJoin[ConstantArray["'", n]]]

Then, I would define a special tostring function:

SetAttributes[tostring, HoldFirst]
tostring[expr_] := Internal`InheritedBlock[{$Nasser = True, SequenceForm},
    SetAttributes[SequenceForm, HoldFirst];
    ToString[SequenceForm[expr], InputForm]
]

A couple examples:

tostring[y'[x] == a/y[x]] // InputForm
(* "y'[x] == a/y[x]" *)

tostring[x''[t] + c0 x'[t]^2 + c1 x[t]] // InputForm
(* "x''[t] + c0*x'[t]^2 + c1*x[t]" *)
$\endgroup$
  • $\begingroup$ How does SequenceForm work in this case? There isn't a lot of detail in the wolfram documentation. When I tried replacing SequenceForm with Row, Text, or Print -- things I assume are alternatives -- it no longer worked. $\endgroup$ – 1110101001 Jun 11 '17 at 0:05
  • 1
    $\begingroup$ @1110101001 SequenceForm is a very special function that disappears when displayed using InputForm. You can think of it as the InputForm analog of HoldForm, except that SequenceForm doesn't hold any arguments. This is why SequenceForm is so useful when creating custom formats in InputForm. $\endgroup$ – Carl Woll Jun 11 '17 at 4:39
2
$\begingroup$

This is pure string hackery and I don't know how robust it is, but it works on your test case and one other that I tried.

helper[l_, d_] := l <> StringJoin @@ ConstantArray["'", ToExpression[d]]

SetAttributes[toPrimedForm, HoldFirst]
toPrimedForm[expr_] :=
  Module[{str},
    str[1] = ToString[HoldForm[expr], FormatType -> InputForm];
    str[2] = StringReplace[str[1], {"HoldForm[" -> "", "]" ~~ EndOfString -> ""}];
    StringReplace[
      str[2], 
      "Derivative[" ~~ d : DigitCharacter ~~ "][" ~~ l : LetterCharacter ~~ "]" :> 
        helper[l, d]]]

toPrimedForm[y'[x] == 1/y[x]]

"y'[x] == 1/y[x]"

toPrimedForm[x''[t] + c0 x'[t]^2 + c1 x[t]]

"x''[t] + c0*x'[t]^2 + c1*x[t]"

Certainly it needs more testing and probably more work, but I'm short on time at the moment. Perhaps it will give you something you can develop further.

Update

Here is version written to work when passed an argument that is an ODE expression or a variables holding such an expression.

helper[l_, d_] := l <> StringJoin @@ ConstantArray["'", ToExpression[d]]

SetAttributes[toPrimedForm, HoldFirst]
toPrimedForm[expr_Symbol] := 
  Module[{str}, 
    str[1] = ToString[HoldForm[Evaluate@expr], FormatType -> InputForm];
    str[2] = StringReplace[str[1], {"HoldForm[" -> "", "]" ~~ EndOfString -> ""}];
    StringReplace[
      str[2], 
      "Derivative[" ~~ d : DigitCharacter ~~ "][" ~~ l : LetterCharacter ~~ "]" :> 
        helper[l, d]]]
toPrimedForm[expr_] := Module[{xpr = expr}, toPrimedForm[xpr]]

expr = y'[x] == a/f[x];
toPrimedForm[expr]

"y'[x] == a/f[x]"

toPrimedForm[y'[x] == a/f[x]]

"y'[x] == a/f[x]"

$\endgroup$
  • $\begingroup$ @Nasser. I know it won't work if you have derivatives of order 10 or more or if you use multi-character dependent variable names. $\endgroup$ – m_goldberg Dec 27 '16 at 0:50
  • $\begingroup$ it works on one example, when I removed the SetAttributes[toPrimedForm,HoldFirst]. But need to test it more. This is good so far.... $\endgroup$ – Nasser Dec 27 '16 at 0:58
  • $\begingroup$ @Nasser. I use the HoldFirst to preserve forms like 1/y. If you evaluate expression like expr = y'[x] == a/f[x], the front-end is going to mess up that expression before my function gets it. Could you live with expr = HoldForm[y'[x] == a/f[x]]? Then the HoldFirst could be dropped. $\endgroup$ – m_goldberg Dec 27 '16 at 1:02
  • $\begingroup$ Will it show like HoldForm[y'[x] == a/f[x]] in the file you mean? Then no, since this can't be copied pasted from the web page. I'd like it to be just "y'[x] == a/f[x]" but I am trying your code now, without the Hold on it, else it does not work, since I need to pass expressions in variable. Not literally, since these are in a long list. As I have in MWE. Testing more now... thanks $\endgroup$ – Nasser Dec 27 '16 at 1:10
  • $\begingroup$ @Nasser. No, it would strip the HoldForm off. It already does this now after it converts the expression to a string. The only change needed would be for it not to add HoldForm in its 1st step. $\endgroup$ – m_goldberg Dec 27 '16 at 1:18

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.