From jmc at research.bell-labs.com  Mon Jun 16 16:38:28 2003
From: jmc at research.bell-labs.com (John Chambers)
Date: Mon Jun 16 21:38:50 2003
Subject: [Rd] Creating a vector class
References: <creddv80t0693ebb5b3qdt847jvr4r95p5@4ax.com>
Message-ID: <3EEE1CB4.7C81E8D4@research.bell-labs.com>

(better late than never, hopefully--a few comments on this example)

Duncan Murdoch wrote:
> 
> I'm trying to create a package for working on orientation data, i.e.
> data where the observations are 3D rotations.
> 
> There are several different representations of orientations in common
> use:  SO(3) matrices, Euler angles, unit quaternions, etc.  One thing
> I'd like is to make it convenient to work in any representation, and
> have conversions to others done as needed.
> 
> I'm trying to do all of this in S4 classes.  Here's the current class
> structure I'm using; please let me know if there's something wrong
> with this:
> 
> The base class is abstract.  All representations will deal with
> vectors of orientations, but at this level I don't know how that will
> be implemented:
> 
> setClass('orientation')
> setIs('orientation', 'vector')
> 
> First questions:  is this the way to say that orientations behave as
> vectors?  

It seems to say the right thing.  1) 'orientation' is a virtual class;
2) it extends 'vector', with no implication about the contents of either
class and, since 'vector' is also virtual, no need to supply functions
to coerce one to the other.


> Do I need to say that?  Should I say that?

Yes to the second question.  To be honest, it probably doesn't make much
practical difference now, but it's the "right thing" to do, as I
understand your example.

The assertion is that for all 'orientation' objects, the vector
computations ( principally "[", "[[", and length) are meaningful & so 
'orientation' objects should inherit methods written for class "vector"
in terms of such computations.

In practice, there aren't many such methods, but there may be more
sometime.  Furthermore, there are many computations that are currently
not methods but that should conceptually be methods for "vector".  The
function rev, e.g.:

R> rev
function (x) 
if (length(x) > 0) x[length(x):1] else x
<environment: namespace:base>

This should really not be applied to non-vectors, and the definition is
in terms of vector computations.

A related point is that "[", etc. currently apply to objects for which
they are not conceptually meaningful.  Objects representing calls to
functions, for example.   (Both R and S-Plus allow it. R gets credit for
not allowing subsets of function objects, though, which S-Plus does
allow.)

R> ttt = quote(plot(x^2, y, pch ="+"))
R> ttt[2:3]
x^2(y)
R> rev(ttt)
"+"(y, x^2, plot)

!!


The behavior of [ is perhaps unlikely to be changed in base.  But if one
could optionally attach a more strict method-based package, I think it
would make sense to restrict "[" to classes that extend "vector" (which
includes, of course, the basic vector datatypes).
> 
> One representation is as an SO(3) matrix (i.e. a 3x3 matrix with
> determinant 1).  I have a descendant class that stores these in a
> 3 x 3 x n array:
> 
> setClass('rotmatrix', representation(x = 'array'))
> setIs('rotmatrix','orientation')
> 
> rotmatrix <- function(a) {
>     d <- dim(a)
>     if (length(d) < 3) d <- c(d,1)
>     a <- array(a, d)
>     stopifnot(dim(a)[1] == 3, dim(a)[2] == 3)
>     new('rotmatrix', x = a)
> }
> 
> Other representations have other storage methods, e.g.
> 
> setClass('quaternion', representation(x = 'matrix'))
> setIs('quaternion', 'orientation')
> 
> Now I want to make sure these work as vectors.  I don't need to define
> a '[' method for the abstract base class, do I?  

No, you don't need or want such a method.  In fact, it would be nice to
require that non-virtual classes extending 'orientation' DO have methods
for "[" (and, presumably, length()?  what about "[["?)

There is a simple technique considered for the Omegahat OOP package that
might be a useful addition in S4 methods as well.  We would introduce a
function, requireMethod, to force methods to be defined:

  requireMethod("[", "orientation")

The function is just like setMethod, except without a method definition,
and its effect is to generate an error for an object that dispatches
this combination of function and signature.  (Under the hood, it can
just call setMethod with a method that produces an appropriate error
message.)

The idea seems to convey what we want for virtual classes such as
'orientation'.  For example, by defining your method below for
"rotmatrix", you satisfy the requirement, and block dispatch of the
error method.

> I originally set this
> definition for the rotmatrix class:
> 
> setMethod('[', 'rotmatrix',
>     def = function(x, i) rotmatrix(x@x[,,i,drop=FALSE])
> )
> 
   ... the rest was discussed before

> Duncan Murdoch
> 
> ______________________________________________
> R-devel@stat.math.ethz.ch mailing list
> https://www.stat.math.ethz.ch/mailman/listinfo/r-devel

-- 
John M. Chambers                  jmc@bell-labs.com
Bell Labs, Lucent Technologies    office: (908)582-2681
700 Mountain Avenue, Room 2C-282  fax:    (908)582-3340
Murray Hill, NJ  07974            web: http://www.cs.bell-labs.com/~jmc


[Rd] Creating a vector class
John Chambers