11
$\begingroup$

You have all convinced me not to type in formulas with subscripts. However, in order to be able to match the subscripts in my whiteboard math, I figure a 2nd best solution is to want to display instances of C[i] with $C_i$ , only in the output, for a chosen set of $C$ and for any $i$

DisplaySubscripted[val_] := 
 Format[val[args__]] := Interpretation[Subscript[val, args], val[args]]
SubscriptVariables[valueList_] := Scan[DisplaySubscripted, valueList];

(* Usage*)
SubscriptVariables[{a, b}]; (* Label the variables I want to display subscripted*)
Solve[a[1] + a[2] - 2 b[2] == c[1], b[2]]
(* This subscripts the a[1], a[2], b[2] and not the c[1], as I hoped.
  It also doesn't seem to mess up the symbolics*)

Am I missing something, or is this a side-effect free approach to this problem? (it also works well with Subsuperscript and a[1][2] displaying as $a_1^2$ and it doesn't seem to mess up the superscript with powers)

ALTERNATIVELY: I also thought that Notation may be a way to do this. Easy enough to do this for an arbitrary variable. e.g.

Needs["Notation`"];
DisplaySubscripted[var_] := 
 Notation[ParsedBoxWrapper[
    SubscriptBox[var, "1"]]\[DoubleLongLeftArrow]ParsedBoxWrapper[
    RowBox[{var, "[", "1", "]"}]]];
DisplaySubscripted["f"];

f[1] (* Displays in output properly as f_1*)

However, I can't figure out how to match this for arbitrary subscript patterns. The obvious one does not work: Notation[ParsedBoxWrapper[ SubscriptBox[var, "_"]]\[DoubleLongLeftArrow]ParsedBoxWrapper[ RowBox[{var, "[", "_", "]"}]]]

I am also not entirely sure that passing var_ as strings is the best way to do it, but if I use DisplaySubscripted[f] instead, it displays the namespace (i.e. Global'f_1). If the Format approach I gave first works, it seems much simpler.

$\endgroup$

2 Answers 2

10
$\begingroup$

I think your approach is fine; in fact I just recommended something similar in another answer.

However, for the best handing of using your formatted output as input may want to use MakeBoxes for the reason described by Michael Pilat.

You also might consider putting a list your subscripted symbols in a global variable for easy changes, or Blocking.

Something like this:

MakeBoxes[a : h_[args__], fmt_] /;
  MemberQ[$subs, Unevaluated @ h] :=
 ToBoxes[Interpretation[Subscript[h, args], a], fmt]

$subs = Hold[a, b];

expr = Solve[a[1] + a[2] - 2 b[2] == c[1], b[2]]

Block[{$subs = {c}}, ToString[expr, StandardForm]]

enter image description here

Notes:

  • I used ToString to cause MakeBoxes to trigger inside the Block; this is similar to what I did for Returning an unevaluated expression with values substituted in.

  • I put the Symbols inside Hold so that global definitions would not conflict; the MakeBoxes definition is written to handle this. A list can also be used as shown in the Block example.


Extension

Matching symbols by pattern was requested. I believe it is best to use string patterns, and require all symbols (in the list) to be in String form:

MakeBoxes[a : h_[args__], fmt_] :=
  ToBoxes[Interpretation[Subscript[h, args], a], fmt] /;
    MemberQ[Names @ $subs, ToString @ Unevaluated @ h]

$subs = {"a", "b*"};

expr = Solve[a[1] + a[2] - 2 b[2] + bx[1]^2 == c[1], b[2]]

The patterns in $subs can be anything that is accepted by Names.

If you want to handle both literal Symbols and string pattern you will need to convert the Symbols to strings. The easiest way would be Names[ToString /@ $subs] but this would preclude using StringExpression patterns and would fail to hold Symbols unevaluated; I recommend just using strings as above.

In version 7 if I Copy As > LaTeX the expression above I get LaTeX with subscripts:

$b_2\to \frac{1}{2} \left(a_1+a_2+\left(\text{bx}_1\right){}^2-c[1]\right)$

In version 9 this does not work right; you could leave out Interpretion to fix the problem at the expense of being able to copy&paste the expression within Mathematica:

MakeBoxes[a : h_[args__], fmt_] := 
 ToBoxes[Subscript[h, args], fmt] /;
   MemberQ[Names@$subs, ToString@Unevaluated@h]

Refactored full definitions

Here is refactored code for the generation of your full set of definitions.

  • The code is far more concise and easily modified; one can make a change in one place rather than a dozen.

  • I noted that repeated calls to Names was the cause of a considerable slow-down so I changed that to StringMatchQ.

  • I Protected the symbols {bar, hat, tilde, vec, underbar, plus, minus, star} because I don't think you'll want those accidentally evaluating to something else.

  • I rewrote definitions that call Subsuperscript to use argsub_ rather than argsub__ as the latter could result in invalid input to Subsuperscript.

  • If you use the following code outside of package, without Begin and End, you should add SetAttributes[makeDef, HoldAll] to prevent things like h from possibly evaluating incorrectly.

The Symbols used before Begin are created in the present (typically Global`) context; the usage messages are therefore not superfluous, but may be replaced with merely $scriptedconstants; $scriptedfunctions; if desired.

With[{symbols := Sequence[bar, hat, tilde, vec, underbar, plus, minus, star]},
  Unprotect @ symbols; ClearAll @ symbols; Protect @ symbols;
]

$scriptedconstants::usage = "a list of string patterns for symbols: constants";
$scriptedfunctions::usage = "a list of string patterns for symbols: functions";

Begin["formattingRules`"];

makeDef[pat_, body_] := (
  MakeBoxes[a : pat, fmt_] := ToBoxes[Interpretation[body, a], fmt] /;
    StringMatchQ[ToString @ Unevaluated @ h, $scriptedconstants];
  MakeBoxes[a : pat[sub_], fmt_] := ToBoxes[Interpretation[body[sub], a], fmt] /;
    StringMatchQ[ToString @ Unevaluated @ h, $scriptedfunctions]
)

set1 = {
  bar      -> OverBar,
  hat      -> OverHat,
  tilde    -> OverTilde,
  vec      -> OverVector,
  underbar -> UnderBar,
  plus     -> SuperPlus,
  minus    -> SuperMinus,
  star     -> SuperStar
 };

set2 = {
  plus  -> "+",
  minus -> "-",
  star  -> "*"
 };

makeDef[h_[#], #2[h]] & @@@ set1;
makeDef[h_[argssub__][#], Subscript[#2[h], argssub]] & @@@ Take[set1, 5];
makeDef[h_[argsub_][#], Subsuperscript[h, argsub, #2]] & @@@ set2;
makeDef[h_[argssub__], Subscript[h, argssub]];

End[];

(You may notice a funny offset in lines containing $ above; this is a known bug in the SE rendering.)

$\endgroup$
19
  • $\begingroup$ The only additional feature I would be interested in is whether we could have it match patterns. e.g. $displayscripted = {a_} where ab[1] becomes $ab_1$ $\endgroup$
    – jlperla
    Commented Aug 22, 2013 at 16:13
  • $\begingroup$ @jlperla I recommend a string pattern directly, rather than a Pattern object: $displayscripted = {"a*"} -- if this is acceptable I will adapt my answer to include it. $\endgroup$
    – Mr.Wizard
    Commented Aug 22, 2013 at 16:16
  • $\begingroup$ Perfectly acceptable. $\endgroup$
    – jlperla
    Commented Aug 22, 2013 at 17:38
  • $\begingroup$ Sorry, just realized there is 1 more potential issue. The goal would also be to copy/paste as latex. I try to latex copy the displayed output from things like a[1], displaying as $a_1$, it copies as a[1]. If I do things like: ToString[a[1],StandardForm and then latex copy/paste the output it is closer, but still wacky. Finally, if I put in my other forms such as a[1][2] to $a_1^2$ or a[star] to $a^{*}$ I get an error like Recursion depth of 4096 exceeded or it simply doesn't copy properly. $\endgroup$
    – jlperla
    Commented Aug 22, 2013 at 17:59
  • $\begingroup$ My implementation of $a^{*}$ is MakeBoxes[a : h_[star], fmt_] /; MemberQ[$variablelist, Unevaluated@h] := ToBoxes[Interpretation[SuperStar[h], a], fmt]; for example. $\endgroup$
    – jlperla
    Commented Aug 22, 2013 at 18:00
2
$\begingroup$

Summarizing all of the above, following code implements the list of formatting rules. The features are:

  • Displays in output based on the set of formatting tags. e.g. a[1] displays as $a_1$
  • Matches string patterns such as "a*" to ab[1]
  • Copy/paste in the notebook of the output a_1 actually copies a[1] as it should.
  • ToLatex takes an expression and generates the latex with the formatting. i.e. a[1] becomes a_1 in latex.
  • Special cases for hats, bars, etc. e.g. a[star] displays as $a^{*}$
  • Rules include variations for both variables and functions. i.e. $\hat{f}_l(z)$ and $a_l$
  • Can handle derivatives $f[l]'[z]$ to $f_l'(z)$
  • Can handle functions with/without subscripts. e.g. $f[z]$ and $f_l[z]$ simultaneously if $z$ is flagged as a parameter.

Usage:

$scriptedconstants = {a, ab};
	$scriptedfunctions = {f};
$scriptedfunctionsvars = {x};

val = {a[1], ab[1], a[1, 2], a[bar], a[hat], a[tilde], a[vec], 
  a[underbar], a[plus], a[minus], a[star], a[l][bar], a[l][hat], 
  a[l][tilde], a[l][vec], a[l][underbar], a[l][plus], a[l][minus], 
  a[l][star]}
val2 = {f[x], D[f[x], x], f[l][x, z], f[l], f[l]'[z], 
  D[f[l][z], z, z], D[f[l][x, z], z], D[f[hat][x, z], z], 
  D[f[l][hat][x, z], z], f[1][z], f[1][z_], f[bar][z], f[bar][x, z], 
  f[hat][z], f[tilde][z], f[vec][z], f[underbar][z], f[plus][z], 
  f[minus][z], f[star][z], f[l][bar][z], f[l][hat][z], f[l][tilde][z],
   f[l][vec][z], f[l][underbar][z], f[l][plus][z], f[l][minus][z], 
  f[l][star][z]}
 val // ToLatex
val2 // ToLatex

This displays as: $$ \left\{a_1,\text{ab}_1,a_{1,2},\bar{a},\hat{a},\tilde{a},\overset{\rightharpoonup }{a},\underline{a},a^+,a^-,a^*,\bar{a}_l,\hat{a}_l,\tilde{a}_l,\overset{\rightharpoonup }{a}_l,\underline{a}_l,\text{Subsuperscript}[a,l,+],\text{Subsuperscript}[a,l,-],\text{Subsuperscript}[a,l,*]\right\} $$ (the super/subscripts here actually show correctly in the FrontEnd, but are the only thing which don't translate to Latex well. An independent problem from this code) $$ \left\{f(x),f'(x),f_l(x,z),f_l,f_l'(z),f_l''(z),f_l{}^{(0,1)}(x,z),\hat{f}^{(0,1)}(x,z),\hat{f}_l{}^{(0,1)}(x,z),f_1(z),f_1(\text{z$\_$}),\bar{f}(z),\bar{f}(x,z),\hat{f}(z),\tilde{f}(z),\overset{\rightharpoonup }{f}(z),\underline{f}(z),f^+(z),f^-(z),f^*(z),\bar{f}_l(z),\hat{f}_l(z),\tilde{f}_l(z),\overset{\rightharpoonup }{f}_l(z),\underline{f}_l(z),\text{Subsuperscript}[f,l,+](z),\text{Subsuperscript}[f,l,-](z),\text{Subsuperscript}[f,l,*](z)\right\} $$ And the ToLatex @val2 generates as:

\left{f(x),f'(x),f_l(x,z),f_l,f_l'(z),f_l''(z),f_l{}^{(0,1)}(x,z),\hat{f}^{(0,1)}(x,z),\hat{f}_l{}^{(0,1)}(x,z),f_1(z),f_1(\text{z$\_$}),\bar{f}(z),\bar{f}(x,z),\hat{f}(z),\tilde{f}(z),\overset{\rightharpoonup }{f}(z),\underline{f}(z),f^+(z),f^-(z),f^*(z),\bar{f}_l(z),\hat{f}_l(z),\tilde{f}_l(z),\overset{\rightharpoonup }{f}_l(z),\underline{f}_l(z),\text{Subsuperscript}f,l,+,\text{Subsuperscript}f,l,-,\text{Subsuperscript}f,l,*\right}

The full listing of code for this, slightly modified version from that of mr-wizard is:

NotScriptedVarQ[z_] := ! MemberQ[$scriptedfunctionsvars, z, Infinity];

makeDef[pat_, body_] := (
  MakeBoxes[a : pat, fmt_] := ToBoxes[Interpretation[body, a], fmt] /;
    MemberQ[Union[$scriptedconstants, $scriptedfunctions], Unevaluated @ h];
      MakeBoxes[a : pat[sub_], fmt_] := ToBoxes[Interpretation[body[sub], a], fmt] /;
        MemberQ[$scriptedfunctions, Unevaluated @ h]
)

set1 = {
  bar      -> OverBar,
  hat      -> OverHat,
  tilde    -> OverTilde,
  vec      -> OverVector,
  underbar -> UnderBar,(* After here subsuper is ncessary to have both annotations*)
  plus     -> SuperPlus,
  minus    -> SuperMinus,
  star     -> SuperStar
 };

set2 = {
  plus  -> "+",
  minus -> "-",
  star  -> "*"
 };

makeDef[h_Symbol[#], #2[h]] & @@@ set1;
makeDef[h_Symbol[argssub__][#], Subscript[#2[h], argssub]] & @@@ Take[set1, 5];
makeDef[h_Symbol[argsub_][#], Subsuperscript[h, argsub, #2]] & @@@ set2;
makeDef[h_Symbol[argssub__?NotScriptedVarQ], Subscript[h, argssub]];
$\endgroup$
19
  • $\begingroup$ I am glad you have a solution. Would you like me to add a condensed version of your rule creation code to my answer? $\endgroup$
    – Mr.Wizard
    Commented Aug 23, 2013 at 4:49
  • $\begingroup$ By all means, please edit or move things into your answer if you prefer. I think this is a very useful utility for people. So far, the only missing pattern I have found is that while F[h][z] matched my function pattern I also needed F[h]'[z] to match to $F_h'(z)$ so I could write an ODE in terms of F[h] $\endgroup$
    – jlperla
    Commented Aug 23, 2013 at 5:08
  • $\begingroup$ Okay; tomorrow. $\endgroup$
    – Mr.Wizard
    Commented Aug 23, 2013 at 6:08
  • $\begingroup$ Thanks. If you have any ideas of the best/idiomatic way to create the as a package it would also be appreciated. I had trouble with contexts, etc when I tried it $\endgroup$
    – jlperla
    Commented Aug 23, 2013 at 16:30
  • $\begingroup$ When you say "ask a package" do you just mean a convenient way to load these definitions? If that is so you shouldn't need things like BeginPackage; if on the other hand you only want the definitions to be active only in certain contexts or the like you'll have to clearly specify what you want. $\endgroup$
    – Mr.Wizard
    Commented Aug 23, 2013 at 17:23

Your Answer

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

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