Allow transposing of sparse sub-matrices whose first row is > 0

PiperOrigin-RevId: 797728559
Change-Id: I3c825118dae198333d357d2e4f248d00b0f5d8ce
This commit is contained in:
Yuval Tassa
2025-08-21 05:55:43 -07:00
committed by Copybara-Service
parent c7a82c32dc
commit 174a50442d
2 changed files with 102 additions and 2 deletions
+7 -2
View File
@@ -535,12 +535,17 @@ int mju_compressSparse(mjtNum* mat, int nr, int nc, int* rownnz, int* rowadr, in
void mju_transposeSparse(mjtNum* res, const mjtNum* mat, int nr, int nc,
int* res_rownnz, int* res_rowadr, int* res_colind, int* res_rowsuper,
const int* rownnz, const int* rowadr, const int* colind) {
if (!nr || !nc) return;
// clear number of non-zeros for each row of transposed
mju_zeroInt(res_rownnz, nc);
// handle the case where the first row of mat is nonzero (offset wrt the base pointers)
int row_offset = rowadr[0];
// count the number of non-zeros for each row of the transposed matrix
for (int r = 0; r < nr; r++) {
int start = rowadr[r];
int start = rowadr[r] - row_offset;
int end = start + rownnz[r];
for (int j = start; j < end; j++) {
res_rownnz[colind[j]]++;
@@ -564,7 +569,7 @@ void mju_transposeSparse(mjtNum* res, const mjtNum* mat, int nr, int nc,
// iterate through each row (column) of mat (res)
for (int r = 0; r < nr; r++) {
int c_prev = -1;
int start = rowadr[r];
int start = rowadr[r] - row_offset;
int end = start + rownnz[r];
for (int i = start; i < end; i++) {
// swap rows with columns and increment res_rowadr
+95
View File
@@ -1446,6 +1446,101 @@ TEST_F(EngineUtilSparseTest, BlockDiagSparse) {
7, 0, 0, 0, 0));
}
TEST_F(EngineUtilSparseTest, BlockDiagSparseTranspose) {
// 4x5 matrix with 3 blocks
constexpr int nr = 4;
constexpr int nc = 5;
const mjtNum mat[nr * nc] = {
1, 2, 0, 0, 0,
0, 0, 3, 4, 0,
0, 0, 5, 6, 0,
0, 0, 0, 0, 7
};
constexpr int nnz = 7;
// block structure
constexpr int nb = 3;
const int block_nr[nb] = {1, 2, 1};
const int block_nc[nb] = {2, 2, 1};
const int block_r[nb] = {0, 1, 3};
const int block_c[nb] = {0, 2, 4};
// convert to sparse
int rownnz[nr];
int rowadr[nr];
int colind[nnz];
mjtNum mat_sparse[nnz];
mju_dense2sparse(mat_sparse, mat, nr, nc, rownnz, rowadr, colind, nnz);
// block diagonalize
const int perm_r[nr] = {0, 1, 2, 3};
const int perm_c[nc] = {0, 1, 2, 3, 4};
int res_rownnz[nr];
int res_rowadr[nr];
int res_colind[nnz];
mjtNum res[nnz];
mju_blockDiagSparse(res, res_rownnz, res_rowadr, res_colind,
mat_sparse, rownnz, rowadr, colind, nr, nb,
perm_r, perm_c,
block_r, block_c, nullptr, nullptr);
// transpose each block
mjtNum matT[nnz];
int colindT[nnz];
int rownnzT[nc]; // Max possible size for rownnzT is nc
int rowadrT[nc];
mjtNum denseT[nr * nc];
mjtNum dense_block[nr * nc];
mjtNum dense_block_T[nr * nc];
for (int b = 0; b < nb; ++b) {
int bnr = block_nr[b];
int bnc = block_nc[b];
int r_offset = block_r[b];
int c_offset = block_c[b];
if (bnr == 0 || bnc == 0) continue;
int block_start_adr = res_rowadr[r_offset];
// pointers to the start of the current block
mjtNum* block_res_vals = res + block_start_adr;
int* block_res_rownnz = res_rownnz + r_offset;
int* block_res_rowadr = res_rowadr + r_offset;
int* block_res_colind = res_colind + block_start_adr;
mju_transposeSparse(
matT, block_res_vals, bnr, bnc,
rownnzT, rowadrT, colindT, nullptr,
block_res_rownnz, block_res_rowadr, block_res_colind);
// verification:
// 1. convert transposed sparse block to dense
mju_zero(denseT, bnc * bnr);
mju_sparse2dense(denseT, matT, bnc, bnr, rownnzT, rowadrT, colindT);
// 2. extract original block to dense
for (int i = 0; i < bnr; ++i) {
for (int j = 0; j < bnc; ++j) {
dense_block[i * bnc + j] = mat[(r_offset + i) * nc + (c_offset + j)];
}
}
// 3. manually transpose the original dense block
for (int i = 0; i < bnr; ++i) {
for (int j = 0; j < bnc; ++j) {
dense_block_T[j * bnr + i] = dense_block[i * bnc + j];
}
}
// 4. Compare
for (int i = 0; i < bnc * bnr; ++i) {
EXPECT_EQ(denseT[i], dense_block_T[i])
<< "block " << b << " element " << i;
}
}
}
TEST_F(EngineUtilSparseTest, PermuteMat) {
const mjtNum mat[] = {1, 2, 0, 0,
0, 0, 3, 4,