Skip to content

Commit 05b64ef

Browse files
committed
elementwise jacobian
1 parent cc6dcc5 commit 05b64ef

41 files changed

Lines changed: 1112 additions & 347 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CMakeLists.txt

Lines changed: 13 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,17 @@ cmake_minimum_required(VERSION 3.10)
22
project(DNLP_Diff_Engine C)
33

44
set(CMAKE_C_STANDARD 99)
5+
set(CMAKE_BUILD_TYPE Debug)
6+
7+
# Debug-friendly flags
8+
add_compile_options(-g -O0)
59

610
# Include directories
711
include_directories(${PROJECT_SOURCE_DIR}/include)
12+
include_directories(${PROJECT_SOURCE_DIR}/tests)
813

9-
# Source files
10-
set(SOURCES
11-
src/expr.c
12-
src/elementwise/exp.c
13-
src/elementwise/log.c
14-
src/affine/variable.c
15-
src/affine/constant.c
16-
src/affine/add.c
17-
)
14+
# Source files - automatically gather all .c files from src/
15+
file(GLOB_RECURSE SOURCES "src/*.c")
1816

1917
# Create library
2018
add_library(dnlp_diff ${SOURCES})
@@ -23,15 +21,10 @@ target_link_libraries(dnlp_diff m)
2321
# Enable testing
2422
enable_testing()
2523

26-
# Single test executable that links all test source files
27-
add_executable(test_all
28-
tests/forward_pass/all_tests.c
29-
tests/forward_pass/test_helpers.c
30-
tests/forward_pass/affine/test_variable_constant.c
31-
tests/forward_pass/affine/test_add.c
32-
tests/forward_pass/elementwise/test_exp.c
33-
tests/forward_pass/elementwise/test_log.c
34-
tests/forward_pass/composite/test_composite.c
24+
# Single test executable combining all tests
25+
add_executable(all_tests
26+
tests/all_tests.c
27+
tests/test_helpers.c
3528
)
36-
target_link_libraries(test_all dnlp_diff)
37-
add_test(NAME AllTests COMMAND test_all)
29+
target_link_libraries(all_tests dnlp_diff)
30+
add_test(NAME AllTests COMMAND all_tests)

README.md

Lines changed: 0 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -1,91 +0,0 @@
1-
# DNLP Diff Engine
2-
3-
A minimalistic C-based expression tree system for nonlinear programming with automatic differentiation capabilities.
4-
5-
## Structure
6-
7-
```
8-
.
9-
├── include/ # Header files
10-
│ ├── expr.h # Base expression node definition
11-
│ ├── affine/ # Affine operations
12-
│ │ ├── variable.h
13-
│ │ ├── constant.h
14-
│ │ └── add.h
15-
│ └── elementwise/ # Elementwise operations
16-
│ ├── exp.h
17-
│ └── log.h
18-
├── src/
19-
│ ├── expr.c # Base node implementation
20-
│ ├── affine/ # Affine operations
21-
│ │ ├── variable.c
22-
│ │ ├── constant.c
23-
│ │ └── add.c
24-
│ └── elementwise/ # Elementwise operations
25-
│ ├── exp.c
26-
│ └── log.c
27-
├── tests/
28-
│ └── forward_pass/ # Forward pass tests
29-
│ └── test_forward_pass.c
30-
└── CMakeLists.txt # Build configuration
31-
```
32-
33-
## Features
34-
35-
- **Expression nodes**: Each node has dimension `m`, preallocated value memory, and up to two children
36-
- **Leaf nodes**: Variable (reads from input `u`) and Constant (fixed values)
37-
- **Affine operations**: Addition
38-
- **Elementwise operations**: Exponential (`exp`) and Logarithm (`log`)
39-
- **Forward pass**: Compute output values through the expression tree
40-
41-
## Building
42-
43-
```bash
44-
mkdir build && cd build
45-
cmake ..
46-
make
47-
```
48-
49-
## Running Tests
50-
51-
```bash
52-
# Run the test executable
53-
./test_forward_pass
54-
55-
# Or use CTest
56-
ctest --verbose
57-
```
58-
59-
## Example Usage
60-
61-
```c
62-
#include "expr.h"
63-
#include "affine/variable.h"
64-
#include "affine/constant.h"
65-
#include "affine/add.h"
66-
#include "elementwise/exp.h"
67-
#include "elementwise/log.h"
68-
69-
// Build expression tree: log(exp(x) + c)
70-
double u[2] = {1.0, 2.0};
71-
double c[2] = {1.0, 1.0};
72-
73-
expr* var = new_variable(2);
74-
expr* exp_node = new_exp(var);
75-
expr* const_node = new_constant(2, c);
76-
expr* sum = new_add(exp_node, const_node);
77-
expr* log_node = new_log(sum);
78-
79-
// Compute forward pass
80-
log_node->forward(log_node, u);
81-
82-
// Result is in log_node->value
83-
```
84-
85-
## Design
86-
87-
- Each node allocates its own output memory
88-
- Forward pass recursively evaluates children before computing node output
89-
- Clean separation: affine operations in `src/affine/`, elementwise in `src/elementwise/`
90-
- Memory management: nodes must be freed manually (children are not auto-freed)
91-
# DNLP-diff-engine

include/affine/linear_op.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#ifndef LINEAR_OP_H
2+
#define LINEAR_OP_H
3+
4+
#include "expr.h"
5+
#include "utils/CSR_Matrix.h"
6+
7+
/* linear operator f(u) = Au */
8+
expr *new_linear(expr *u, const CSR_Matrix *A);
9+
10+
#endif /* LINEAR_OP_H */

include/affine/variable.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@
33

44
#include "expr.h"
55

6-
expr *new_variable(int m);
6+
expr *new_variable(int m, int var_id, int n_vars);
77

88
#endif /* VARIABLE_H */

include/elementwise.h

Lines changed: 0 additions & 9 deletions
This file was deleted.

include/elementwise_univariate.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#ifndef ELEMENTWISE_H
2+
#define ELEMENTWISE_H
3+
4+
#include "expr.h"
5+
6+
expr *new_exp(expr *child);
7+
expr *new_log(expr *child);
8+
expr *new_entr(expr *child);
9+
expr *new_sin(expr *child);
10+
expr *new_cos(expr *child);
11+
expr *new_tan(expr *child);
12+
13+
/* the jacobian for elementwise atoms are always initialized in the
14+
same way and implement the chain rule in the same way */
15+
void jacobian_init_elementwise(expr *node);
16+
void eval_jacobian_elementwise(expr *node);
17+
18+
/* no elementwise atoms are affine according to our convention,
19+
so we can have a common implementation */
20+
bool is_affine_elementwise(expr *node);
21+
22+
#endif /* ELEMENTWISE_H */

include/expr.h

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,53 @@
11
#ifndef EXPR_H
22
#define EXPR_H
33

4+
#include "utils/CSR_Matrix.h"
5+
#include <stdbool.h>
46
#include <stddef.h>
57

6-
/* Forward declaration */
8+
#define JAC_IDXS_NOT_SET -1
9+
10+
/* Forward declarations */
711
struct expr;
812

9-
/* Function pointer type for forward pass computation */
13+
/* Function pointer types */
1014
typedef void (*forward_fn)(struct expr *node, const double *u);
15+
typedef void (*jacobian_init_fn)(struct expr *node);
16+
typedef void (*eval_jacobian_fn)(struct expr *node);
17+
typedef void (*eval_local_jacobian_fn)(struct expr *node, double *out);
18+
typedef bool (*is_affine_fn)(struct expr *node);
1119

1220
/* Expression node structure */
1321
typedef struct expr
1422
{
15-
int m; /* Output dimension */
16-
double *value; /* Preallocated output value array */
17-
struct expr *left; /* Left child (can be NULL) */
18-
struct expr *right; /* Right child (can be NULL) */
19-
forward_fn forward; /* Forward pass function */
23+
// ------------------------------------------------------------------------
24+
// general quantities
25+
// ------------------------------------------------------------------------
26+
int m;
27+
int n_vars;
28+
int var_id;
29+
struct expr *left;
30+
struct expr *right;
31+
double *dwork;
32+
33+
// ------------------------------------------------------------------------
34+
// forward pass related quantities
35+
// ------------------------------------------------------------------------
36+
double *value;
37+
forward_fn forward;
38+
39+
// ------------------------------------------------------------------------
40+
// jacobian related quantities
41+
// ------------------------------------------------------------------------
42+
CSR_Matrix *jacobian;
43+
jacobian_init_fn jacobian_init;
44+
eval_jacobian_fn eval_jacobian;
45+
eval_local_jacobian_fn eval_local_jacobian;
46+
is_affine_fn is_affine;
47+
2048
} expr;
2149

22-
/* Memory management functions */
23-
expr *new_expr(int m);
50+
expr *new_expr(int m, int n_vars);
2451
void free_expr(expr *node);
2552

2653
#endif /* EXPR_H */

include/utils/COO_Matrix.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#ifndef MATRIX_H
2+
#define MATRIX_H
3+
4+
/* Coordinate (COO) format sparse matrix */
5+
typedef struct COO_Matrix
6+
{
7+
int nnz;
8+
int *rows;
9+
int *cols;
10+
double *vals;
11+
} COO_Matrix;
12+
13+
COO_Matrix *new_coo_matrix(int nnz);
14+
void free_coo_matrix(COO_Matrix *matrix);
15+
16+
#endif /* MATRIX_H */

include/utils/CSR_Matrix.h

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#ifndef CSR_MATRIX_H
2+
#define CSR_MATRIX_H
3+
4+
/* CSR (Compressed Sparse Row) Matrix Format
5+
*
6+
* For an m x n matrix with nnz nonzeros:
7+
* - p: array of size (m + 1) indicating start of each row
8+
* - i: array of size nnz containing column indices
9+
* - x: array of size nnz containing values
10+
* - m: number of rows
11+
* - n: number of columns
12+
* - nnz: number of nonzero entries
13+
*/
14+
typedef struct CSR_Matrix
15+
{
16+
int *p;
17+
int *i;
18+
double *x;
19+
int m;
20+
int n;
21+
int nnz;
22+
} CSR_Matrix;
23+
24+
/* Allocate a new CSR matrix with given dimensions and nnz */
25+
CSR_Matrix *new_csr_matrix(int m, int n, int nnz);
26+
27+
/* Free a CSR matrix */
28+
void free_csr_matrix(CSR_Matrix *matrix);
29+
30+
/* Copy CSR matrix A to C */
31+
void copy_csr_matrix(const CSR_Matrix *A, CSR_Matrix *C);
32+
33+
/* matvec y = Ax, where A indices minus col_offset gives x indices */
34+
void csr_matvec(const CSR_Matrix *A, const double *x, double *y, int col_offset);
35+
36+
/* Compute C = diag(d) * A where d is an array and A, C are CSR matrices
37+
* d must have length m
38+
* C must be pre-allocated with same dimensions as A */
39+
void diag_csr_mult(const double *d, const CSR_Matrix *A, CSR_Matrix *C);
40+
41+
/* Compute C = A + B where A, B, C are CSR matrices
42+
* A and B must have same dimensions
43+
* C must be pre-allocated with sufficient nnz capacity */
44+
void sum_csr_matrices(const CSR_Matrix *A, const CSR_Matrix *B, CSR_Matrix *C);
45+
46+
#endif /* CSR_MATRIX_H */

src/affine/add.c

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,46 @@ static void add_forward(expr *node, const double *u)
1313
}
1414
}
1515

16+
static void jacobian_init(expr *node)
17+
{
18+
/* initialize children's jacobians */
19+
node->left->jacobian_init(node->left);
20+
node->right->jacobian_init(node->right);
21+
22+
/* we never have to store more than the sum of children's nnz */
23+
int nnz_max = node->left->jacobian->nnz + node->right->jacobian->nnz;
24+
node->jacobian = new_csr_matrix(node->m, node->n_vars, nnz_max);
25+
}
26+
27+
static void eval_jacobian(expr *node)
28+
{
29+
/* evaluate children's jacobians */
30+
node->left->eval_jacobian(node->left);
31+
node->right->eval_jacobian(node->right);
32+
33+
/* sum children's jacobians */
34+
sum_csr_matrices(node->left->jacobian, node->right->jacobian, node->jacobian);
35+
}
36+
37+
static bool is_affine(expr *node)
38+
{
39+
return node->left->is_affine(node->left) && node->right->is_affine(node->right);
40+
}
41+
1642
expr *new_add(expr *left, expr *right)
1743
{
1844
if (!left || !right) return NULL;
19-
if (left->m != right->m) return NULL; /* Dimension mismatch */
45+
if (left->m != right->m) return NULL;
2046

21-
expr *node = new_expr(left->m);
47+
expr *node = new_expr(left->m, left->n_vars);
2248
if (!node) return NULL;
2349

2450
node->left = left;
2551
node->right = right;
2652
node->forward = add_forward;
53+
node->is_affine = is_affine;
54+
node->jacobian_init = jacobian_init;
55+
node->eval_jacobian = eval_jacobian;
2756

2857
return node;
2958
}

0 commit comments

Comments
 (0)