9
$\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$
6
  • $\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$ Dec 26, 2016 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$ Dec 26, 2016 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, 2016 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, 2016 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, 2016 at 0:10

2 Answers 2

8
$\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$
2
  • $\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, 2017 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, 2017 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$
5
  • $\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, 2016 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, 2016 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, 2016 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, 2016 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, 2016 at 1:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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