Title: | Sparse Arrays and Multivariate Polynomials |
---|---|
Description: | Sparse arrays interpreted as multivariate polynomials. Uses 'disordR' discipline (Hankin, 2022, <doi:10.48550/ARXIV.2210.03856>). To cite the package in publications please use Hankin (2022) <doi:10.48550/ARXIV.2210.10848>. |
Authors: | Robin K. S. Hankin [aut, cre] |
Maintainer: | Robin K. S. Hankin <[email protected]> |
License: | GPL (>= 2) |
Version: | 1.0-27 |
Built: | 2025-01-05 06:20:06 UTC |
Source: | https://github.com/robinhankin/spray |
Functionality for sparse arrays, with emphasis on their interpretation as multivariate polynomials.
Base R has the capability of dealing with arbitrary dimensioned
numerical arrays, with the array
class.
A sparse array is a type of array in which nonzero elements are stored along with an index vector describing their coordinates. This allows for efficient storage and manipulation as base arrays often require the storing of many zero elements which consume computational and memory resources.
In the package, sparse arrays are represented as objects of class
spray
. They use the C++ standard template library
(STL) map
class, with keys being (unsigned) integer vectors, and
values floats.
One natural application of sparse arrays, for which the package was
written, is multivariate polynomials and the package vignette presents
an extended discussion. Note that other interpretations exist: the
stokes and weyl packages interpret spray
objects as differential forms and elements of a Weyl algebra
respectively.
Robin K. S. Hankin
# define a spray using a matrix of indices and a vector of values: M <- matrix(sample(0:3,21,replace=TRUE),ncol=3) a <- spray(M,sample(7)) # there are many pre-defined simple sprays: b <- homog(3,4) # arithmetic operators work: a + 2*b a - a*b^2/4 a+b # we can sum over particular dimensions: asum(a+b,1) # differentiation is supported: deriv(a^6,2) # extraction and replacement work as expected: b[1,2,1] b[1,2,1,drop=TRUE] b[diag(3)] <- 3
# define a spray using a matrix of indices and a vector of values: M <- matrix(sample(0:3,21,replace=TRUE),ncol=3) a <- spray(M,sample(7)) # there are many pre-defined simple sprays: b <- homog(3,4) # arithmetic operators work: a + 2*b a - a*b^2/4 a+b # we can sum over particular dimensions: asum(a+b,1) # differentiation is supported: deriv(a^6,2) # extraction and replacement work as expected: b[1,2,1] b[1,2,1,drop=TRUE] b[diag(3)] <- 3
The arity of a spray object: the number of indices needed to retrieve an entry, or the number of columns in the index matrix.
arity(S)
arity(S)
S |
a spray object |
Returns an integer
Robin K. S. Hankin
(a <- rspray()) arity(a)
(a <- rspray()) arity(a)
Coerces spray objects to arrays. Includes off-by-one functionality via
option offbyone
.
## S3 method for class 'spray' as.array(x, offbyone=FALSE, compact=FALSE, ...) ## S3 method for class 'spray' dim(x)
## S3 method for class 'spray' as.array(x, offbyone=FALSE, compact=FALSE, ...) ## S3 method for class 'spray' dim(x)
x |
spray object |
offbyone |
Boolean with default |
compact |
Boolean with default |
... |
Further arguments, currently ignored |
Argument offbyone
defaults to FALSE
; but if it is set to
TRUE
, it effectively adds one from the index matrix, so a zero
entry in the index matrix means the first position in that dimension.
After the subtraction, if performed, the function will not operate if any index is less than 1.
Returns an array of dimension dim(S)
. The “meat” of the
function is
out <- array(0, dS) out[ind] <- coeffs(S)
Robin K. S. Hankin
(M <- matrix(sample(0:4,28,replace=TRUE),ncol=4)) (S <- spray(M,sample(7),addrepeats=TRUE)) as.array(S,offbyone=TRUE) # a large object! sprays are terse S <- spray(matrix(sample(1:4,28,replace=TRUE),ncol=4),sample(7)) A <- as.array(S) # S has no zero indices [if it did, we would need to use offbyone=TRUE] stopifnot(all(S[index(S),drop=TRUE] == A[index(S)]))
(M <- matrix(sample(0:4,28,replace=TRUE),ncol=4)) (S <- spray(M,sample(7),addrepeats=TRUE)) as.array(S,offbyone=TRUE) # a large object! sprays are terse S <- spray(matrix(sample(1:4,28,replace=TRUE),ncol=4),sample(7)) A <- as.array(S) # S has no zero indices [if it did, we would need to use offbyone=TRUE] stopifnot(all(S[index(S),drop=TRUE] == A[index(S)]))
Coerces spray objects to a character string or disord
character vector.
## S3 method for class 'spray' as.character(x, ..., split=FALSE)
## S3 method for class 'spray' as.character(x, ..., split=FALSE)
x |
spray object |
... |
Further arguments, currently ignored |
split |
Boolean with default |
The method uses print_spray_polyform()
and as such is sensitive
to option sprayvars
, but not polyform
.
Robin K. S. Hankin
as.character(rspray()) as.character(rspray(),split=TRUE)
as.character(rspray()) as.character(rspray(),split=TRUE)
Coerce a spray object to a function
## S3 method for class 'spray' as.function(x,...)
## S3 method for class 'spray' as.function(x,...)
x |
spray object, interpreted as a multivariate polynomial |
... |
Further arguments, currently ignored |
Returns a function; this function returns a numeric vector.
Coercion is possible even if some indices are zero or negative. The function is not vectorized in the arity of its argument.
Robin K. S. Hankin
(S <- spray(matrix(1:6,3,2),1:3)) (f <- as.function(S)) f(2:3) == 3*2^3*3^6 + 2*2^2*3^5 + 1*2^1*3^4 # should be TRUE S1 <- spray(matrix(sample(-2:2,replace=TRUE,21),ncol=3),rnorm(7),addrepeats=TRUE) S2 <- spray(matrix(sample(-2:2,replace=TRUE,15),ncol=3),rnorm(5),addrepeats=TRUE) f1 <- as.function(S1) f2 <- as.function(S2) f3 <- as.function(S1*S2) x <- 4:6 f1(x)*f2(x)-f3(x) # should be zero # coercion is vectorized: f1(matrix(1:33,ncol=3))
(S <- spray(matrix(1:6,3,2),1:3)) (f <- as.function(S)) f(2:3) == 3*2^3*3^6 + 2*2^2*3^5 + 1*2^1*3^4 # should be TRUE S1 <- spray(matrix(sample(-2:2,replace=TRUE,21),ncol=3),rnorm(7),addrepeats=TRUE) S2 <- spray(matrix(sample(-2:2,replace=TRUE,15),ncol=3),rnorm(5),addrepeats=TRUE) f1 <- as.function(S1) f2 <- as.function(S2) f3 <- as.function(S1*S2) x <- 4:6 f1(x)*f2(x)-f3(x) # should be zero # coercion is vectorized: f1(matrix(1:33,ncol=3))
Sum over specified dimension margins.
## S3 method for class 'spray' asum(S, dims, drop=TRUE, ...) asum_inverted(S, dims) process_dimensions(S,dims)
## S3 method for class 'spray' asum(S, dims, drop=TRUE, ...) asum_inverted(S, dims) process_dimensions(S,dims)
S |
spray object |
dims |
Vector of strictly positive integers corresponding to dimensions to be summed over |
drop |
Boolean, with default |
... |
Further arguments, currently ignored |
Function asum.spray()
is the method for asum()
. This
takes a spray, and a vector of integers corresponding to dimensions to
be summed over.
Function asum_inverted()
is the same, but takes a vector of
integers corresponding to dimensions not to sum over. This function is
here because there is nice C++
idiom for it.
Function process_dimensions()
ensures that the dims
argument is consistent with the spray S
and returns a cleaned
version thereof.
Returns a spray object.
Robin K. S. Hankin
S <- spray(matrix(sample(0:2,60,replace=TRUE),ncol=3),addrepeats=TRUE) S asum(S,1) asum(S,1:2) asum(S,1:2,drop=FALSE) asum(S,c(1,3)) == asum_inverted(S,2)
S <- spray(matrix(sample(0:2,60,replace=TRUE),ncol=3),addrepeats=TRUE) S asum(S,1) asum(S,1:2) asum(S,1:2,drop=FALSE) asum(S,c(1,3)) == asum_inverted(S,2)
The constant term of a spray object is the coefficient corresponding to an index of all zeros. These functions get or set the constant of a spray object.
is.constant(x) constant(x,drop=FALSE) constant(x) <- value drop(x)
is.constant(x) constant(x,drop=FALSE) constant(x) <- value drop(x)
x |
Object of class spray |
value |
Numeric value to set the constant coefficient to |
drop |
Boolean, with default |
In function constant()
, return the coefficient, or a constant
multivariate polynomial, depending on the value of drop
.
The behaviour of the drop
argument (sort of) matches that of the
spray extractor method. Function drop()
returns the elements of
the coefficients.
Function constant()
ensures that zero spray objects retain the
argument's arity.
It might have been better to call is.constant()
is.scalar()
, for consistency with the stokes
and
clifford
packages. But this is not clear.
Robin K. S. Hankin
(S <- spray(partitions::blockparts(rep(2,3),3,TRUE))) constant(S) constant(S) <- 33 S drop(constant(S,drop=FALSE))
(S <- spray(partitions::blockparts(rep(2,3),3,TRUE))) constant(S) constant(S) <- 33 S drop(constant(S,drop=FALSE))
Partial differentiation of spray objects interpreted as multivariate polynomials
## S3 method for class 'spray' deriv(expr, i , derivative = 1, ...) aderiv(S,orders)
## S3 method for class 'spray' deriv(expr, i , derivative = 1, ...) aderiv(S,orders)
expr |
A spray object, interpreted as a multivariate polynomial |
i |
Dimension to differentiate with respect to |
derivative |
How many times to differentiate |
... |
Further arguments, currently ignored |
S |
spray object |
orders |
The orders of the differentials |
Function deriv.spray()
is the method for generic spray()
;
if S
is a spray object, then spray(S,i,n)
returns
.
Function aderiv()
is the generalized derivative; if S
is a
spray of arity 3, then aderiv(S,c(i,j,k))
returns
.
Both functions return a spray object.
Robin K. S. Hankin
(S <- spray(matrix(sample(-2:2,15,replace=TRUE),ncol=3),addrepeats=TRUE)) deriv(S,1) deriv(S,2,2) # differentiation is invariant under order: aderiv(S,1:3) == deriv(deriv(deriv(S,1,1),2,2),3,3) # Leibniz's rule: S1 <- spray(matrix(sample(0:3,replace=TRUE,21),ncol=3),sample(7),addrepeats=TRUE) S2 <- spray(matrix(sample(0:3,replace=TRUE,15),ncol=3),sample(5),addrepeats=TRUE) S1*deriv(S2,1) + deriv(S1,1)*S2 == deriv(S1*S2,1) # Generalized Leibniz: aderiv(S1*S2,c(1,1,0)) == ( aderiv(S1,c(0,0,0))*aderiv(S2,c(1,1,0)) + aderiv(S1,c(0,1,0))*aderiv(S2,c(1,0,0)) + aderiv(S1,c(1,0,0))*aderiv(S2,c(0,1,0)) + aderiv(S1,c(1,1,0))*aderiv(S2,c(0,0,0)) )
(S <- spray(matrix(sample(-2:2,15,replace=TRUE),ncol=3),addrepeats=TRUE)) deriv(S,1) deriv(S,2,2) # differentiation is invariant under order: aderiv(S,1:3) == deriv(deriv(deriv(S,1,1),2,2),3,3) # Leibniz's rule: S1 <- spray(matrix(sample(0:3,replace=TRUE,21),ncol=3),sample(7),addrepeats=TRUE) S2 <- spray(matrix(sample(0:3,replace=TRUE,15),ncol=3),sample(5),addrepeats=TRUE) S1*deriv(S2,1) + deriv(S1,1)*S2 == deriv(S1*S2,1) # Generalized Leibniz: aderiv(S1*S2,c(1,1,0)) == ( aderiv(S1,c(0,0,0))*aderiv(S2,c(1,1,0)) + aderiv(S1,c(0,1,0))*aderiv(S2,c(1,0,0)) + aderiv(S1,c(1,0,0))*aderiv(S2,c(0,1,0)) + aderiv(S1,c(1,1,0))*aderiv(S2,c(0,0,0)) )
Extract or replace subsets of sprays.
## S3 method for class 'spray' S[..., drop=FALSE] ## S3 replacement method for class 'spray' S[index, ...] <- value
## S3 method for class 'spray' S[..., drop=FALSE] ## S3 replacement method for class 'spray' S[index, ...] <- value
S |
A spray object |
index |
elements to extract or replace |
value |
replacement value |
... |
Further arguments |
drop |
Boolean, with default |
These methods should work as expected, although the off-by-one issue might be a gotcha. disordR discipline is enforced where appropriate.
In S[index,...]
, argument drop
is FALSE
by
default, in which case a spray
object is returned. If
drop
is TRUE
a numeric vector is returned, with elements
corresponding to the rows of index
. Compare coeffs(S)
,
which returns a disord
object; in S[index,drop=TRUE]
,
the rows of index
specify a unique order for the return value.
If a <- spray(diag(3))
, for example, then idiom such as
a[c(1,2,3)]
cannot work, because one would like a[1,2,3]
and a[1:3,2,3]
to work.
If p <- 1:3
, then one might expect idiom such as
S[1,,p,1:3]
to work but this is problematic and a discussion is
given in inst/missing_accessor.txt
.
Functions spray_extract_disord()
and
spray_replace_disord()
are low-level helper functions which
implement idiom such as a[coeffs(a) < 3]
anda[coeffs(a) <
3] <- 99
.
(a <- spray(diag(5))) a[rbind(rep(1,5))] <- 5 a a[3,4,5,3,1] # the NULL polynomial a[0,1,0,0,0] a[0,1,0,0,0,drop=TRUE] a[2,3:5,4,3,3] <- 9 a options(polyform = TRUE) # print as a multivariate polynomial a options(polyform = FALSE) # print in sparse array form a (S1 <- spray(diag(5),1:5)) (S2 <- spray(1-diag(5),11:15)) (S3 <- spray(rbind(c(1,0,0,0,0),c(1,2,1,1,1)))) S1[] <- 3 S1[] <- S2 S1[S3] <- 99 S1 S <- rspray() S[coeffs(S) > 4] S[coeffs(S) < 6] <- 99 S
(a <- spray(diag(5))) a[rbind(rep(1,5))] <- 5 a a[3,4,5,3,1] # the NULL polynomial a[0,1,0,0,0] a[0,1,0,0,0,drop=TRUE] a[2,3:5,4,3,3] <- 9 a options(polyform = TRUE) # print as a multivariate polynomial a options(polyform = FALSE) # print in sparse array form a (S1 <- spray(diag(5),1:5)) (S2 <- spray(1-diag(5),11:15)) (S3 <- spray(rbind(c(1,0,0,0,0),c(1,2,1,1,1)))) S1[] <- 3 S1[] <- S2 S1[S3] <- 99 S1 S <- rspray() S[coeffs(S) > 4] S[coeffs(S) < 6] <- 99 S
Various functions to create simple spray objects such as single-term, homogeneous, and constant multivariate polynomials.
product(power) homog(d,power=1) linear(x,power=1) lone(n,d=n) one(d) as.id(S) xyz(d)
product(power) homog(d,power=1) linear(x,power=1) lone(n,d=n) one(d) as.id(S) xyz(d)
d |
An integer; generally, the dimension or arity of the resulting spray object |
power |
Integer vector of powers |
x |
Numeric vector of coefficients |
S |
A spray object |
n |
In function |
All functions documented here return a spray object
The functions here are related to their equivalents in the multipol package, but are not exactly the same.
Function zero()
is documented at zero.Rd
, but is listed
below for convenience.
Robin K. S. Hankin
product(1:3) # x * y^2 * z^3 homog(3) # x + y + z homog(3,2) # x^2 + xy + xz + y^2 + yz + z^2 linear(1:3) # 1*x + 2*y + 3*z linear(1:3,2) # 1*x^2 + 2*y^2 + 3*z^2 lone(3) # z lone(2,3) # y one(3) # 1 zero(3) # 0 xyz(3) # xyz
product(1:3) # x * y^2 * z^3 homog(3) # x + y + z homog(3,2) # x^2 + xy + xz + y^2 + yz + z^2 linear(1:3) # 1*x + 2*y + 3*z linear(1:3,2) # 1*x^2 + 2*y^2 + 3*z^2 lone(3) # z lone(2,3) # y one(3) # 1 zero(3) # 0 xyz(3) # xyz
Generating function for a chess knight and king on an arbitrarily-dimensioned chessboard
knight(d=2) king(d=2)
knight(d=2) king(d=2)
d |
Dimensionality of the board, defaulting to 2 |
Returns the generating function of the piece in question.
The pieces are forced to move; if they have the option of not moving, add 1 to the returned spray. The vignette contains a short discussion.
Robin K. S. Hankin
knight() # default 2D chess board king() # ditto knight()^2 # generating function for two knight's moves ## How many ways can a knight return to its starting square in 6 moves? constant(knight()^6) ## How many in 6 or fewer? constant((1+knight())^6) ## Where does a randomly-moving knight end up? d <- xyz(2) kt <- (1+knight())*d^2/9 persp(1:25,1:25,as.array(d*kt^6)) ## what is the probability that a 4D king is a knight's move from ## (0,0,0,0) after 6 moves? sum(coeffs(((king(4)/80)^4)[knight(4)]))
knight() # default 2D chess board king() # ditto knight()^2 # generating function for two knight's moves ## How many ways can a knight return to its starting square in 6 moves? constant(knight()^6) ## How many in 6 or fewer? constant((1+knight())^6) ## Where does a randomly-moving knight end up? d <- xyz(2) kt <- (1+knight())*d^2/9 persp(1:25,1:25,as.array(d*kt^6)) ## what is the probability that a 4D king is a knight's move from ## (0,0,0,0) after 6 moves? sum(coeffs(((king(4)/80)^4)[knight(4)]))
spray
objectNumber of nonzero terms in a spray
object
nterms(x) ## S3 method for class 'spray' length(x)
nterms(x) ## S3 method for class 'spray' length(x)
x |
Object of class |
Number of nonzero terms in a spray
object. Function
length()
is defined so that seq_along()
works as
expected
Robin K. S. Hankin
(a <- rspray()) nterms(a) seq_along(a)
(a <- rspray()) nterms(a) seq_along(a)
One-over-one-minus for spray objects; the nearest to ‘division’ that we can get.
ooom(S, n)
ooom(S, n)
S |
object of class spray |
n |
Order of the approximation |
Returns the Taylor expansion to order of
, that
is,
.
Returns a spray object of the same arity as S
.
Uses Horner's method for efficiency
Robin K. S. Hankin
(x <- spray(matrix(1))) ooom(x,5) # 1 + x + x^2 + x^3 + x^4 + x^5 (a <- homog(4,2)) d <- (1-a)*ooom(a,3) constant(d) # should be 1 rowSums(index(d)) # a single 0 and lots of 8s.
(x <- spray(matrix(1))) ooom(x,5) # 1 + x + x^2 + x^3 + x^4 + x^5 (a <- homog(4,2)) d <- (1-a)*ooom(a,3) constant(d) # should be 1 rowSums(index(d)) # a single 0 and lots of 8s.
Allows arithmetic operators to be used for spray calculations, such as addition, multiplication, division, integer powers, etc. Objects of class spray are interpreted as sparse multivariate polynomials.
## S3 method for class 'spray' Ops(e1, e2 = NULL) spray_negative(S) spray_times_spray(S1,S2) spray_times_scalar(S,x) spray_plus_spray(S1,S2) spray_plus_scalar(S,x) spray_power_scalar(S,n) spray_power_scalar_stla(S,n) spray_eq_spray(S1,S2) spray_eq_numeric(S1,x)
## S3 method for class 'spray' Ops(e1, e2 = NULL) spray_negative(S) spray_times_spray(S1,S2) spray_times_scalar(S,x) spray_plus_spray(S1,S2) spray_plus_scalar(S,x) spray_power_scalar(S,n) spray_power_scalar_stla(S,n) spray_eq_spray(S1,S2) spray_eq_numeric(S1,x)
e1 , e2 , S , S1 , S2
|
Objects of class spray, here interpreted as sparse multivariate polynomials |
x |
Real valued scalar |
n |
Non-negative integer |
The function Ops.spray()
passes unary and binary arithmetic
operators (“+
”, “-
”, “*
”,
“/
”,“==
”, and “^
”) to the
appropriate specialist function.
The most interesting operators are “*
” and
“+
” which execute multivariate polynomial multiplication
and addition respectively.
Testing for equality uses spray_eq_spray()
. Note that
spray_eq_spray(S1,S2)
is algebraically equivalent to
is.zero(S1-S2)
, but faster (FALSE
is returned as soon as
a mismatch is found).
Function spray_power_scalar()
is the functional representation
for powers: spray_power_scalar(X,n)
is the same as X^n
.
Function spray_power_scalar_stla()
is an experimental
replacement for spray_power_scalar()
that may offer speed
advantages. It is based on code by Stephane Laurent.
The functions all return spray objects except “==
”, which
returns a logical.
Robin K. S. Hankin
M <- matrix(sample(0:3,21,replace=TRUE),ncol=3) a <- spray(M,sample(7)) b <- homog(3,4) # arithmetic operators mostly work as expected: a + 2*b a - a*b^2/4 a+b S1 <- spray(partitions::compositions(4,3)) S2 <- spray(diag(3)) # S2 = x+y+z stopifnot( (S1+S2)^3 == S1^3 + 3*S1^2*S2 + 3*S1*S2^2 + S2^3 )
M <- matrix(sample(0:3,21,replace=TRUE),ncol=3) a <- spray(M,sample(7)) b <- homog(3,4) # arithmetic operators mostly work as expected: a + 2*b a - a*b^2/4 a+b S1 <- spray(partitions::compositions(4,3)) S2 <- spray(diag(3)) # S2 = x+y+z stopifnot( (S1+S2)^3 == S1^3 + 3*S1^2*S2 + 3*S1*S2^2 + S2^3 )
Parallel (pairwise) maxima and minima for sprays.
maxpair_spray(S1,S2) minpair_spray(S1,S2) ## S3 method for class 'spray' pmax(x, ...) ## S3 method for class 'spray' pmin(x, ...)
maxpair_spray(S1,S2) minpair_spray(S1,S2) ## S3 method for class 'spray' pmax(x, ...) ## S3 method for class 'spray' pmin(x, ...)
x , S1 , S2
|
Spray objects |
... |
spray objects to be compared |
Function maxpair_spray()
finds the pairwise maximum for two
sprays. Specifically, if S3 <- maxpair_spray(S1,S2)
, then
S3[v] == max(S1[v],S2[v])
for every index vector v
.
Function pmax.spray()
is the method for the generic
pmax()
, which takes any number of arguments. If S3 <-
maxpair_spray(S1,S2,...)
, then S3[v] == max(S1[v],S2[v],...)
for
every index vector v
.
Function pmax.spray()
operates right-associatively:
pmax(S1,S2,S3,S4) == f(S1,f(S2,f(S3,S4)))
where f()
is
short for maxpair_spray()
. So if performance is important, put
the smallest spray (in terms of number of nonzero entries) last.
In these functions, a scalar is interpreted as a sort of global maximum.
Thus if S3 <- pmax(S,x)
we have S3[v] == max(S[v],x)
for
every index v
. Observe that this operation is not defined if
x>0
, for then there would be an infinity of v
for which
S3[v] != 0
, an impossibility (or at least counter to the
principles of a sparse array). The frab package discussses
this issue in vignette inst/wittgenstein.Rmd
. Note also that
x
cannot have length as the elements of a spray
object are stored in an arbitrary order, following
disordR
discipline.
Functions minpair_spray()
and pmin.spray()
are analogous.
Note that minpair_spray(S1,S2)
is algebraically equivalent to
-pmax_spray(-S1,-S2)
; see the examples.
The value of pmax(S)
is problematic. Suppose
all(coeffs(S)<0)
; the current implementation returns
pmax(S)==S
but there is a case for returning the null polynomial.
Returns a spray object
Robin K. S. Hankin
S1 <- rspray(100,vals=sample(100)-50) S2 <- rspray(100,vals=sample(100)-50) S3 <- rspray(100,vals=sample(100)-50) # following comparisons should all be TRUE: jj <- pmax(S1,S2,S3) jj == maxpair_spray(S1,maxpair_spray(S2,S3)) jj == maxpair_spray(maxpair_spray(S1,S2),S3) pmax(S1,S2,S3) == -pmin(-S1,-S2,-S3) pmin(S1,S2,S3) == -pmax(-S1,-S2,-S3) pmax(S1,-Inf) == S1 pmin(S1, Inf) == S2 pmax(S1,-3) ## Not run: pmax(S1,3) # not defined ## End(Not run)
S1 <- rspray(100,vals=sample(100)-50) S2 <- rspray(100,vals=sample(100)-50) S3 <- rspray(100,vals=sample(100)-50) # following comparisons should all be TRUE: jj <- pmax(S1,S2,S3) jj == maxpair_spray(S1,maxpair_spray(S2,S3)) jj == maxpair_spray(maxpair_spray(S1,S2),S3) pmax(S1,S2,S3) == -pmin(-S1,-S2,-S3) pmin(S1,S2,S3) == -pmax(-S1,-S2,-S3) pmax(S1,-Inf) == S1 pmin(S1, Inf) == S2 pmax(S1,-3) ## Not run: pmax(S1,3) # not defined ## End(Not run)
Print methods for spray objects with options for printing in matrix form or multivariate polynomial form
## S3 method for class 'spray' print(x, ...) print_spray_matrixform(S) print_spray_polyform(S,give=FALSE) printedvalue(v)
## S3 method for class 'spray' print(x, ...) print_spray_matrixform(S) print_spray_polyform(S,give=FALSE) printedvalue(v)
x , S
|
spray object |
give |
Boolean, with default |
v |
Numeric vector |
... |
Further arguments (currently ignored) |
The print method, print.spray()
, dispatches to helper functions
print_spray_matrixform()
and print_spray_polyform()
depending on the value of option polyform
; see the examples
section.
Option sprayvars
is a character vector with entries
corresponding to the variable names for printing. The sprayvars
option has no algebraic significance: all it does is affect the print
method.
Function printedvalue()
is a low-level helper function that
takes a numeric argument and returns the value as printed (thus
respecting options scipen
and digits
). It uses
gsub()
to remove the “[1]
” produced by
capture.output()
. The code is not perfect and sometimes fails
(for reasons that are not clear to me) when applied to large objects on
the Rstudio console.
Note that printing a spray object (in either matrix form or polynomial form) generally takes much longer than calculating it.
Returns its argument invisibly.
There are a couple of hard-wired symbols for multiplication and equality which are defined near the top of the helper functions.
There are no checks for option sprayvars
being sensible. For
example, repeated entries, or entries with zero length, are acceptable
but the output might be confusing or uninformative.
Robin K. S. Hankin
(a <- spray(diag(3))) options(polyform = FALSE) a^3 options(polyform = TRUE) a^3 options(sprayvars=letters) a <- diag(26) spray(a) ## Following example from mpoly: a[1 + cbind(0:25, 1:26) %% 26] <- 2 spray(a)
(a <- spray(diag(3))) options(polyform = FALSE) a^3 options(polyform = TRUE) a^3 options(sprayvars=letters) a <- diag(26) spray(a) ## Following example from mpoly: a[1 + cbind(0:25, 1:26) %% 26] <- 2 spray(a)
Creates random spray objects as quick-and-dirty examples of multivariate polynomials
rspray(n=9 , vals = seq_len(n), arity = 3, powers = 0:2) rsprayy(n=30, vals = seq_len(n), arity = 7, powers = 0:8)
rspray(n=9 , vals = seq_len(n), arity = 3, powers = 0:2) rsprayy(n=30, vals = seq_len(n), arity = 7, powers = 0:8)
n |
Number of distinct rows (maximum); repeated rows are merged
(argument |
vals |
Values to use for coefficients |
arity |
Arity of the spray; the number of columns in the index matrix |
powers |
Set from which to sample the entries of the index matrix |
Returns a spray object
If the index matrix contains repeated rows, the returned spray object
will contain fewer than n
entries
Robin K. S. Hankin
rspray() rspray(4)*rspray(3,rnorm(3)) rspray(3,arity=7,powers=-2:2)^3 rspray(1000,vals=rnorm(1000))
rspray() rspray(4)*rspray(3,rnorm(3)) rspray(3,arity=7,powers=-2:2)^3 rspray(1000,vals=rnorm(1000))
spray
objects
Create, coerce, and test for sparse array objects
spray(M, x, addrepeats=FALSE) spraymaker(L, addrepeats=FALSE, arity=ncol(L[[1]])) is.spray(S) as.spray(arg1, arg2, addrepeats=FALSE, offbyone=FALSE) index(S) coeffs(S) coeffs(S) <- value is_valid_spray(L)
spray(M, x, addrepeats=FALSE) spraymaker(L, addrepeats=FALSE, arity=ncol(L[[1]])) is.spray(S) as.spray(arg1, arg2, addrepeats=FALSE, offbyone=FALSE) index(S) coeffs(S) coeffs(S) <- value is_valid_spray(L)
M |
Integer matrix with rows corresponding to index positions |
x |
Numeric value with elements corresponding to spray entries |
S |
Object to be tested for being a spray |
L |
A list, nominally of two elements (index matrix and value) which is to be tested for acceptability to be coerce to class spray |
arg1 , arg2
|
Various arguments to be coerced to a spray |
addrepeats |
Boolean, with default |
value |
In the assignment operator |
offbyone |
In function |
arity |
In function |
Spray objects are sparse arrays interpreted as multivariate
polynomials. They can be added and subtracted; “*
” is
interpreted as polynomial multiplication.
To create a spray object the user should use spray()
, if a
matrix of indices and vector of values is available, or
as.spray()
which tries hard to do the Right Thing (tm).
Function spraymaker()
is the formal creator function, and it is
written to take the output of the C++ routines and return a
spray object. The reason this needs an arity
argument is that
C++ sometimes returns NULL
(in lieu of a zero-row
matrix, which it cannot deal with). In this case, we need some way to
tell R the arity of the corresponding spray object.
Rownames and colnames of the index matrix are removed by
spraymaker()
[C++ routine spray_maker()
discards the dimnames
attribute of matrix M
], but the
print method might add colnames to printed output, via option
sprayvars
.
Functions index()
and coeffs()
are accessor methods.
Function index()
returns an integer-valued matrix with rows
corresponding to variable powers.
There is an extensive vignette available; type
vignette("spray")
at the command line.
Function coeffs()
was formerly known as value()
.
Technically, index()
breaks disordR
discipline.
Robin K. S. Hankin
S <- spray(diag(5)) # missing second argument interpreted as '1'. as.array(S,offbyone=TRUE) # zero indices interpreted as ones. M <- matrix(1:5,6,5) # note first row matches the sixth row ## Not run: spray(M,1:6) # will not work because addrepeats is not TRUE spray(M,1:6,addrepeats=TRUE) # 7=1:6 S <- spray(matrix(1:7,5,7)) a <- as.array(S) # will not work if any(M<1) S1 <- as.spray(a) stopifnot(S==S1) a <- rspray(20) coeffs(a)[coeffs(a) %% 2 == 1] <- 99 # every odd coefficient -> 99
S <- spray(diag(5)) # missing second argument interpreted as '1'. as.array(S,offbyone=TRUE) # zero indices interpreted as ones. M <- matrix(1:5,6,5) # note first row matches the sixth row ## Not run: spray(M,1:6) # will not work because addrepeats is not TRUE spray(M,1:6,addrepeats=TRUE) # 7=1:6 S <- spray(matrix(1:7,5,7)) a <- as.array(S) # will not work if any(M<1) S1 <- as.spray(a) stopifnot(S==S1) a <- rspray(20) coeffs(a)[coeffs(a) %% 2 == 1] <- 99 # every odd coefficient -> 99
Low-level functions that call C++ source code, as detailed in the
automatically generated RcppExports.R
file.
spray_maker(M, d) spray_add(M1, d1, M2, d2) spray_mult(M1, d1, M2, d2) spray_overwrite(M1, d1, M2, d2) spray_accessor(M, d, Mindex) spray_setter(M1, d1, M2, d2) spray_equality(M1, d1, M2, d2) spray_asum_include(M,d,n) spray_asum_exclude(M,d,n) spray_deriv(M,d,n) spray_pmax(M1,d1,M2,d2) spray_pmin(M1,d1,M2,d2) spray_power(M,d,pow) spray_spray_accessor() spray_spray_add() spray_spray_asum_exclude() spray_spray_asum_include() spray_spray_deriv() spray_spray_equality() spray_spray_maker() spray_spray_mult() spray_spray_overwrite() spray_spray_pmax() spray_spray_pmin() spray_spray_setter() spray_spray_power()
spray_maker(M, d) spray_add(M1, d1, M2, d2) spray_mult(M1, d1, M2, d2) spray_overwrite(M1, d1, M2, d2) spray_accessor(M, d, Mindex) spray_setter(M1, d1, M2, d2) spray_equality(M1, d1, M2, d2) spray_asum_include(M,d,n) spray_asum_exclude(M,d,n) spray_deriv(M,d,n) spray_pmax(M1,d1,M2,d2) spray_pmin(M1,d1,M2,d2) spray_power(M,d,pow) spray_spray_accessor() spray_spray_add() spray_spray_asum_exclude() spray_spray_asum_include() spray_spray_deriv() spray_spray_equality() spray_spray_maker() spray_spray_mult() spray_spray_overwrite() spray_spray_pmax() spray_spray_pmin() spray_spray_setter() spray_spray_power()
M , M1 , M2 , Mindex
|
Integer valued matrices with rows corresponding to array indices |
d , d1 , d2
|
Vector of values corresponding to nonzero array entries |
n |
Integer vector corresponding to dimensions to sum over for the sum functions |
pow |
Nonnegative integer for |
These functions return a two-element list which is coerced to
an object of class spray
by function spraymaker()
.
These functions aren't really designed for the end-user.
Function spray_equality()
cannot simply check for equality of
$value
because the order of the index rows is not specified in
a spray object. Function spray_crush()
has been removed as it
is redundant.
Robin K. S. Hankin
Discussion about the difficulties of implementing idiom like
S[1,,5,,]
in the package
spray_missing_accessor(S, dots)
spray_missing_accessor(S, dots)
S |
Object of class spray |
dots |
further arguments |
File inst/missing_accessor.txt
presents an extended discussion
of the difficulties of implementing idiom like S[1,,5,,]
in the
package.
Robin K. S. Hankin
The formal S4 class for sprays.
Objects can be created by calls of the form new("spray",
...)
but this is not encouraged. Use functions spray()
or
as.spray()
instead.
index
:Index matrix
value
:Numeric vector holding coefficients
Robin K. S. Hankin
Provides a natural cross product for spray objects, useful for tensors
and -forms
spraycross(S, ...) spraycross2(S1,S2)
spraycross(S, ...) spraycross2(S1,S2)
S , S1 , S2 , ...
|
spray objects |
Tensor products for sprays. This is not an algebraic product of sprays interpreted as multivariate polynomials. The function is used in the stokes package.
Function spraycross2()
is a helper function that takes exactly
two arguments. Function spraycross()
is a more general function
that takes any number of arguments.
Returns a spray object
Robin K. S. Hankin
a <- spray(matrix(1:4,2,2),c(2,5)) b <- spray(matrix(c(10,11,12,13),2,2),c(7,11)) a b spraycross2(a,b) spraycross2(b,a) spraycross(a,b,b)
a <- spray(matrix(1:4,2,2),c(2,5)) b <- spray(matrix(c(10,11,12,13),2,2),c(7,11)) a b spraycross2(a,b) spraycross2(b,a) spraycross(a,b,b)
Substitute values into a spray object, interpreted as a multivariate polynomial
subs(S, dims, x, drop=TRUE)
subs(S, dims, x, drop=TRUE)
S |
spray object |
dims |
Integer or logical vector with entries corresponding to the dimensions to be substituted |
x |
Numeric vector of values to be substituted |
drop |
Boolean, with default |
It is much easier if argument dims
is sorted into increasing
order. If not, caveat emptor!
Robin K. S. Hankin
(S <- spray(matrix(sample(0:3,60,replace=TRUE),nrow=12))) subs(S,c(2,5),1:2) P <- homog(3,3) subs(P,1,2)
(S <- spray(matrix(sample(0:3,60,replace=TRUE),nrow=12))) subs(S,c(2,5),1:2) P <- homog(3,3) subs(P,1,2)
A summary method for spray objects, and a print method for summaries.
## S3 method for class 'spray' summary(object, ...) ## S3 method for class 'summary.spray' print(x, ...)
## S3 method for class 'spray' summary(object, ...) ## S3 method for class 'summary.spray' print(x, ...)
object , x
|
Object of class |
... |
Further arguments, passed to |
A summary.spray
object is summary of a spray
object
x
: a list with first element being a summary()
of the
coefficients (which is a disord
object), and the second being a
spray
object comprising a few selected index-coefficient
pairs. The selection is done by head()
.
The “representative selection” is impementation-specific, as it
uses disordR::elements()
to extract rows of the index matrix
and coefficients.
Robin K. S. Hankin
a <- rspray()^2 a summary(a) summary(a,2) options(polyform=TRUE) summary(a^4,3) options(polyform=FALSE) # restore default
a <- rspray()^2 a summary(a) summary(a,2) options(polyform=TRUE) summary(a^4,3) options(polyform=FALSE) # restore default
Generic version of zapsmall()
zap(x, digits = getOption("digits")) ## S4 method for signature 'spray' zapsmall(x, digits = getOption("digits"))
zap(x, digits = getOption("digits")) ## S4 method for signature 'spray' zapsmall(x, digits = getOption("digits"))
x |
spray object |
digits |
number of digits to retain |
Given a spray object, coefficients close to zero are ‘zapped’,
i.e., replaced by ‘0’, using base::zapsmall()
. Function
zap()
is an easily-typed alias; zapsmall()
is the
S4
generic.
Note, zap()
actually changes the numeric value, it is not just
a print method.
Robin K. S. Hankin
(S <- spray(matrix(sample(1:50),ncol=2),10^-(1:25))) zap(S) S-zap(S) # print method will probably print zeros... coeffs(S-zap(S)) # ...but they are nevertheless nonzero
(S <- spray(matrix(sample(1:50),ncol=2),10^-(1:25))) zap(S) S-zap(S) # print method will probably print zeros... coeffs(S-zap(S)) # ...but they are nevertheless nonzero
Test for the zero, or empty, polynomial
zero(d) is.zero(x) is.empty(L)
zero(d) is.zero(x) is.empty(L)
L , x
|
A two-element list of indices and values, possibly a spray object or numeric vector |
d |
Integer specifying dimensionality of the spray (the arity) |
Functions is.empty()
and is.zero()
are synonyms. If spray
objects are interpreted as multivariate polynomials,
“is.zero()
” is more intuitive, if sprays are interpreted
as sparse arrays, “is.empty()
” is better (for me).
Passing a zero-row index matrix can have unexpected effects:
> dput(spray(matrix(0,0,5),9)) structure(list(structure(numeric(0), .Dim = c(0L, 5L)), numeric(0)), class = "spray")
Above, the index matrix has zero rows (and no elements) but the fact
that it has five columns is retained. Arguably spray()
should
return an error here, as the number of rows of the index matrix should
match the length of the coefficient vector and they do not: the index
has zero rows and the coefficient vector has length 1 (although they
match in the returned value). The returned spray
object has no
coefficients [specifically, numeric(0)
]; this is consistent with
the index matrix having zero rows.
Zero coefficients are discarded by the back end:
> spray(matrix(1,1,5),0) empty sparse array with 5 columns > dput(spray(matrix(1,1,5),0)) structure(list(structure(numeric(0), dim = c(0L, 5L)), numeric(0)), class = "spray")
Above, the index matrix given to spray()
has one row but the
coefficient is a length-one vector with element zero. The resulting
spray
object has a NULL
index matrix [because rows with
zero coefficients are removed] and a NULL
coefficient. It is
also permissible to pass a a zero-row matrix:
spray(matrix(0,0,5),0) empty sparse array with 5 columns dput(spray(matrix(0,0,5),0)) structure(list(structure(numeric(0), dim = c(0L, 5L)), numeric(0)), class = "spray")
In previous versions of the package, the index matrix in the returned
spray
object could be NULL
under some circumstances. If
so, the arity of the spray object is lost. It is probably worth noting
that spray()
, given a zero-row index matrix, loses a length one
coefficients vector, but complains about a length-two coefficient
vector:
> dput(spray(matrix(0,0,5),0)) structure(list(structure(numeric(0), dim = c(0L, 5L)), numeric(0)), class = "spray") > dput(spray(matrix(0,0,5),3)) structure(list(structure(numeric(0), dim = c(0L, 5L)), numeric(0)), class = "spray") > dput(spray(matrix(0,0,5),1:2)) Error in is_valid_spray(L) : nrow(L[[1]]) == length(L[[2]]) is not TRUE > > identical(spray(matrix(0,0,5),0),spray(matrix(0,0,5),3)) [1] TRUE
(a <- lone(1,3)) is.zero(a-a) # should be TRUE is.zero(zero(6)) x <- spray(t(0:1)) y <- spray(t(1:0)) is.zero((x+y)*(x-y)-(x^2-y^2)) # TRUE
(a <- lone(1,3)) is.zero(a-a) # should be TRUE is.zero(zero(6)) x <- spray(t(0:1)) y <- spray(t(1:0)) is.zero((x+y)*(x-y)-(x^2-y^2)) # TRUE