Skip to content

Commit 7c84b13

Browse files
authored
COO support (#47)
* basic coo support * run formatter only on macos * ran formatter * added comment * formatter * updated version number
1 parent 5316598 commit 7c84b13

8 files changed

Lines changed: 293 additions & 7 deletions

File tree

.github/workflows/formatting.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ jobs:
1111
runs-on: ${{ matrix.os }}
1212
strategy:
1313
matrix:
14-
os: [ubuntu-latest]
14+
os: [macos-latest]
1515

1616
steps:
1717
- uses: actions/checkout@v5

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ set(CMAKE_C_STANDARD 99)
44

55
set(DIFF_ENGINE_VERSION_MAJOR 0)
66
set(DIFF_ENGINE_VERSION_MINOR 1)
7-
set(DIFF_ENGINE_VERSION_PATCH 2)
7+
set(DIFF_ENGINE_VERSION_PATCH 3)
88
set(DIFF_ENGINE_VERSION "${DIFF_ENGINE_VERSION_MAJOR}.${DIFF_ENGINE_VERSION_MINOR}.${DIFF_ENGINE_VERSION_PATCH}")
99
add_compile_definitions(DIFF_ENGINE_VERSION="${DIFF_ENGINE_VERSION}")
1010

include/problem.h

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#define PROBLEM_H
2020

2121
#include "expr.h"
22+
#include "utils/COO_Matrix.h"
2223
#include "utils/CSR_Matrix.h"
2324
#include "utils/Timer.h"
2425
#include <stdbool.h>
@@ -45,21 +46,22 @@ typedef struct problem
4546
int n_vars;
4647
int total_constraint_size;
4748

48-
/* Allocated by new_problem */
49+
/* allocated by new_problem */
4950
double *constraint_values;
5051
double *gradient_values;
5152

52-
/* Allocated by problem_init_derivatives */
53+
/* allocated by problem_init_derivatives */
5354
CSR_Matrix *jacobian;
5455
CSR_Matrix *lagrange_hessian;
55-
int *hess_idx_map; /* Maps all wsum_hess nnz to lagrange_hessian (obj +
56-
constraints) */
56+
int *hess_idx_map; /* maps all wsum_hess nnz to lagrange_hessian */
57+
COO_Matrix *jacobian_coo;
58+
COO_Matrix *lagrange_hessian_coo; /* lower triangular part stored in COO */
5759

5860
/* for the affine shortcut we keep track of the first time the jacobian and
5961
* hessian are called */
6062
bool jacobian_called;
6163

62-
/* Statistics for performance measurement */
64+
/* statistics for performance measurement */
6365
Diff_engine_stats stats;
6466
bool verbose;
6567
} problem;
@@ -70,6 +72,8 @@ problem *new_problem(expr *objective, expr **constraints, int n_constraints,
7072
void problem_init_jacobian(problem *prob);
7173
void problem_init_hessian(problem *prob);
7274
void problem_init_derivatives(problem *prob);
75+
void problem_init_jacobian_coo(problem *prob);
76+
void problem_init_hessian_coo_lower_triangular(problem *prob);
7377
void free_problem(problem *prob);
7478

7579
double problem_objective_forward(problem *prob, const double *u);

include/utils/COO_Matrix.h

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
#ifndef COO_MATRIX_H
2+
#define COO_MATRIX_H
3+
4+
#include "CSR_Matrix.h"
5+
6+
/* COO (Coordinate) Sparse Matrix Format
7+
*
8+
* For an m x n matrix with nnz nonzeros:
9+
* - rows: array of size nnz containing row indices
10+
* - cols: array of size nnz containing column indices
11+
* - x: array of size nnz containing values
12+
* - value_map: array of size nnz mapping CSR entries to COO entries (for
13+
* lower-triangular COO)
14+
* - m: number of rows
15+
* - n: number of columns
16+
* - nnz: number of nonzero entries
17+
*/
18+
typedef struct COO_Matrix
19+
{
20+
int *rows;
21+
int *cols;
22+
double *x;
23+
int *value_map;
24+
int m;
25+
int n;
26+
int nnz;
27+
} COO_Matrix;
28+
29+
/* Construct a COO matrix from a CSR matrix */
30+
COO_Matrix *new_coo_matrix(const CSR_Matrix *A);
31+
32+
/* Construct a COO matrix containing only the lower-triangular
33+
* entries (col <= row) of a symmetric CSR matrix. Populates
34+
* value_map so that refresh_lower_triangular_coo can update
35+
* values without recomputing structure. */
36+
COO_Matrix *new_coo_matrix_lower_triangular(const CSR_Matrix *A);
37+
38+
/* Refresh COO values from a new CSR value array using value_map */
39+
void refresh_lower_triangular_coo(COO_Matrix *coo, const double *vals);
40+
41+
void free_coo_matrix(COO_Matrix *matrix);
42+
43+
#endif /* COO_MATRIX_H */

src/problem.c

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,27 @@ void problem_init_hessian(problem *prob)
237237
prob->stats.time_init_derivatives += GET_ELAPSED_SECONDS(timer);
238238
}
239239

240+
void problem_init_jacobian_coo(problem *prob)
241+
{
242+
problem_init_jacobian(prob);
243+
Timer timer;
244+
clock_gettime(CLOCK_MONOTONIC, &timer.start);
245+
prob->jacobian_coo = new_coo_matrix(prob->jacobian);
246+
clock_gettime(CLOCK_MONOTONIC, &timer.end);
247+
prob->stats.time_init_derivatives += GET_ELAPSED_SECONDS(timer);
248+
}
249+
250+
void problem_init_hessian_coo_lower_triangular(problem *prob)
251+
{
252+
problem_init_hessian(prob);
253+
Timer timer;
254+
clock_gettime(CLOCK_MONOTONIC, &timer.start);
255+
prob->lagrange_hessian_coo =
256+
new_coo_matrix_lower_triangular(prob->lagrange_hessian);
257+
clock_gettime(CLOCK_MONOTONIC, &timer.end);
258+
prob->stats.time_init_derivatives += GET_ELAPSED_SECONDS(timer);
259+
}
260+
240261
void problem_init_derivatives(problem *prob)
241262
{
242263
problem_init_jacobian(prob);
@@ -286,6 +307,7 @@ void free_problem(problem *prob)
286307
free(prob->gradient_values);
287308
free_csr_matrix(prob->jacobian);
288309
free_csr_matrix(prob->lagrange_hessian);
310+
free_coo_matrix(prob->jacobian_coo);
289311
free(prob->hess_idx_map);
290312

291313
/* Release expression references (decrements refcount) */

src/utils/COO_Matrix.c

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
/*
2+
* Copyright 2026 Daniel Cederberg and William Zhang
3+
*
4+
* This file is part of the DNLP-differentiation-engine project.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
#include "utils/COO_Matrix.h"
19+
#include <stdlib.h>
20+
#include <string.h>
21+
22+
COO_Matrix *new_coo_matrix(const CSR_Matrix *A)
23+
{
24+
COO_Matrix *coo = (COO_Matrix *) malloc(sizeof(COO_Matrix));
25+
coo->m = A->m;
26+
coo->n = A->n;
27+
coo->nnz = A->nnz;
28+
coo->rows = (int *) malloc(A->nnz * sizeof(int));
29+
coo->cols = (int *) malloc(A->nnz * sizeof(int));
30+
coo->x = (double *) malloc(A->nnz * sizeof(double));
31+
coo->value_map = NULL;
32+
33+
for (int r = 0; r < A->m; r++)
34+
{
35+
for (int k = A->p[r]; k < A->p[r + 1]; k++)
36+
{
37+
coo->rows[k] = r;
38+
}
39+
}
40+
41+
memcpy(coo->cols, A->i, A->nnz * sizeof(int));
42+
memcpy(coo->x, A->x, A->nnz * sizeof(double));
43+
44+
return coo;
45+
}
46+
47+
COO_Matrix *new_coo_matrix_lower_triangular(const CSR_Matrix *A)
48+
{
49+
/* Pass 1: count lower-triangular entries (col <= row) */
50+
int count = 0;
51+
for (int r = 0; r < A->m; r++)
52+
{
53+
for (int k = A->p[r]; k < A->p[r + 1]; k++)
54+
{
55+
if (A->i[k] <= r)
56+
{
57+
count++;
58+
}
59+
}
60+
}
61+
62+
COO_Matrix *coo = (COO_Matrix *) malloc(sizeof(COO_Matrix));
63+
coo->m = A->m;
64+
coo->n = A->n;
65+
coo->nnz = count;
66+
coo->rows = (int *) malloc(count * sizeof(int));
67+
coo->cols = (int *) malloc(count * sizeof(int));
68+
coo->x = (double *) malloc(count * sizeof(double));
69+
coo->value_map = (int *) malloc(count * sizeof(int));
70+
71+
/* Pass 2: fill arrays */
72+
int idx = 0;
73+
for (int r = 0; r < A->m; r++)
74+
{
75+
for (int k = A->p[r]; k < A->p[r + 1]; k++)
76+
{
77+
if (A->i[k] <= r)
78+
{
79+
coo->rows[idx] = r;
80+
coo->cols[idx] = A->i[k];
81+
coo->x[idx] = A->x[k];
82+
coo->value_map[idx] = k;
83+
idx++;
84+
}
85+
}
86+
}
87+
88+
return coo;
89+
}
90+
91+
void refresh_lower_triangular_coo(COO_Matrix *coo, const double *vals)
92+
{
93+
for (int i = 0; i < coo->nnz; i++)
94+
{
95+
coo->x[i] = vals[coo->value_map[i]];
96+
}
97+
}
98+
99+
void free_coo_matrix(COO_Matrix *matrix)
100+
{
101+
if (matrix)
102+
{
103+
free(matrix->rows);
104+
free(matrix->cols);
105+
free(matrix->x);
106+
free(matrix->value_map);
107+
free(matrix);
108+
}
109+
}

tests/all_tests.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
#include "jacobian_tests/test_trace.h"
4444
#include "jacobian_tests/test_transpose.h"
4545
#include "problem/test_problem.h"
46+
#include "utils/test_coo_matrix.h"
4647
#include "utils/test_csc_matrix.h"
4748
#include "utils/test_csr_csc_conversion.h"
4849
#include "utils/test_csr_matrix.h"
@@ -271,6 +272,9 @@ int main(void)
271272
mu_run_test(test_ATA_alloc_random, tests_run);
272273
mu_run_test(test_ATA_alloc_random2, tests_run);
273274
mu_run_test(test_BTA_alloc_and_BTDA_fill, tests_run);
275+
mu_run_test(test_csr_to_coo, tests_run);
276+
mu_run_test(test_csr_to_coo_lower_triangular, tests_run);
277+
mu_run_test(test_refresh_lower_triangular_coo, tests_run);
274278

275279
printf("\n--- Problem Struct Tests ---\n");
276280
mu_run_test(test_problem_new_free, tests_run);

tests/utils/test_coo_matrix.h

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
#include <stdio.h>
2+
#include <stdlib.h>
3+
#include <string.h>
4+
5+
#include "minunit.h"
6+
#include "test_helpers.h"
7+
#include "utils/COO_Matrix.h"
8+
9+
const char *test_csr_to_coo()
10+
{
11+
/* Create a 3x3 CSR matrix A:
12+
* [1.0 2.0 0.0]
13+
* [0.0 3.0 4.0]
14+
* [5.0 0.0 6.0]
15+
*/
16+
CSR_Matrix *A = new_csr_matrix(3, 3, 6);
17+
double Ax[6] = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0};
18+
int Ai[6] = {0, 1, 1, 2, 0, 2};
19+
int Ap[4] = {0, 2, 4, 6};
20+
memcpy(A->x, Ax, 6 * sizeof(double));
21+
memcpy(A->i, Ai, 6 * sizeof(int));
22+
memcpy(A->p, Ap, 4 * sizeof(int));
23+
24+
COO_Matrix *coo = new_coo_matrix(A);
25+
26+
mu_assert("m incorrect", coo->m == 3);
27+
mu_assert("n incorrect", coo->n == 3);
28+
mu_assert("nnz incorrect", coo->nnz == 6);
29+
30+
int expected_rows[6] = {0, 0, 1, 1, 2, 2};
31+
int expected_cols[6] = {0, 1, 1, 2, 0, 2};
32+
double expected_x[6] = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0};
33+
34+
mu_assert("rows incorrect", cmp_int_array(coo->rows, expected_rows, 6));
35+
mu_assert("cols incorrect", cmp_int_array(coo->cols, expected_cols, 6));
36+
mu_assert("vals incorrect", cmp_double_array(coo->x, expected_x, 6));
37+
38+
free_coo_matrix(coo);
39+
free_csr_matrix(A);
40+
41+
return 0;
42+
}
43+
44+
const char *test_csr_to_coo_lower_triangular()
45+
{
46+
/* Symmetric 3x3 matrix:
47+
* [1 2 3]
48+
* [2 5 6]
49+
* [3 6 9]
50+
*/
51+
CSR_Matrix *A = new_csr_matrix(3, 3, 9);
52+
int Ap[4] = {0, 3, 6, 9};
53+
int Ai[9] = {0, 1, 2, 0, 1, 2, 0, 1, 2};
54+
double Ax[9] = {1, 2, 3, 2, 5, 6, 3, 6, 9};
55+
memcpy(A->p, Ap, 4 * sizeof(int));
56+
memcpy(A->i, Ai, 9 * sizeof(int));
57+
memcpy(A->x, Ax, 9 * sizeof(double));
58+
59+
COO_Matrix *coo = new_coo_matrix_lower_triangular(A);
60+
61+
mu_assert("ltri m incorrect", coo->m == 3);
62+
mu_assert("ltri n incorrect", coo->n == 3);
63+
mu_assert("ltri nnz incorrect", coo->nnz == 6);
64+
65+
int expected_rows[6] = {0, 1, 1, 2, 2, 2};
66+
int expected_cols[6] = {0, 0, 1, 0, 1, 2};
67+
double expected_x[6] = {1, 2, 5, 3, 6, 9};
68+
int expected_map[6] = {0, 3, 4, 6, 7, 8};
69+
70+
mu_assert("ltri rows incorrect", cmp_int_array(coo->rows, expected_rows, 6));
71+
mu_assert("ltri cols incorrect", cmp_int_array(coo->cols, expected_cols, 6));
72+
mu_assert("ltri vals incorrect", cmp_double_array(coo->x, expected_x, 6));
73+
mu_assert("ltri value_map incorrect",
74+
cmp_int_array(coo->value_map, expected_map, 6));
75+
76+
free_coo_matrix(coo);
77+
free_csr_matrix(A);
78+
79+
return 0;
80+
}
81+
82+
const char *test_refresh_lower_triangular_coo()
83+
{
84+
CSR_Matrix *A = new_csr_matrix(3, 3, 9);
85+
int Ap[4] = {0, 3, 6, 9};
86+
int Ai[9] = {0, 1, 2, 0, 1, 2, 0, 1, 2};
87+
double Ax[9] = {1, 2, 3, 2, 5, 6, 3, 6, 9};
88+
memcpy(A->p, Ap, 4 * sizeof(int));
89+
memcpy(A->i, Ai, 9 * sizeof(int));
90+
memcpy(A->x, Ax, 9 * sizeof(double));
91+
92+
COO_Matrix *coo = new_coo_matrix_lower_triangular(A);
93+
94+
double vals2[9] = {10, 20, 30, 20, 50, 60, 30, 60, 90};
95+
refresh_lower_triangular_coo(coo, vals2);
96+
97+
double expected_x[6] = {10, 20, 50, 30, 60, 90};
98+
mu_assert("refresh vals incorrect", cmp_double_array(coo->x, expected_x, 6));
99+
100+
free_coo_matrix(coo);
101+
free_csr_matrix(A);
102+
103+
return 0;
104+
}

0 commit comments

Comments
 (0)