You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I'm preparing the docs for the sum-to-zero matrix and it's essentially a nested version of the 1-d sum-to-zero constraint. I thought that there must be an easier way to generalize everything to higher order dimensions. In fact there is and it's even better than I originally thought. We can re-use the 1-d constraint for everything.
I did write this up in cpp that you can see in Godbolt (here) but I find the R code to be easier to understand.
# Row-major strides & flat-index helpersmake_strides<-function(dims) {
D<- length(dims)
stride<- integer(D)
stride[D] <-1for (din (D-1):1) stride[d] <-stride[d+1] *dims[d+1]
stride
}
flat_index<-function(idx0, stride) {
## idx0 is *zero-based*; add 1 for R’s one-based vectors
sum(idx0*stride) +1L
}
# General N-D zero-sum expansionzero_sum_nd<-function(x, type= c("helmert", "householder"), dims=NULL) {
Z<-xcur<-dims# current extentstype<- match.arg(type)
for (axin seq_along(dims)) {
next_dims<-curnext_dims[ax] <-next_dims[ax] +1LZ_next<-numeric(prod(next_dims))
stride_in<- make_strides(cur)
stride_out<- make_strides(next_dims)
## loop over every index-tuple except the current axislead_total<- prod(cur[-ax])
lead_idx<- integer(length(cur))
for (flatin0:(lead_total-1L)) {
# decode flat -> lead_idxrem<-flatfor (din rev(setdiff(seq_along(cur), ax))) {
lead_idx[d] <-rem%%cur[d]
rem<-rem%/%cur[d]
}
# pull the 1-D sliceslice<-numeric(cur[ax])
for (kin0:(cur[ax] -1L)) {
lead_idx[ax] <-kslice[k+1L] <-Z[flat_index(lead_idx, stride_in)]
}
# any orthgonal type zero-sum tranform works here!z<- zero_sum_helmert(slice)
# write backfor (kin0:(cur[ax])) {
lead_idx[ax] <-kZ_next[flat_index(lead_idx, stride_out)] <-z[k+1L]
}
}
Z<-Z_nextcur<-next_dims
}
Z
}
This works because the constraint is a projection onto the 1-dimensional span of a row vector of 1s, is square, and orthogonal. Applying the constraint sequentially over each tensor slice (often called a fiber) works because of the orthonormal property of the transform we are using.
We can actually confirm that this is the result of the orthogonal transform by running basis vectors over each dimension through the transform and then seeing if the resultant basis matrix is orthogonal.
make_basis<-function(input_dims, fun, type=NULL) {
dim_prod<- prod(input_dims)
out_size<- prod(input_dims+1)
B<-matrix(0, out_size, dim_prod) # each column will be one basis vectortype<- match.arg(type, choices= c("helmert", "householder"))
if (as.character(substitute(fun)) =="zero_sum_nd") {
dims<-input_dims
} else {
dims<-NULL
}
for (jin1:dim_prod) { # feed unit vectors through the loope<-numeric(dim_prod)
e[j] <-1X<-array(e, dim=input_dims)
Z<- fun(X, type, dims)
B[, j] <- as.vector(Z)
}
B
}
B_tensor<- make_basis(c(3, 1), fun=zero_sum_nd, type="helmert")
# this should be identity if orthogonal basis
zapsmall(crossprod(B_tensor))
Proposal
Since we do not have any N-d array constraint types I think it would be easiest to initially include this as part of the constrain transform functions (forthcoming). The user would specify an N-d array where the size of each dimension is M - 1 (where M itself is an N-d array) in the parameters block and feed this to the constraint transform which returns an N-d array of length M.
parameters {
array[4, 5, 6, 7] real y;
}
transformed parameters {
array[5, 6, 7, 8] real z = zero_sum_array_constraint(y);
}
It's annoying to type out the array dimensions but I'm not sure if we can feed an array in for the dimension sizes or have some syntactic sugar like
array[sizes = my_array_sizes] real y;
The text was updated successfully, but these errors were encountered:
I'm preparing the docs for the sum-to-zero matrix and it's essentially a nested version of the 1-d sum-to-zero constraint. I thought that there must be an easier way to generalize everything to higher order dimensions. In fact there is and it's even better than I originally thought. We can re-use the 1-d constraint for everything.
I did write this up in cpp that you can see in Godbolt (here) but I find the R code to be easier to understand.
N-d zero-sum background
Our 1-d sum-to-zero is
To do an N-d the code is
This works because the constraint is a projection onto the 1-dimensional span of a row vector of 1s, is square, and orthogonal. Applying the constraint sequentially over each tensor slice (often called a fiber) works because of the orthonormal property of the transform we are using.
We can actually confirm that this is the result of the orthogonal transform by running basis vectors over each dimension through the transform and then seeing if the resultant basis matrix is orthogonal.
Proposal
Since we do not have any N-d array constraint types I think it would be easiest to initially include this as part of the constrain transform functions (forthcoming). The user would specify an N-d array where the size of each dimension is M - 1 (where M itself is an N-d array) in the parameters block and feed this to the constraint transform which returns an N-d array of length M.
It's annoying to type out the array dimensions but I'm not sure if we can feed an array in for the dimension sizes or have some syntactic sugar like
The text was updated successfully, but these errors were encountered: