7
$\begingroup$

I wish to write a function like MatrixForm, that affect only display but not evaluation, i.e. a function that automatically is stripped away from output.

More precisely I wish a MatrixForm-variant that collect the LCM of the denominators of the elements of the matrix out of the matrix.

Is this possible?

I think Interpretation, Defer, etc. combined with Row, MatrixForm doesn't fill exactly this need. This is the best I did.

PrettyMatrixForm[
  m_ /; VectorQ[m, NumericQ] \[Or] MatrixQ[m, NumericQ]] :=
 With[{lcm = LCM @@ (Denominator /@ Flatten[m])},
  If[lcm === 1, MatrixForm[m],
   With[{mm = m*lcm},
    Interpretation[Row[{1/lcm, " \[Times] ", MatrixForm[mm]}], m]]
   ]]

PrettyMatrixForm[{{1/2, 1/4}, {2, 1/3}}]

EDIT. I wish to be able to write and evaluate something linke this in a cell, as I can do with MatrixForm.

{{1/2, 1/4}, {2, 1/3}} //PrettyMatrixForm
% * 2

EDIT 2 To be even more clear, I wish to reproduce this behavior of Mathematica Kernel.

In[1]:= MatrixForm[{{a,b},{c,d}}]

Out[1]//MatrixForm= a   b

                    c   d

In[2]:= Out[1]

Out[2]= {{a, b}, {c, d}}

As you can see the Kernel show Out[1]//MatrixForm i.e. MatrixForm[Out[1]] but store in Out[1] only the list {{a,b},{c,d}}. This happens for all XyzForm-like symbols, and obviously happens also in the Mathematica Front-End.

$\endgroup$
8
  • $\begingroup$ You might want m instead of mm as the second argument to Interpretation, but otherwise I'm feeling a little dense this morning -- exact what is unsatisfactory about your attempt? $\endgroup$
    – Michael E2
    Commented Jul 1, 2014 at 14:16
  • $\begingroup$ I don't have time to write a more complete explanation at the moment but the rawest way to do this is with custom MakeBoxes and MakeExpression definitions, and you can also use TemplateBox (which can be hooked to the stylesheet, too). The Notation package may meet your needs, but I personally find myself always using Make... manually. $\endgroup$
    – mfvonh
    Commented Jul 1, 2014 at 14:53
  • $\begingroup$ @MichaelE2 Yes, I fixed the code, thanks. What is unsatisfatcory? The fact I cannot take the output of this as a new input; Interpretation works only for copy-pasting, which is something I rarely use; yes, I can save the list in a symbol, but this add sometime confusion... $\endgroup$
    – unlikely
    Commented Jul 1, 2014 at 15:06
  • $\begingroup$ MatrixForm works the same way, doesn't it? (That's what it seems you're asking for, to me.) $\endgroup$
    – Michael E2
    Commented Jul 1, 2014 at 15:08
  • 1
    $\begingroup$ I'd recommend you work through Jason Harris presentation on typesetting: library.wolfram.com/infocenter/Conferences/8010 $\endgroup$ Commented Jul 1, 2014 at 22:20

3 Answers 3

6
$\begingroup$

UPDATE

Nice work, OP, with $OutputForms. I did not know about that. Here is my take on a complete solution that takes advantage of that find, and adds input handling with MakeExpression. I can't think of a situation in which this would be superior to InterpretationBox for this problem, but it is helpful in more complex cases.

If[
  FreeQ[$OutputForms, pm = PrettyMatrixForm],
  Unprotect@$OutputForms;
  AppendTo[$OutputForms, pm];
  Protect@$OutputForms];

MakeBoxes[
   PrettyMatrixForm[m_ /;
     MatrixQ[m, ExactNumberQ] \[Or]
      VectorQ[m, ExactNumberQ]], form_] ^:=
  With[
   {lcm = LCM @@ (Denominator /@ Flatten@m)},
   If[
    lcm === 1,
    MakeBoxes[MatrixForm@m, form],
    TagBox[RowBox[
      Riffle[
       MakeBoxes[#, form] & /@ {1/lcm, MatrixForm[m*lcm]},
       "\[Times]"]],
     "PrettyMatrix"]]];

MakeExpression[
   TagBox[RowBox[{
      c_,
      "\[Times]",
      m_
      }], "PrettyMatrix"], form_] :=
  MakeExpression[RowBox[{c, " ", m}], form];

PrettyMatrixForm[{{1/2, 1/4}, {2, 1/3}}]
%    

{{1/2, 1/4}, {2, 1/3}}

(Notice, however, that PrettyMatrixForm only gets stripped when boxes are actually generated. The same code with a ; after the fist line will behave differently. This is the same as MatrixForm.)

If you copy the PrettyMatrixForm output into a new cell and evaluate it, it will be rearranged before evaluation.

{{1/2, 1/4}, {2, 1/3}}

ORIGINAL POST

The FrontEnd uses a system of boxes to represent expressions. Try typing this into a cell and then hitting Ctrl+Shift+E:

matrix = {{a, b}, {c, d}};
matrix // ToBoxes

Cell[BoxData[ RowBox[{"{", RowBox[{ RowBox[{"{", RowBox[{"a", ",", "b"}], "}"}], ",", RowBox[{"{", RowBox[{"c", ",", "d"}], "}"}]}], "}"}]], "Output", CellChangeTimes->{3.613237646690503*^9}]

You can hit the same key combination to close that view. This is what's going on under the hood, and Mathematica uses a variety of mechanisms to translate between what you're seeing in the two different views -- that is, between boxes and expressions. This can happen at every layer of evaluation subject to complex rules that are not important here. In general those rules will operate the way they intuitively ought to.

We can see how an expression is translated into boxes using ToBoxes:

matrix // ToBoxes

RowBox[{{,RowBox[{RowBox[{{,RowBox[{a,,,b}],}}],,,RowBox[{{,RowBox[{c,,,d}],}}]}],}}]

Likewise, we can go the other direction:

% // ToExpression

{{a, b}, {c, d}}

Boxes are symbolically much more complex than their corresponding expressions, which is one reason they are stripped as part of evaluation:

% // (tf = TreeForm[#, VertexLabeling -> False] &)

enter image description here

%%% // tf

enter image description here

MatrixForm changes the box structure:

matrix // MatrixForm // ToBoxes

TagBox[RowBox[{(,,GridBox[{{a,b},{c,d}},RowSpacings->1,ColumnSpacings->1,RowAlignments->Baseline,ColumnAlignments->Center],,)}],Function[BoxForme$,BoxForme$]]

And Mathematica has built-in rules that tell it to interpret this box pattern correctly:

% // ToExpression

{{a, b}, {c, d}}

I mentioned that this transformation is part of the evaluation procedure. ToBoxes and ToExpression transform and evaluate, which is usually what we want. At a lower level, however, you can also specify how Mathematica should transform box structures before evaluation. This enables you to rearrange these structures and define forms of notational equivalence. Compare:

1 + 2 // ToBoxes

3

1 + 2 // MakeBoxes

RowBox[{"1", "+", "2"}]

Similarly,

RowBox[{"1", "+", "2"}] // ToExpression

3

RowBox[{"1", "+", "2"}] // MakeExpression

HoldComplete[1 + 2]

MakeBoxes will be applied whenever an expression is "rendered" in the FrontEnd, and Mathematica allows us to override arbitrary patterns. So we'll do:

MakeBoxes[PrettyMatrixForm[m_], form_] ^:=
  With[
   {lcm = LCM @@ (Denominator /@ Flatten@m)},
   If[
    lcm === 1,
    ToBoxes@MatrixForm@m,
    RowBox[ToBoxes /@ {1/lcm, "\[Times]", MatrixForm[m*lcm]}]]];

matrix = {{1/2, 1/4}, {2, 1/3}};
matrix // PrettyMatrixForm

enter image description here

We have only altered how this expression is rendered into boxes:

% // InputForm

PrettyMatrixForm[{{1/2, 1/4}, {2, 1/3}}]

For complicated cases you could define a corresponding set of rules using MakeExpression, but I think this situation can be handled more simply:

PrettyMatrixForm /: head_[left___, PrettyMatrixForm[m_], right___] := 
  head[left, m, right];

matrix // PrettyMatrixForm;
%^2

{{1/4, 1/16}, {4, 1/9}}

% // PrettyMatrixForm

enter image description here

$\endgroup$
10
  • $\begingroup$ Thanks for your detailed presentation of boxes and related construct. And also for the interesting workaround using upvalues. When I can upvote I'll do. But I'm still interested to know it there is a way to reproduce the exact beahvior of bultin MatrixForm... $\endgroup$
    – unlikely
    Commented Jul 2, 2014 at 8:08
  • $\begingroup$ @unlikely This is essentially how MatrixForm works, and the behavior is fairly close. What behavior do you want that this does not provide? $\endgroup$
    – mfvonh
    Commented Jul 2, 2014 at 12:54
  • $\begingroup$ Please see my EDIT 2 to the question. $\endgroup$
    – unlikely
    Commented Jul 2, 2014 at 13:35
  • $\begingroup$ I upvoted already, but, in addition to Out[1] or % not working in a new input cell, consider mat = RandomInteger[{1, 5}, {2, 2}]/12 // PrettyMatrixForm, which displays as lists instead of in PrettyMatrixForm. I thought I had it down once, but I must have had a latent definition lurking in the kernel and I can't reproduce it. :( The output was even labeled Out[1]//PrettyMatrixForm=...but I'm not very good at this stuff. $\endgroup$
    – Michael E2
    Commented Jul 2, 2014 at 17:34
  • 1
    $\begingroup$ @MichaelE2 (Who is good at this stuff? :P It's some of the most tedious crap you can do in Mathematica in my opinion.) Your example is a good one I hadn't thought about -- my upvalues hack isn't the best I see. I'll have to play with it some more. $\endgroup$
    – mfvonh
    Commented Jul 2, 2014 at 19:11
10
$\begingroup$

I think I finally found the right way.

$OutputForms is a list of the formatting functions that get stripped off when wrapped around the output.

$OutputForms= {InputForm,OutputForm,TextForm,CForm,Short,Shallow,MatrixForm,TableForm,TreeForm,FullForm,NumberForm,EngineeringForm,ScientificForm,QuantityForm,PaddedForm,AccountingForm,BaseForm,DisplayForm,StyleForm,FortranForm,MathMLForm,TeXForm,StandardForm,TraditionalForm}

With this approach, I understand that PrettyMatrixForm become a tagging symbol that doesn't transform its argument. Then, as explained by @tomfvonh (thanks again) we need to attach a MakeBoxes definition to PrettyMatrixForm. This is what I wrote and apparently work.

MakeBoxes[PrettyMatrixForm[m_ /; MatrixQ[m, ExactNumberQ] \[Or] VectorQ[m, ExactNumberQ]], form_] ^:=
  With[{lcm = LCM @@ (Denominator /@ Flatten@m)},
   If[lcm === 1,
    ToBoxes@MatrixForm@m,
    With[{boxes = ToBoxes@Row[{1/lcm, MatrixForm[m*lcm]}, " \[Times] "]},
     InterpretationBox[boxes, m]
     ]
    ]
   ];

If[!MemberQ[$OutputForms, PrettyMatrixForm], 
      Unprotect[$OutputForms];
  AppendTo[$OutputForms, PrettyMatrixForm];
      Protect[$OutputForms];
]

PrettyMatrixForm[{{1/2, 1/4}, {2, 1/3}}]
%*12

Probably we can do better with a little more knowledge about boxes, so any amendment is still appreciated.

Another improvement will be to handle symbolic and radicals of rational on denominators...

$\endgroup$
3
$\begingroup$
PrettyMatrixForm[m_ /; VectorQ[m, NumericQ] \[Or] MatrixQ[m, NumericQ]] := 
 With[{lcm = LCM @@ (Denominator /@ Flatten[m])}, 
  If[lcm === 1, MatrixForm[m], 
   With[{mm = m*lcm}, 
    Interpretation[Row[{1/lcm, " \[Times] ", MatrixForm[mm]}], m]]]]

You need to remember the unformatted input to PrettyMatrixForm so that the matrix is available for subsequent use since the formatted form is not directly useable.

{{1/2, 1/4}, {2, 1/3}} // PrettyMatrixForm

enter image description here

%.%

enter image description here

(m = {{1/2, 1/4}, {2, 1/3}}) // PrettyMatrixForm

enter image description here

m2 = m.m

enter image description here

Alternatively, use PrettyMatrixForm in $Post

$Post = If[MatrixQ[#], PrettyMatrixForm[#], #] &;

m = {{1/2, 1/4}, {2, 1/3}}

enter image description here

m2 = m.m

enter image description here

To clear $Post

$Post =.
$\endgroup$
2
  • $\begingroup$ Thanks, yes remembering the unformatted input can help, but sometimes add confusion and is error-prone; while a % or %% is better... I need to investigate on $Post: internal MatrixForm is based on this? $\endgroup$
    – unlikely
    Commented Jul 1, 2014 at 15:09
  • $\begingroup$ after investigating a bit, I think your code (the $Post method), while interesting, is not exactly what I'm searching for, mainly because force to use it everywhere along a session. I added some detail to my original question. $\endgroup$
    – unlikely
    Commented Jul 1, 2014 at 18:47

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.