Skip to content

Commit d4ea7d6

Browse files
committed
correct handling of reshape - solves major bug
1 parent ddcea3d commit d4ea7d6

11 files changed

Lines changed: 202 additions & 41 deletions

File tree

include/affine.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,6 @@ expr *new_constant(int d1, int d2, int n_vars, const double *values);
1919
expr *new_variable(int d1, int d2, int var_id, int n_vars);
2020

2121
expr *new_index(expr *child, const int *indices, int n_idxs);
22+
expr *new_reshape(expr *child, int d1, int d2);
2223

2324
#endif /* AFFINE_H */

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ wheel.packages = ["src/dnlp_diff_engine"]
2929
build-dir = "build/{wheel_tag}"
3030

3131
[tool.scikit-build.cmake.define]
32-
CMAKE_BUILD_TYPE = "Release"
32+
CMAKE_BUILD_TYPE = "Debug"
3333

3434
[tool.pytest.ini_options]
3535
testpaths = ["python/tests"]

python/atoms/getters.h

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
#ifndef ATOM_GETTERS_H
2+
#define ATOM_GETTERS_H
3+
4+
#include "common.h"
5+
6+
static PyObject *py_get_expr_dimensions(PyObject *self, PyObject *args)
7+
{
8+
PyObject *expr_capsule;
9+
10+
if (!PyArg_ParseTuple(args, "O", &expr_capsule))
11+
{
12+
return NULL;
13+
}
14+
15+
expr *node = (expr *) PyCapsule_GetPointer(expr_capsule, EXPR_CAPSULE_NAME);
16+
if (!node)
17+
{
18+
return NULL;
19+
}
20+
21+
// Return tuple (d1, d2)
22+
return Py_BuildValue("(ii)", node->d1, node->d2);
23+
}
24+
25+
static PyObject *py_get_expr_size(PyObject *self, PyObject *args)
26+
{
27+
PyObject *expr_capsule;
28+
29+
if (!PyArg_ParseTuple(args, "O", &expr_capsule))
30+
{
31+
return NULL;
32+
}
33+
34+
expr *node = (expr *) PyCapsule_GetPointer(expr_capsule, EXPR_CAPSULE_NAME);
35+
if (!node)
36+
{
37+
return NULL;
38+
}
39+
40+
return Py_BuildValue("i", node->size);
41+
}
42+
43+
#endif /* ATOM_GETTERS_H */

python/atoms/reshape.h

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#ifndef ATOM_RESHAPE_H
2+
#define ATOM_RESHAPE_H
3+
4+
#include "common.h"
5+
6+
static PyObject *py_make_reshape(PyObject *self, PyObject *args)
7+
{
8+
PyObject *child_capsule;
9+
int d1, d2;
10+
11+
if (!PyArg_ParseTuple(args, "Oii", &child_capsule, &d1, &d2))
12+
{
13+
return NULL;
14+
}
15+
16+
expr *child = (expr *) PyCapsule_GetPointer(child_capsule, EXPR_CAPSULE_NAME);
17+
if (!child)
18+
{
19+
return NULL;
20+
}
21+
22+
expr *node = new_reshape(child, d1, d2);
23+
if (!node)
24+
{
25+
PyErr_SetString(PyExc_RuntimeError, "failed to create reshape node");
26+
return NULL;
27+
}
28+
29+
expr_retain(node);
30+
return PyCapsule_New(node, EXPR_CAPSULE_NAME, expr_capsule_destructor);
31+
}
32+
33+
#endif /* ATOM_RESHAPE_H */

python/bindings.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "atoms/cos.h"
1313
#include "atoms/entr.h"
1414
#include "atoms/exp.h"
15+
#include "atoms/getters.h"
1516
#include "atoms/index.h"
1617
#include "atoms/left_matmul.h"
1718
#include "atoms/linear.h"
@@ -24,6 +25,7 @@
2425
#include "atoms/quad_form.h"
2526
#include "atoms/quad_over_lin.h"
2627
#include "atoms/rel_entr.h"
28+
#include "atoms/reshape.h"
2729
#include "atoms/right_matmul.h"
2830
#include "atoms/sin.h"
2931
#include "atoms/sinh.h"
@@ -90,6 +92,11 @@ static PyMethodDef DNLPMethods[] = {
9092
"Create quad_over_lin node (sum(x^2) / y)"},
9193
{"make_rel_entr", py_make_rel_entr, METH_VARARGS,
9294
"Create rel_entr node: x * log(x/y) elementwise"},
95+
{"get_expr_dimensions", py_get_expr_dimensions, METH_VARARGS,
96+
"Get the dimensions (d1, d2) of an expression"},
97+
{"get_expr_size", py_get_expr_size, METH_VARARGS,
98+
"Get the total size of an expression"},
99+
{"make_reshape", py_make_reshape, METH_VARARGS, "Create reshape atom"},
93100
{"make_problem", py_make_problem, METH_VARARGS,
94101
"Create problem from objective and constraints"},
95102
{"problem_init_derivatives", py_problem_init_derivatives, METH_VARARGS,

src/affine/constant.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ static bool is_affine(const expr *node)
4343
expr *new_constant(int d1, int d2, int n_vars, const double *values)
4444
{
4545
expr *node = new_expr(d1, d2, n_vars);
46-
memcpy(node->value, values, d1 * d2 * sizeof(double));
46+
memcpy(node->value, values, node->size * sizeof(double));
4747
node->forward = forward;
4848
node->is_affine = is_affine;
4949
node->jacobian_init = jacobian_init;

src/affine/index.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,5 @@ expr *new_index(expr *child, const int *indices, int n_idxs)
168168

169169
/* detect duplicates for Hessian optimization */
170170
idx->has_duplicates = check_for_duplicates(indices, n_idxs, child->size);
171-
172171
return node;
173172
}

src/affine/promote.c

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#include "affine.h"
2+
#include <assert.h>
23
#include <stdlib.h>
34
#include <string.h>
45

@@ -10,35 +11,32 @@ static void forward(expr *node, const double *u)
1011
node->left->forward(node->left, u);
1112

1213
/* broadcast scalar value to all output elements */
13-
double val = node->left->value[0];
1414
for (int i = 0; i < node->size; i++)
1515
{
16-
node->value[i] = val;
16+
node->value[i] = node->left->value[0];
1717
}
1818
}
1919

2020
static void jacobian_init(expr *node)
2121
{
22-
node->left->jacobian_init(node->left);
23-
24-
/* Each output row copies the single row from child's jacobian */
25-
CSR_Matrix *child_jac = node->left->jacobian;
26-
int nnz = node->size * child_jac->nnz;
22+
expr *x = node->left;
23+
x->jacobian_init(x);
2724

25+
// each output row copies the single row from child's jacobian
26+
int nnz = node->size * x->jacobian->nnz;
2827
node->jacobian = new_csr_matrix(node->size, node->n_vars, nnz);
29-
CSR_Matrix *jac = node->jacobian;
3028

31-
/* Build sparsity pattern by replicating child's single row */
32-
int child_nnz = child_jac->p[1] - child_jac->p[0];
33-
jac->nnz = 0;
29+
// fill sparsity pattern
30+
CSR_Matrix *J = node->jacobian;
31+
J->nnz = 0;
3432
for (int row = 0; row < node->size; row++)
3533
{
36-
jac->p[row] = jac->nnz;
37-
memcpy(jac->i + jac->nnz, child_jac->i + child_jac->p[0],
38-
child_nnz * sizeof(int));
39-
jac->nnz += child_nnz;
34+
J->p[row] = J->nnz;
35+
memcpy(J->i + J->nnz, x->jacobian->i, x->jacobian->nnz * sizeof(int));
36+
J->nnz += x->jacobian->nnz;
4037
}
41-
jac->p[node->size] = jac->nnz;
38+
assert(J->nnz == nnz);
39+
J->p[node->size] = J->nnz;
4240
}
4341

4442
static void eval_jacobian(expr *node)
@@ -95,6 +93,7 @@ static bool is_affine(const expr *node)
9593

9694
expr *new_promote(expr *child, int d1, int d2)
9795
{
96+
assert(child->size == 1);
9897
expr *node = new_expr(d1, d2, child->n_vars);
9998
node->forward = forward;
10099
node->jacobian_init = jacobian_init;

src/affine/reshape.c

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
#include "affine.h"
2+
#include <assert.h>
3+
#include <stdio.h>
4+
#include <string.h>
5+
6+
/* Reshape changes the shape of an expression without permuting data.
7+
* Only Fortran (column-major) order is supported, where reshape is a no-op
8+
* for the underlying data layout. */
9+
10+
static void forward(expr *node, const double *u)
11+
{
12+
node->left->forward(node->left, u);
13+
memcpy(node->value, node->left->value, node->size * sizeof(double));
14+
}
15+
16+
static void jacobian_init(expr *node)
17+
{
18+
expr *x = node->left;
19+
x->jacobian_init(x);
20+
node->jacobian = new_csr_matrix(node->size, node->n_vars, x->jacobian->nnz);
21+
CSR_Matrix *jac = node->jacobian;
22+
memcpy(jac->p, x->jacobian->p, (x->size + 1) * sizeof(int));
23+
memcpy(jac->i, x->jacobian->i, x->jacobian->nnz * sizeof(int));
24+
}
25+
26+
static void eval_jacobian(expr *node)
27+
{
28+
expr *x = node->left;
29+
x->eval_jacobian(x);
30+
memcpy(node->jacobian->x, x->jacobian->x, x->jacobian->nnz * sizeof(double));
31+
}
32+
33+
static void wsum_hess_init(expr *node)
34+
{
35+
node->left->wsum_hess_init(node->left);
36+
CSR_Matrix *child_hess = node->left->wsum_hess;
37+
node->wsum_hess = new_csr_matrix(child_hess->m, child_hess->n, child_hess->nnz);
38+
memcpy(node->wsum_hess->p, child_hess->p, (child_hess->m + 1) * sizeof(int));
39+
memcpy(node->wsum_hess->i, child_hess->i, child_hess->nnz * sizeof(int));
40+
node->wsum_hess->nnz = child_hess->nnz;
41+
}
42+
43+
static void eval_wsum_hess(expr *node, const double *w)
44+
{
45+
node->left->eval_wsum_hess(node->left, w);
46+
CSR_Matrix *child_hess = node->left->wsum_hess;
47+
CSR_Matrix *hess = node->wsum_hess;
48+
memcpy(hess->x, child_hess->x, child_hess->nnz * sizeof(double));
49+
}
50+
51+
static bool is_affine(const expr *node)
52+
{
53+
return node->left->is_affine(node->left);
54+
}
55+
56+
expr *new_reshape(expr *child, int d1, int d2)
57+
{
58+
assert(d1 * d2 == child->size);
59+
expr *node = new_expr(d1, d2, child->n_vars);
60+
node->left = child;
61+
expr_retain(child);
62+
node->forward = forward;
63+
node->is_affine = is_affine;
64+
node->jacobian_init = jacobian_init;
65+
node->eval_jacobian = eval_jacobian;
66+
node->wsum_hess_init = wsum_hess_init;
67+
node->eval_wsum_hess = eval_wsum_hess;
68+
69+
return node;
70+
}

src/bivariate/left_matmul.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "bivariate.h"
22
#include "subexpr.h"
3+
#include <assert.h>
34
#include <stdlib.h>
45

56
/* This file implement the atom 'left_matmul' corresponding to the operation y =
@@ -113,6 +114,7 @@ static void eval_wsum_hess(expr *node, const double *w)
113114

114115
expr *new_left_matmul(expr *u, const CSR_Matrix *A)
115116
{
117+
assert(u->d1 == A->n);
116118
/* Allocate the type-specific struct */
117119
left_matmul_expr *lin_node =
118120
(left_matmul_expr *) calloc(1, sizeof(left_matmul_expr));

0 commit comments

Comments
 (0)