\( \DeclareMathOperator{\abs}{abs} \newcommand{\ensuremath}[1]{\mbox{$#1$}} \)

Vectors

 1 Note about kill(all)

In this lesson, we will be defining custom operations and bindings.  It is strongly advised that you
avoid using the kill(all) command as it will destroy those customizations!

 2 Basic Vector Operations

Vectors in Maxima are nothing more than lists indicated by square brackets.  So, for example,
the vector <1,0> and <0,1> are encoded as:

--> [1,0];
[0,1];
\[\tag{%o3} [1,0]\] \[\tag{%o4} [0,1]\]

We can encode 3-vectors in the same way:

--> [1,0,0]; [0,1,0]; [0,0,1];
\[\tag{%o5} [1,0,0]\] \[\tag{%o6} [0,1,0]\] \[\tag{%o7} [0,0,1]\]

Addition, subtraction, and scalar multiplication work in the obvious ways:

--> [2,2,2]+[1,3,5];
[2,2,2]-[1,3,5];
5*[1,3,5];
[1,3,5]/5;
\[\tag{%o8} [3,5,7]\] \[\tag{%o9} [1,-1,-3]\] \[\tag{%o10} [5,15,25]\] \[\tag{%o11} [\frac{1}{5},\frac{3}{5},1]\]

And we can bind vectors to variables as usual:

--> u:[1,3,5]; v:[2,2,2];
v+u;
v-u;
5*u;
u/5;
\[\tag{u}[1,3,5]\] \[\tag{v}[2,2,2]\] \[\tag{%o14} [3,5,7]\] \[\tag{%o15} [1,-1,-3]\] \[\tag{%o16} [5,15,25]\] \[\tag{%o17} [\frac{1}{5},\frac{3}{5},1]\]

 3 Standard Basis Vectors i,j,k

Note that Maxima does not come equipped with i, j, or k, but we can make them ourselves.
we could bind:

--> i:[1,0,0];
j:[0,1,0];
k:[0,0,1];
\[\tag{i}[1,0,0]\] \[\tag{j}[0,1,0]\] \[\tag{k}[0,0,1]\]

And now we can define a vector <a,b,c> in the usual way:

--> a*i+b*j+c*k;
\[\tag{%o21} [a,b,c]\]

Note that, unlike humans, Maxima doesn't have the luxury of knowing the *context* of i and j, and so
it doesn't know when i and j are supposed to be vectors in 2D or 3D. Our definition above forces
i and j to be 3D vectors.  If we want 2D vectors, we could define:

--> I:[1,0]; J:[0,1];
\[\tag{I}[1,0]\] \[\tag{J}[0,1]\]

And now:

--> a*I+b*J;
\[\tag{%o24} [a,b]\]

But it is definitely not the case that i=I or j=J:

--> is(i=I);is(j=J);
\[\tag{%o25} \mbox{false}\] \[\tag{%o26} \mbox{false}\]

Note again that if you use kill(all), this will unbind i,j,k, I, J and these will no longer hold the basis vectors that
you intend!  So caution with this technique is advised.

 4 The Dot Product and Magnitude

The dot product between two vectors is given by the English period (.).  So for instance:

--> [1,1,1].[2,1,3];
\[\tag{%o27} 6\]
--> [0,1].[3,4];
\[\tag{%o28} 4\]

Recall the magnitude of a vector u is related to the dot product by way of:

|u| = sqrt(u.u)

This is exactly how we can compute the magnitude of a vector irrespective of whether or not it
is 2D or 3D:

--> u:[1,2,2];
v:[1,1];
sqrt(u.u);
sqrt(v.v);
\[\tag{u}[1,2,2]\] \[\tag{v}[1,1]\] \[\tag{%o111} 3\] \[\tag{%o112} \sqrt{2}\]

If we know that we are going to be computing a lot of magnitudes, we can define a function:

--> mag(x):=sqrt(x.x);
\[\tag{%o34} \operatorname{mag}(x):=\sqrt{x\mathit{ . }x}\]

So that the above calculations become:

--> mag(u);mag(v);
\[\tag{%o35} 3\] \[\tag{%o36} \sqrt{2}\]

Again, if you call kill(all), this function will be obliterated --- so caution is advised.

 4.1 Making Unit Vectors

We know that a vector u can be normalized by u/|u|.  Now that we have a magnitude function, this can
easily be done in Maxima:

--> u:[1,2,3];
uhat:u/mag(u);
\[\tag{u}[1,2,3]\] \[\tag{uhat}[\frac{1}{\sqrt{14}},\frac{2}{\sqrt{14}},\frac{3}{\sqrt{14}}]\]

Note that:

--> mag(uhat);
\[\tag{%o97} 1\]

as required.

We can even define a function hat() that takes in a vector and automatically produces its unit form:

--> hat(u):=u/sqrt(u.u);
\[\tag{%o98} \operatorname{hat}(u):=\frac{u}{\sqrt{u\mathit{ . }u}}\]

So that:

--> hat(u);
\[\tag{%o99} [\frac{1}{\sqrt{14}},\frac{2}{\sqrt{14}},\frac{3}{\sqrt{14}}]\]

Notice that since we used vector operations to define hat(), the above definition works equally well for
2D vectors:

--> hat([1,1]);
\[\tag{%o113} [\frac{1}{\sqrt{2}},\frac{1}{\sqrt{2}}]\]

This shortcut could come in really handy -- as we will see in the following section.

 4.2 The Angle Between Two Vectors

The dot product is related to the angle between two vectors via:

u.v = |u||v|cos(%theta)

If we divide both sides by the magnitudes we get:

hat(u).hat(v) = cos(%theta)

So let's say we have defined the hat() function and we want to know the angle between u=<1,0,0> and v=<1,1,0>.
We know that the answer should be %pi/4, so let's check.  Note that acos() is arccos:

--> acos(hat([1,0,0]).hat([1,1,0]));
\[\tag{%o101} \frac{\ensuremath{\pi} }{4}\]

Perfect!  Of course, we do not require the hat() function to make the above work, it just makes life a little simpler.
Let's find the angle between the vectors i and -i+j:

--> acos(i.(-i+j)/mag(-i+j));
\[\tag{%o104} \frac{3 \ensuremath{\pi} }{4}\]

Or using hat():

--> acos(i.hat(-i+j));
\[\tag{%o105} \frac{3 \ensuremath{\pi} }{4}\]

What if the angle isn't so pretty?

--> acos(i.hat(2*i+3*j));
\[\tag{%o114} \operatorname{acos}\left( \frac{2}{\sqrt{13}}\right) \]

Maxima resorts -- as always -- to giving us the most exact answer possible.  In this case that means it leaves the
arccosine unevaluated.  We can force the result by way of float():

--> float(acos(i.hat(2*i+3*j)));
\[\tag{%o115} 0.98279372324732\]

We see that the angle between <1,0> and <2,3> is just shy of 1 radian, or just a little south of 100*%pi/315 since:

--> float(100*%pi/315);
\[\tag{%o119} 0.99733100113961\]

(If you think about it for just a second, you should be able to figure out where 100%pi/315 came from --- hint:
what is %pi to three decimal places?)

 5 The Cross Product

Maxima does not have a cross product loaded by default.  In order to gain access to it we must load the "vect" package like so:

(%i1) load("vect");
\[\tag{%o1} /usr/share/maxima/5.32.1/share/vector/vect.mac\]

Note: when you first load the vect package, it may need to be compiled -- and a string of messages may appear in your session telling you the progress of the compilation.  If you want to supress these messages altogether, you can simply call:

load("vect")$

With a dollar sign after it.  In any case, don't worry about any of the compiling messages!  It's normal.  Once it is loaded, we compute the cross product with the tilde (~) which is in the upper left hand corner of most keyboards.  It is computed like so:

(%i3) u:[1,2,3];v:[3,2,1];
\[\tag{u}[1,2,3]\] \[\tag{v}[3,2,1]\]
(%i4) u~v;
\[\tag{%o4} [1,2,3]\mbox{\sim }[3,2,1]\]

Wait... what?!  The above is actually calculated, but it's not being expressed in the form that is convenient for humans.
To get the human readable answer, we ask for:

(%i5) express(u~v);
\[\tag{%o5} [-4,8,-4]\]

Note that as usual:

(%i6) express(v~u);
\[\tag{%o6} [4,-8,4]\]

So, to take a cross product, we do: express(u~v):

(%i12) i:[1,0,0];j:[0,1,0];k:[0,0,1];
express(i~j);
express(j~k);
express(k~i);
\[\tag{i}[1,0,0]\] \[\tag{j}[0,1,0]\] \[\tag{k}[0,0,1]\] \[\tag{%o10} [0,0,1]\] \[\tag{%o11} [1,0,0]\] \[\tag{%o12} [0,1,0]\]
(%i13) express((3*i+2*j)~(4*k));
\[\tag{%o13} [8,-12,0]\]

 5.1 Crossing 2D Vectors:

Let's see what happens when we cross two 2D vectors:

(%i14) express([1,0]~[0,1]);
\[\tag{%o14} 1\]

Hmmm... that's not a vector... let's try a few more:

(%i15) express([0,1]~[1,0]);
\[\tag{%o15} -1\]

That's good that the result is negative, but it's still not clear what's going on...

(%i16) express([a,b]~[c,d]);
\[\tag{%o16} a d-b c\]

Aha!  We recognize this as the usual cross product for 2D vectors --- without the k vector!
When you give Maxima two 2D vectors, it only gives back the k *component* of the cross
product and not the vector.

This is due to the common practice of defining the cross product of two 2D vectors as simply a
scalar -- since k does not technically exist in 2D space, this is reasonable, though a little problematic
when we consider the cross product in 3D.

Our advice on this issue is to only cross 3D vectors.  If you want to cross [1,2] and [3,4] for instance
then:

(%i17) express([1,2,0]~[3,4,0]);
\[\tag{%o17} [0,0,-2]\]

If we have defined i,j,k as three vectors, then this poses no real issue since:

(%i18) express((i+2*j)~(3*i+4*j));
\[\tag{%o18} [0,0,-2]\]

Looks the same in 2D as in 3D and yields the correct (more general) vector result.

 5.2 (Re)Defining the Cross Product for Convenience: The ## Helper

If the call to express() bugs you, then you can define your own custom cross product that includes the call to express by way of:

(%i20) infix("##");
u ## v:=express(u~v);
\[\tag{%o19} \neq \neq \] \[\tag{%o20} u\mathit{ \neq \neq }v:=\operatorname{express}\left( u\mbox{\sim }v\right) \]

The first line tells Maxima that ## is going to be a new operator, while the second line tells Maxima how that operator will do
its work.  The word "infix" means that the operator is "fixed" with"in" two things.  There are also prefix, postfix, and matchfix
operators that work pre-, post-, and around expressions as well!  We will define a prefix operator @ in a later section.

Let's see the results of this new addition:

(%i25) [1,2,0]##[3,4,0];
\[\tag{%o25} [0,0,-2]\]
(%i28) i##j;j##k;k##i;
\[\tag{%o26} [0,0,1]\] \[\tag{%o27} [1,0,0]\] \[\tag{%o28} [0,1,0]\]

Extreme caution must be taken when defining your own custom operators, as you are overloading the default behavior
of the core language.  For simple calculations that we need, this shouldn't pose too much of a problem however.

Again, if you kill(all), you will lose the above.

Finally, we note that when using our custom ## we must take care to use parentheses for compound expressions,
since we did not tell Maxima exactly where ## sits in the order of operations!

 6 Scalar Triple Product

The scalar triple product of three vectors u,v,w is defined to be u.(v~w) and so we can now compute it via:

u.express(v~w)

As a test case, let's consider i.express(j~k) which should give 1, and j.express(i~k) which is -1:

(%i30) i.express(j~k);
j.express(i~k);
\[\tag{%o29} 1\] \[\tag{%o30} -1\]

If we use our redefined cross product:

(%i32) i.(j##k);
j.(i##k);
\[\tag{%o31} 1\] \[\tag{%o32} -1\]

As one further test, note that the scalar triple product just gives the determinant of the matrix with
rows (or columns) given by the vectors u,v,w.  So let's test that the above is doing the right thing:

(%i33) kill(u,v,w);
\[\tag{%o33} \mathit{done}\]
(%i34) [u[1],u[2],u[3]].express([v[1],v[2],v[3]]~[w[1],w[2],w[3]]);
\[\tag{%o34} {u_1}\, \left( {v_2}\, {w_3}-{w_2}\, {v_3}\right) +{u_2}\, \left( {w_1}\, {v_3}-{v_1}\, {w_3}\right) +\left( {v_1}\, {w_2}-{w_1}\, {v_2}\right) \, {u_3}\]
(%i35) [u[1],u[2],u[3]].([v[1],v[2],v[3]]##[w[1],w[2],w[3]]);
\[\tag{%o35} {u_1}\, \left( {v_2}\, {w_3}-{w_2}\, {v_3}\right) +{u_2}\, \left( {w_1}\, {v_3}-{v_1}\, {w_3}\right) +\left( {v_1}\, {w_2}-{w_1}\, {v_2}\right) \, {u_3}\]

Careful comparison with the formula for the determinant of the 3x3 matrix with rows
given by u,v, and w shows that the above both yield the correct result, and we
can be confident in our computations.

 6.1 Creating a Custom Triple Product

If we have defined ## as the cross product, then it is pretty simple to type in u.(v##w)
to get the triple product.  But suppose we want a function to do that for us.  We can
define:

(%i36) triple(x,y,z):=x.express(y~z);
\[\tag{%o36} \operatorname{triple}\left( x,y,z\right) :=x\mathit{ . }\operatorname{express}\left( y\mbox{\sim }z\right) \]

So now, if u,v, and w are symbollically given by:

(%i37) triple([u[1],u[2],u[3]],[v[1],v[2],v[3]],[w[1],w[2],w[3]]);
\[\tag{%o37} {u_1}\, \left( {v_2}\, {w_3}-{w_2}\, {v_3}\right) +{u_2}\, \left( {w_1}\, {v_3}-{v_1}\, {w_3}\right) +\left( {v_1}\, {w_2}-{w_1}\, {v_2}\right) \, {u_3}\]

Which can be really nice if you already have u,v, and w previously defined!

(%i42) u:[1,1,0]; v:[0,2,3];w:[0,0,-7];
triple(u,v,w);
triple(v,u,w);
\[\tag{u}[1,1,0]\] \[\tag{v}[0,2,3]\] \[\tag{w}[0,0,-7]\] \[\tag{%o41} -14\] \[\tag{%o42} 14\]

Or for display purposes:

(%i43) 'triple(u,v,w)=triple(u,v,w);
\[\tag{%o43} \operatorname{triple}\left( [1,1,0],[0,2,3],[0,0,-7]\right) =-14\]
Created with wxMaxima.