How and why? 9.3
Mathematica Techniques
By Allan Hayes

In this issue: labels along axes; faster sorting; removing repeats; axes on contour plots, messages, Graphics3D displaying; .

Labels along the axes

If axes along the edges of the picture are acceptable we can use a frame.

\(\(Plot[
        Sin[x], {x, 0, 
          2}, 
FrameLabel -> {"xlabel", "ylabel"}, 
PlotRange -> 
          All, 
Frame -> {True, True, \ False, 
            False}, 
RotateLabel -> 
          False
];\)\)

Otherwise, more programming is needed:
- create the plot to be labelled

gr = 
        Plot[Sin[x], {x, 0, 2}, AxesOrigin -> { .5,  .4}];

- get the plot range and the axes origin

{{xmin, xmax}, {ymin, ymax}} =  PlotRange[gr];
{ox, oy} =  AbsoluteOptions[gr, AxesOrigin][[1, 
          2]];

- fix the axes and ticks in gr as graphics primitives by using FullGraphics
- show the result with added labels

\(\(\(Show[
        FullGraphics[
          gr], 
Graphics[{Text["xlabel", {\((xmin + 
                      xmax)\)/2, 
                oy -  .2}], 
Text["ylabel", {ox -  .2, \((ymin + ymax)\)/
                  2}]
}], 
PlotRange -> {{xmin, xmax}, {ymin, ymax}}];\)\(
\)\)\)

Some interactive adjustments may be needed.

Faster sorting

Here are three ways to sort a list of pairs by their second elements. The main lessons are:
• use the default order iff possible (manipulate the object if necessary);
• use whole-object manipulation instead of going inside the object.

lst  =  
        Table[Random[Integer, {0, 1000}], {10000}, {2}];

By defining the order:

(s1 = Sort[lst,  OrderedQ[{#1[[2]], #2[[2]]}] &]); // 
        Timing // First

By manipulation and using the built-in order

(s2 = Reverse /@ Sort[Reverse /@ lst ]); // Timing // 
      First

By using a whole object manipulation rather then going inside the objects, and using the built-in order (Transpose and Reverse are very quick)

(s3 = 
              Transpose[
                Reverse[
                  Transpose[
                    Sort[Transpose[Reverse[Transpose[lst]]]]]]]); // 
        Timing // First

Check

Last /@ s1  ===  Last /@ s2 === Last /@ s3

Removing repeats

The following elegant code is due to Carl Woll:

RemoveRepeats[x_List] := 
        Block[{i}, i[n_] := (i[n] = Sequence[];  n);  
          i /@ x];

RemoveRepeats[{2, 2, 1, 3, 2, 1, 3}]

Axes on contour plots

Suppose that we want to add axes with axes origin at {.3. .5} to the following

cp = 
        ContourPlot[x^2 + y^2, {x, 0, 1}, {y, 0, 1}, 
          ColorFunction -> (Hue[ .7  #,  .5, 1] &)];

Unfortunately any axes are hidden by the contour plot, apart from the ends that just peep out (this also happens if we use the options Axes->True, Frame->False, AxesOrigin -> {.3,.5} with  ContourPlot directly)

cp1 = 
        Show[cp,  Axes -> True,  Frame -> False, 
          AxesOrigin  ->  { .3,  .5}];

But we can construct appropriate axes with an empty graphics of the same plot range as cp; save them as graphics primitives using FullGraphics; and then add them to the contour plot. 
Notice that PlotRange[cp] has a third, z-component, which has to be dropped

axes =  
        FullGraphics[
          Graphics[{}, PlotRange  ->  Drop[PlotRange[cp], {3}], 
            Axes  ->  True,  
            AxesOrigin  ->  { .3,  .5}
]];

cp2 = Show[cp, axes,  Frame -> False];

We could have both frame and axes.

Graphics3D displaying

You may want to look at the examples given later before reading the following account.

When Mathematica is given a Graphics3D object, gr,  to display on screen, it first has to find
• the bounding box, the part of user space that will be shown, given by the option PlotRange (its edges are drawn when the option Boxed has its default value True).
• the relative edge lengths that the bounding box will appear to have (option BoxRatios), 
• the view point, from which the box will be viewed (option ViewPoint); 
• the view center, the point that we shall be looking towards, and which will usually be the center of the bounding box  (option ViewCenter) 
• the view vertical, a vector from the view center that will appear to be vertical (option ViewVertical).

The bounding box and its contents are then projected onto the projection plane, the plane through the view center that is normal to the line of sight (view center to view point). I'll try to explain how the options are implemented in terms of the several coordinate systems that are used; then I shall give some examples. While showing the examples I shall comment on how the display of the projection the notebook is affected by the options  ViewCenter, SphericalRegion and  AspectRatio

In programming, we need to get the actual, absolute, values, used (for example, although the default value of PlotRange may be Automatic, we need to know the numerical specification of PlotRange that will be used); we can find these by using the function AbsoluteOptions:

AbsoluteOptions[
      gr,  {PlotRange, BoxRatios, ViewPoint, ViewCenter, 
        ViewVertical}]

The output will be of the form

{ PlotRange -> {{xmin, xmax}, {ymin, ymax}, {zm in, zmax}}, 
        BoxRatios -> {brx, bry, 
            brz}, 
ViewPoint -> {vpx, vpy, 
            vpz}, 
ViewCenter -> {vcx, vcy, 
            vcz}, 
ViewVertical -> {vvx, vvy, 
            vvz}
};

where all symbols on the rights sides of the rules are real numbers.
Call the right sides of the rules  {pr,br,vp,vc,vv}

The basic coordinate system is the user coordinate system, the one we use in specifications like Line[{{0,0,0},{1,2,3}}] and for plotting functions. Other coordinate systems will be specified by their origin and unit vectors in user coordinates.

The box ratios are implemented in the scaled coordinate system with origin at the center of the bounding box and unit vectors  pr.{-1,1} Max[br]/br. In this system the box edges have lengths  br/Max[br], which are in the required proportions,  and the longest edge has length 1. 
The view point is also implemented in scaled coordinates. In user coordinates it is

vpu = pr.{1,1}/2 + vp pr.{-1,1} Max[br]/br;

The view center and view vertical are given in the CV-coordinate system with origin at Min/@pr (the "lower left front" of the bounding box) and unit vectors pr.{-1,1}(so in this system the box edges are all of unit length). 
In user coordinates view center and view vertical are

vcu = Min/@pr + vc pr.{-1,1}
vvu = vc pr.{-1,1}

Conversely, vp, vc and vv are given in terms of vpu, vcu and vvu respectively by:

vp  =  ( vpu - pr . {1, 1}/2 ) br/Max[br]/
          pr . {-1, 1}
vc = (vcu - Min /@ pr)/pr . {-1, 1}
vv = vvu/pr . {-1, 1}

To use these conversions in a program we need first of all to find the absolute values of the plot range and the box ratios. Here is a function that lets us use user coordinates in specifying the "view" options

FromUser[gr_, opts___?OptionQ]:=
Block[{br,pr,$DisplayFunction = Identity}, 
{br,pr} = {BoxRatios,PlotRange}/.Flatten[{opts}]/.AbsoluteOptions[gr];
Show[gr,{opts}/.{
	(ViewCenter->vcu_):>(ViewCenter-> (vcu - Min/@pr)/(pr.{-1,1}) ),
	(ViewVertical->vvu_):>(ViewVertical->vvu/( pr.{-1,1})),
	(ViewPoint->vpu_):>
		(ViewPoint->(( vpu - pr.{1,1}/2 ) br/Max[br]/ pr.{-1,1}))
	}]
 ]

Projection and lighting

To produce the display that we see on screen the bounding box and its contents are projected onto the projection plane – the plane through the view center that is normal (in scaled coordinates) to the line of sight (view center to view point). 
To describe this, a third coordinate system, the projection system, is needed. In the scaled coordinates, its origin is the view center; its y-unit vector is the unit vector, py, along the projection of the view vertical onto the projection plane; its z-unit vector is a unit vector, pz, towards the view point and its x-unit vector, px,  is  py×pz. 
Light source directions {r, u, v}are given in this coordinate system; they can be remembered as (right, up, viewer).

To get a feel for the directions of the default lighting,
{{{1.,0.,1.},RGBColor[1,0,0]},{{1.,1.,1.},RGBColor[0,1,0]},{{0.,1.,1.},
      RGBColor[0,0,1]}}}
imagining yourself at the viewpoint with the view center at arms length in front of you and with pz vertical; point to your right and let your arm describe a 90° arc to vertically up; the red, green and blue lights are at 0°, 45° and 90°. More precisely, it is the directions of these lights from the view center that determines the directions from which the lights are coming.

Examples

\(\(\(gr = 
        Show[Graphics3D[{
Line[{{ .5,  .5, 2}, { .5, 2.5, 
                    6}}], 
GrayLevel[
                0], 
AbsolutePointSize[
                5], \ 
Point[{5, 5, 
                  5}], 
Point[{ .5,  .5, 
                  2}], 
Point[{ .5, 1, 
                  3}], 
Point[{ .5, 2.5, 
                  6}], 
Text["o", { .07,  .05, 
                  1.15}], 
Text["x", { .95,  .05, 
                  1.15}], 
Text["y", { .05,  .95, 
                  1.15}], 
Text["z", { .05,  .07, 
                  2.9}], 
Text["c", { .52,  .52, 
                  1.8}], 
Text["d", { .55,  .96, 
                  2.9}], 
Text["e", { .45, 2.46, 
                  5.9}]
}, \ 
            TextStyle -> {FontSize -> \ 14, 
                FontWeight -> "Bold"}], 
Background -> 
            GrayLevel[ .85]];\)\(
\)\)\)

The outlined bounding box shows the plot range, the part of user space to be shown, it is controlled by the option PlotRange (it is outlined because of the default, Boxed -> True). With the default, PlotRange -> Automatic, Mathematica sets the plot range to be what it calculates is the significant part of user space (notice that the point Point[{5,5,5}] is not included – we could  include it by using PointRange -> All).

Let'a set our own plot range

gr1  =  
        Show[gr,  
          PlotRange -> {{0, 1}, {0, 1}, {1, 
                3}}];

Because of the default, BoxRatios -> Automatic, the bounding box is shown above with its shape in user coordinates.

If we specify the box ratios numerically,  BoxRatios-> {4, 1, 2}, then the edges of the bounding box appear to have lengths in the ratios 4:1:2.

gr2  = Show[gr1,  BoxRatios ->  {4, 1, 2}];

ViewPoint->{1.,.4,-.4} , specifies the point from which we look using the scaled coordinate system with origin at the center, c, of the box and unit vectors all equal in length to the edge of the box with the biggest entry in box ratios, here this is ox.

gr3 = Show[gr2, ViewPoint -> {1. ,  .4, - .4}];

In all the displays so far, the final two dimensional projection just fits is inside a rectangle, the plot region, shown here shaded, that has a maximum side of 288 printer's points (set by the cell and graphics option ImageSize).
This implementation alters if we change from the default ViewCenter -> Automatic.  Recall that ViewCenter->{vcx,vcy,vcz} specifies the position of the point at which are looking, in the CV-coordinate system with origin o and in which the bounding box edges are all of length 1. 
With the default ViewCenter -> Automatic, as above, we get

AbsoluteOptions[gr2,  ViewCenter]

However  if we use ViewCenter->{.5,.5,.5} explicitly then we find that the final display is now constructed by fitting the projection of the bounding box inside a 288×288 point plot region with the projection of the center, c, of the box at the center of the plot region.

gr4 = Show[gr3,ViewCenter->{.5,.5,.5}];

Here is a more extreme example

gr5 = Show[gr4,ViewCenter->{1,.0,.0}];

ViewVertical→ {0,.5,.5} specifies that, in the CV-coordinate system, the  vector {0,.5,.5} drawn from the view center must appear vertical in the final projection

gr6 = Show[gr5,ViewVertical -> {0,.5,.5}];

SphericalRegion

The method used above to show the projection of the bounding box in the plot region is not suitable if we want to look at the bounding box from the same distance in various directions. With SphericalRegion -> True , the view center is always taken to be the center, c,  of the bounding box; and the projection of the smallest sphere centre c around the bounding box is centered in plot region and touches its sides

gr7 = 
      Show[gr6,  ViewPoint -> {6, 10, -2},  
        SphericalRegion -> True]

Other options

gr8 = Show[gr7,  ImageSize  ->  {300,  150}];

gr9 = Show[gr8,  AspectRatio  ->  2];

Notice that it is the bounding box that is scaled by AspectRatio – the display region remains the same.

You might also like to look into the effects of the graphics option, PlotRegion and the cell options ImageSize, ImageRegion and ImageMargins.

Problem

How can we show realistically the spiral approach of the view point to a graphics object? We will certainly have to alter the plot range.

Messages

Messages are generated during evaluation to indicate and warn of what has happened. Many are generated automatically but we can also program our own. Messages can be turned on and off. We can use the input that is used to generate the message to, for example, modify an evaluation or build up a data list.

The Help Browser can help in understanding the built-in messages: to find out about the warning message that is generated below

Plot[a, {a, b, c}]

- select plln (not Plot::plln)
-  Help > Find Selected Function
- click button Other Information 
- click button Go To

We can set up our own messages:
- assign a control string to message "n" for symbol s ( s can be protected or even locked).

MessageName[s, "n"]= "`1` before `2`";

Note:
Special form:  s::n   or   s::"n" for  MessageName[s, "n"].
Attributes: MessageName and Messages , below, are HoldFirst.

The corresponding rule is added to the list of messages for s. For user-defined messages these lists can be shown:

Messages[s]

- display a message with inserts for the items `1` and `2` (usually, we also get a beep)

Message[MessageName[s, "n"], "work",  "play"]

- turn the message off :

Off[s::n]

This just wraps the message name in $Off:

s::n

The message is not now shown:

Message[s::n,  "work",  "play"]

- turn the message back on

On[s::n]

$Off  has been removed:

s::n

and the message is shown:

Message[s::n,  "work",  "play"]

The most common messages to be turned off are those arising from spell-checking , these are turned off  by evaluating Off[General::spell, General::spell1] (note the lower case s, and that we can turn on and off a sequence of message names).

• Turning on and turning off does not affect the display of usage messages with ? and ??,  which use Information rather than Message. For example after

t::usage  =  "about t";
Off[t::usage]

we still get

?t

?? t

See also:
$MessageList: the list of message names for messages so far generated (even if not displayed) since the line was input. It is reset to {} when the line has been processed.
MessageList[n]: the list  of message names of messages displayed during the evaluation of a previous Input[n]  ($Line, it is assigned a value only after the line has been processed);
Check[expr,failexpr] gives expr* (the value of expr) if no message was displayed in evaluating expr (turned-off messages are not counted) , otherwise it gives failedexpr*. 
Check[expr,failexpr,s::n..] gives expr* if no message with name in the,  sequence s::n..  was displayed in evaluating expr , otherwise it gives failedexpr*.

Programming with Messages

Input checking and returning unchanged.

Warning messages can be very helpful to users. Here is some code with no warning messages: it only works as intended when x is an expression with an odd number of elements. You will see several way in which the user could make mistakes. Of course we must expect that adding tests and messages will slow down the computation; and, certainly, doing the extra programming can be quite time-consuming.

Clear["`*"]
middle[x_] := x[[Ceiling[Length[x]/2]]]

We could protect the user much more:

Clear["`*"]
middle[x_ /; OddQ[Length[x]]] := 
      x[[Ceiling[Length[x]/2]]]

But all that happens now is that with inappropriate entries the expression is returned unchanged:

middle[{1, 2}]

We can give more comprehensive protection and add helpful messages (one of the message names, middle::argx, uses a built-in definition (look it up as shown earlier); the other one, middle::nodd is specially defined):

Clear["`*"]
\(middle[
        x___] := 
x[\([Ceiling[
              Length[x]/2]]\)] /; 
If[
	
          Length[{x}] === 1, 
		If[OddQ[Length[x]], True, 
            Message[middle::nodd]], 
Message[middle::argx, 
            middle, \ Length[{x}]]
]\)

Define middle::nodd:

middle::nodd =  "The argument of middle is expected to be normal \
with an odd number of elements";

Tests

middle[{1, 2}]

middle[1, 2]

middle[p[1, 2, 3]]

Here is an alternative style, due to David Withoff.  It is more complicated for simple examples, but using Throw and Catch lets us escape from deep within complex code.

Clear["`*"]

middle[x___] := 
      With[{result = middle1[x]}, 
        result /; 
          result =!= $Failed
]
\(middle1[
        x___] := \ 
Module[{tag}, 
Catch[
          If[
	
            Length[{x}] === 1, 
		If[OddQ[Length[x]], 
              Throw[x[\([Ceiling[Length[x]/2]]\)], tag], 
              Message[middle::nodd]; 
              Throw[$Failed, \ tag]], 
Message[
              middle::argx, middle, \ Length[{x}]]; 
            Throw[$Failed, 
              tag]
], 
tag]
]
\)

middle[{1, 2}]

middle[1, 2]

middle[p[1, 2, 3]]

Using data supplied to Message

There is a problem – messages are not output, they are printed.  Moreover, we may not want the message to be displayed. We have to do a little programming: here are some examples.

SilentCheck, below, is  a variant of Check, based on an idea of Carl Woll for getting the benefit of Check without displaying any messages(the reason for the condition f_/;Not[MatchQ[f,_$Off]] will be explained later; the attributes HoldAll guarantees that the evaluation of expr take place on the right, inside Catch[expr, SilentCheck]).

\(\(SilentCheckPlus::usage = \ 
\*""SilentCheckPlus[expr_, failexpr_] evaluates expr and gives its value, \!\(\(expr\^*\)\), unless expressions like Message[s::t, d,...] are evaluated, in which case it gives \!\(\(failexpr\^*\)\). No messages are displayed."";\)\)

ClearAll[SilentCheck];
SetAttributes[SilentCheck,  HoldAll]
SilentCheck[expr_,  
        failexpr_] := 
Block[{Message}, 
Message[f_ /; Not[MatchQ[f, _$Off]], ___] :=  
          Throw[failexpr, SilentCheck]; 
Catch[expr, 
          SilentCheck]
]

Tests

SilentCheck[FindMinimum[-Sin[Pi x], {x, 0, 0, 0.4}],  
      ff]

SilentCheck[FindMinimum[-Sin[Pi x], {x, 0, 0, 1}],  
      ff]

SilentCheckPlus lets us use the inputs to Message.

\(\(SilentCheckPlus::usage = \ 
\*""SilentCheckPlus[expr, f] evaluates expr and gives its value, \!\(\(expr\^*\)\), unless expressions like Message[s::t, d,...] are evaluated, in which case it gives \!\(\(f\^*\)\)[{s::t, d,...}, ...]. No messages are displayed.\n
SilentCheckPlus[expr_, s1::t1, s2::t2,...] evaluates expr and gives its value, \!\(\(expr\^*\)\), unless expressions like Message[s::t, d,...] with s::t in {s1::t1, s2::t2,...} are evaluated, in which case it gives \!\(\(f\^*\)\)[{s::t, d,...},...]. No messages are displayed"";\)\)

SetAttributes[SilentCheckPlus,  
        HoldAll];

SilentCheckPlus[expr_,  f :  _?(Not[MatchQ[#, _MessageName]] &), 
        mns___MessageName] := 
Block[{Message,  
          acc = f[]}, 
Message[
            n_ /; Not[
                  MatchQ[n, _$Off]] && ({mns} == {} || 
                    MemberQ[{mns}, n]), 
r___] := 
          AppendTo[
            acc, {n, r}]; 
If[acc === f[], #, acc] &[
          expr]
]

SilentCheckPlus[FindMinimum[-Sin[Pi x], {x, 0, 0, 0.4}],  
      f]

SilentCheckPlus[FindMinimum[-Sin[Pi x], {x, 0, 0, 1}],  
      f]

SilentCheckPlus[1/0 +  0^0 + 0 Infinity, f]

SilentCheckPlus[1/0 +  0^0, f,  Power::"indet"]

•Notice that some built-in message names, here Power::indet and ∞::indet,  do not have a control string assigned.

• Now for an explanation of why  Not[MatchQ[n,_$Off]] is used in SilentCheck and SilentCheckPlus.

SilentCheckPlus2 and SilentCheck below are SilentCheckPlus and Silent Check with  Not[MatchQ[n,_$Off]] deleted.

SetAttributes[SilentCheckPlus2,  
        HoldAll];

SilentCheckPlus2[expr_,  f :  _?(Not[MatchQ[#, _MessageName]] &), 
        mns___MessageName] := 
Block[{Message,  
          acc = f[]}, 
Message[
            n_ /; ({mns} == {} || 
                  MemberQ[{mns}, n]), 
r___] := 
          AppendTo[
            acc, {n, r}]; 
If[acc === f[], #, acc] &[
          expr]
]

Short[SilentCheckPlus2[Integrate[x,  {x, 1,  2}], f], 4]

We see that an enormous number of expressions like Message[$Off[],...] have been generated, in fact 505 of them. Their contribution to the output is probably not wanted, and the output is not as expected – it should be 3/2.
A second evaluation does not show this feature (this is why we quitted the kernel), so it could be due to loading the code for integrating.

Short[SilentCheckPlus2[Integrate[x,  {x, 1,  2}], f], 4]

But a different integral give similar problems

Short[SilentCheckPlus2[Integrate[1/x^2,  {x, 1,  Infinity}],  f], 
      4]

In this case we can see what is happenning, but with SilentCheck2 below, the there is no evidence.

Please quit the kernel.

SetAttributes[SilentCheck2,  HoldAll]
SilentCheck2[expr_,  
        failexpr_] := 
Block[{Message}, 
Message[___] :=  
          Throw[failexpr, SilentCheck2]; 
Catch[expr, 
          SilentCheck2]
]

SilentCheck2[Integrate[x,  {x, 1,  2}],  $Failed]

SilentCheck2[Integrate[x,  {x, 1,  2}],  $Failed]