Pacioli

Java implementation of the Pacioli language

View project onGitHub

Matrix Tutorial

An introduction to matrices in Pacioli


Dimensioned Vector Spaces

This tutorial demonstrates Pacioli's matrix features with some simplistic calculations on the resource consumption of NASA's mars rover Curiosity using dimensioned vector spaces for the rover's actions and for its resources.

A dimensioned vector space is a vector space where a unit of measurement is associated with each element. For any index set P there exist many dimensioned vector spaces, written like P!u, P!v, ... . The exclamation mark denotes a vector space and is followed by a unique identifier for a particular space. Space P! is the unique dimensionless vector space for index set P.

Create a module "Curiosity" and define two index sets

defindex Action = {picture, sample, travel, laser};

defindex Resource = {power, communication, time};

To keep the model small, the actions are limited to taking a picture, taking a soil sample, traveling or firing the laser. This is not an accurate model of Curiosity!

Now we can define the spaces. In this case the index sets and the units are known at compile time, so we can define the spaces directly. Before we do that we define the individual units that are used throughout the tutorial:

defunit sol "sol";
defunit metre "m";
defunit second "s";
defunit hour "hour" = 3600*second;
defunit joule "J";
defunit watt "W" = joule/second;
defunit bit "b";
defunit byte "B" = 8*bit;
defunit workhour "workhour";

Define a dimensioned vector space for actions as follows.

defunit Action!unit = {
    travel: metre
};

Traveling is measured in meters, the other actions are dimensionless.

For resources we define two spaces. One for the units in which the actions are planned, and one for the system's units.

defunit Resource!plan_unit = {
  power: watt*hour,
  communication: megabit,
  time: hour
};

defunit Resource!operating_unit = {
  power: joule,
  communication: byte,
  time: second
};

With these dimensioned vector spaces defined we can define vectors and matrices from these spaces.

The Matrix Type

Any numerical value in Pacioli is a matrix. A scalar is a 1x1 matrix and a vector a nx1 or 1xn matrix. All these matrix values are typed by the matrix type.

The most general matrix type is a*P!u per Q!v. The combination of a scalar unit a, the row units P!u, and the column units Q!v matches any matrix type. The next table lists some common specific ones.

Type Shorthand Description
1 per 1 1 dimensionless scalar
a per 1 a dimensioned scalar
P! per 1 P! dimensionless column vector
P!u per 1 P!u dimensioned column vector
a*P! per 1 a*P! homogeneous dimensioned column vector
1 per Q!v dimensioned row vector
P! per Q! dimensionless rectangular matrix
P!u per P!v square matrix
P!u per P!u even more square matrix
... ...

The first matrix we define is the capacity vector. The numbers are from MSL Science Corner.

defmatrix capacity :: Resource!plan_unit/sol = {
  power -> 250,
  communication -> 250,
  time -> 6
};

If you add a line capacity; to the script and run it you should see output like

Index                Value
-------------------------------------
power           250.000000 hour*W/sol
communication   250.000000 Mb/sol
time              6.000000 hour/sol

This is the capacity vector in the proper units of measurement.

Next we define a matrix for resource consumption. This is a real matrix in the sense that it is a linear transformation. It transforms vectors from an action to a resource space.

A matrix that transforms vectors from the Q!v space to the P!u has type P!u per Q!v. Define the consumption matrix as follows.

defmatrix consumption :: Resource!operating_unit per Action!unit = {
  power, sample -> 2700000,
  power, laser -> 2520,
  power, travel -> 5000,
  communication, picture -> 1500000,
  communication, sample -> 30000000,
  communication, laser -> 200000,
  time, sample -> 64800,
  time, laser -> 360,
  time, travel -> 120
};

The numbers are roughly guessed from the ChemCam fact sheet, the Curiosity wiki, NASA's raw images website, and the The Sample Analysis at Mars Investigation and Instrument Suite documentation. If you print the matrix it looks like:

Index                              Value
--------------------------------------------
power, sample             2700000.000000 J
power, travel                5000.000000 J/m
power, laser                 2520.000000 J
communication, picture    1500000.000000 B
communication, sample    30000000.000000 B
communication, laser       200000.000000 B
time, sample                64800.000000 s
time, travel                  120.000000 s/m
time, laser                   360.000000 s

Every combination of resource and action is in the proper unit.

The last matrix we define is the scientific plan. Define the plan as follows.

defmatrix plan :: Action!unit = {
   picture -> 30,
   travel -> 25,
   laser -> 10
};

We plan to take thirty pictures, travel twenty-five meters, and fire the laser ten times.

Conversion Matrices

With the available data we can compute how much resources the plan uses.

define usage = consumption '*' plan;

The '*' operator is the matrix product. The value of usage is

Index                    Value
--------------------------------
power             150200.000000 J
communication    6500000.000000 B
time                6600.000000 s

The numbers are correct, but we would like to see them in plan units. If we infer the types we see resource usage is in operating unit.

usage :: Resource!operating_unit

The consumption matrix transformed the Action!unit vector into a Resource!operating_unit vector.

To convert from Resource!operating_unit to Resource!plan_unit we can create a conversion matrix as follows:

defconv conv :: Resource!plan_unit per Resource!operating_unit;

The defined matrix conv contains the proper conversion factors to perform the desired transformation. We can get the resource usage in plan units by

conv '*' usage;

It results in

Index                Value
---------------------------------
power            41.722222 hour*W
communication    52.000000 Mb
time              1.833333 hour

This is the same resource usage, but now in plan units.

A different way to look at it is that the total transformation is conv '*' consumption.

define conv_consumption = conv '*' consumption;

It is the composition of the conversion and the consumption matrix. The type of this matrix is

conv_consumption :: Resource!plan_unit per Action!unit

It makes the total transformation from action units to resource plan units in one step. The matrix is:

Index                         Value
--------------------------------------------
power, sample            750.000000 hour*W
power, travel              1.388889 hour*W/m
power, laser               0.700000 hour*W
communication, picture    12.000000 Mb
communication, sample    240.000000 Mb
communication, laser       1.600000 Mb
time, sample              18.000000 hour
time, travel               0.033333 hour/m
time, laser                0.100000 hour

Scalar Unit Factors

Now that we can convert between the different resource spaces we can compare the capacity with the plan. The difference between the capacity and value conv_usage from the previous section is the remaining resources. The converted usage and the capacity are both in plan units so they should be comparable. However, the following fails:

capacity - conv_usage;

It correctly complains that the capacity is per sol and that the usage isn't.

During inference at line 94

capacity - conv_usage;
^^^^^^^^^^^^^^^^^^^^^
the infered type must match known types:

(Resource!plan_unit/sol, Resource!plan_unit) -> a
 =
(a*D!b per E!c, a*D!b per E!c) -> a*D!b per E!c 


Function domain's types must match:

Tuple(Resource!plan_unit/sol, Resource!plan_unit)
 =
Tuple(a*D!b per E!c, a*D!b per E!c) 


Tuple arugment 2 must match:

Resource!plan_unit
 =
Resource!plan_unit/sol 


Cannot unify units 1 and 1/sol

The type of the usage is Resource!plan_unit while the capacity vector has type Resource!plan_unit/sol. The capacity needs to be scaled by a number of sols to get to total plan units. The following definition computes the remaining capacity:

define remaining_capacity = capacity '*.' |sol| - conv_usage;

The value is

Index                Value
---------------------------------
power           208.277778 hour*W
communication   198.000000 Mb
time              4.166667 hour

Unit factors like |sol| can usually be moved around easily in expressions. For instance, the following matrix types are all equivalent

a*(P!u per Q!v) = (a*P!u) per Q!v = P!u per (Q!v/a) = (P!u per Q!v)*a

Note how the factor's reciprocal appears when it is moved to the co-variant column dimension.

The Dimensional Inverse

The reverse conversion from plan units to operating units requires the dimensional inverse of the conversion matrix. The dimensional inverse operator makes a conversion go in the opposite direction. For example to convert the capacity from plan units to operating units would require a Resource!operating_unit per Resource!plan_unit matrix. We could define such a conversion, but it is just the inverse of matrix conv. We can convert the capacity vector with the dimensional inverse operator as follows:

conv^D '*' capacity;

It gives the capacity in operating units

Index                     Value
-------------------------------------
power             900000.000000 J/sol
communication   31250000.000000 B/sol
time               21600.000000 s/sol

The dimensional inverse swaps the row and column dimension, but also takes the reciprocal. It has property x^D = x^R^T = x^T^R. The combination of the transpose and the reciprocal is exactly what is needed to invert a conversion matrix.

The dimensional inverse together with the transpose and the reciprocal form a strongly connected triple of functions. They have very useful properties in the context of dimensioned vector spaces. For example x^D^D = x, just as with transposes and reciprocals. Also the earlier x^D = x^R^T is true for any permutation of D, R and T.

Say we are interested in the work time for the Curiosity operators and we have the following (completely made up) productivity numbers:

defmatrix productivity :: Resource!operating_unit/workhour = {
  power -> 10000,
  communication -> 500000,
  time -> 1000
};

For the purpose of illustration let's assume the work is linearly related to these imaginary productivity numbers. First we take the reciprocal

define worktime = productivity^R;

It has the desired unit workhour/Resource!operating_unit and value

Index                Value
-------------------------------------
power             0.000100 workhour/J
communication     0.000002 workhour/B
time              0.001000 workhour/s

We can multiply it with the usage to get the amount of work for a plan

usage * worktime;

The value is

Index                Value
-----------------------------------
power            15.020000 workhour
communication    13.000000 workhour
time              6.600000 workhour

Or we can get take the inner product

inner(usage, worktime);

to get total

34.620000 workhour

To see how many work is required to operate Curiosity at full capacity compute

worktime * conv_capacity;

It gives

Index                Value
---------------------------------------
power            90.000000 workhour/sol
communication    62.500000 workhour/sol
time             21.600000 workhour/sol

This used the conversed capacity. We can also convert the worktime from operating units to plan units.

conv^R '*' worktime;

This conversion requires the reciprocal. It gives

Index                Value
------------------------------------------
power             0.360000 workhour/hour/W
communication     0.250000 workhour/Mb
time              3.600000 workhour/hour

The following equivalent formulations to compute the total worktime at full capacity may help explain the reciprocal.

inner(worktime, conv_capacity);
worktime^T '*' conv_capacity;
worktime^T '*' conv^D '*' capacity;
(worktime^T '*' conv^D)^T^T '*' capacity;
(conv^D^T '*' worktime^T^T)^T '*' capacity;
(conv^R '*' worktime)^T '*' capacity;
inner(conv^R '*' worktime, capacity);

All lines give the total value 174.100000 workhour/sol.

2013-2014 Paul Griffioen