Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update proxTV to easily call it from C #62

Open
wants to merge 37 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
9c97251
Minor syntax changes to compile properly
nathanemac Sep 3, 2024
bc79acd
avoid mangling by encapsulating functions in cpp files
nathanemac Sep 3, 2024
73b2528
pus for alexis
nathanemac Sep 3, 2024
5fb3bef
fixing bugs
nathanemac Sep 3, 2024
1a0b1aa
fix one extern C
nathanemac Sep 3, 2024
149df38
Fix the compilation
amontoison Sep 3, 2024
f06877d
Remove Mac files
amontoison Sep 3, 2024
6f7ad7e
Final version of proxTV
amontoison Sep 3, 2024
02af58b
fix double-free memory issue with wsinner in GPFW_TVp
nathanemac Sep 20, 2024
37ccfc9
fix double-free memory issue with wsinner in GPFW_TVp
nathanemac Sep 20, 2024
fa1bd94
Update .gitignore with .DS_Store
nathanemac Sep 20, 2024
8d780db
Résolution des conflits et configuration de .gitignore
nathanemac Nov 19, 2024
d35fc41
modify first candidate in LPopt.cpp & allow TV to take objGap as opti…
nathanemac Nov 19, 2024
3225ef9
add files to .gitignore
nathanemac Nov 19, 2024
716fa41
Résolution des conflits et configuration de .gitignore
nathanemac Nov 19, 2024
c7c91a5
modify first candidate in LPopt.cpp & allow TV to take objGap as opti…
nathanemac Nov 19, 2024
2a51811
add files to .gitignore
nathanemac Nov 19, 2024
0272ffc
Merge remote-tracking branch 'upstream/master'
nathanemac Dec 3, 2024
977395b
add callback
nathanemac Nov 26, 2024
a9add91
small updates
nathanemac Nov 27, 2024
ed29a96
small updates
nathanemac Nov 27, 2024
08048a4
use kwargs in PN_LPp to dispatch whether to use objgap condition or c…
nathanemac Nov 28, 2024
5d937a3
fix signs when calling callback
nathanemac Nov 29, 2024
8a8b7ff
small updates
nathanemac Nov 29, 2024
48c5847
small updates
nathanemac Nov 29, 2024
b8f5d3f
last changes before update
nathanemac Dec 2, 2024
324313a
last update on clipping if d in inf ou too big
nathanemac Dec 2, 2024
66a2ced
remove crap
nathanemac Dec 2, 2024
ac72164
remove crap
nathanemac Dec 2, 2024
45e51ec
remove crap
nathanemac Dec 2, 2024
243b28c
remove useless prints
nathanemac Dec 3, 2024
1531747
addressing memory issue
nathanemac Dec 3, 2024
1e5026e
fixing memory issue
nathanemac Dec 4, 2024
2774119
fixing memory issue
nathanemac Dec 4, 2024
778233b
fixing memory issue
nathanemac Dec 4, 2024
cf17880
addressing memory issue
nathanemac Dec 4, 2024
ac21e5b
Merge pull request #3 from nathanemac/add_callback_objgap
nathanemac Dec 4, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,11 @@ prox_tv.egg-info
.coverage
.idea
wheelhouse
*.so
*.dSYM/
main
main.cpp
*.o
*.DS_Store
*.dylib

Binary file added src/.DS_Store
Binary file not shown.
134 changes: 99 additions & 35 deletions src/LPopt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@
#include <limits.h>
#include "LPopt.h"

#ifdef __cplusplus
extern "C" {
#endif

/*** Internal functions headers ***/
double PN_LPpGap(double *x, double *y, double *diff, int n, double q, double lambda, double norm);

Expand Down Expand Up @@ -208,8 +212,10 @@ int PN_LPinf(double *y,double lambda,double *x,double *info,int n,Workspace *ws)
- ws: workspace of allocated memory to use. If NULL, any needed memory is locally managed.
- positive: 1 if all inputs y >= 0, 0 else.
- objGap: desired quality of the solution in terms of duality gap.
- ctx_ptr: pointer to context data to be passed to the callback function.
- callback: callback function to be called at each iteration.
*/
int PN_LPp(double *y,double lambda,double *x,double *info,int n,double p,Workspace *ws,int positive,double objGap){
int PN_LPp(double *y,double lambda,double *x,double *info,int n,double p,Workspace *ws,int positive,double objGap, void* ctx_ptr, int (*callback)(const double* s_ptr, size_t s_length, double delta_k, void* ctx_ptr)){
double *g=NULL,*d=NULL,*xnorm=NULL,*auxv=NULL;
double stop,stop2,q,nx,f,fupdate,aux,c,den,xp1vGrad,gRd,delta,prevDelta,improve,rhs,grad0,gap,epsilon;
int *inactive=NULL,*signs=NULL;
Expand Down Expand Up @@ -243,6 +249,10 @@ int PN_LPp(double *y,double lambda,double *x,double *info,int n,double p,Workspa
/* Compute dual norm q */
q = 1/(1-1/p);

/* initialize stopping condition */
bool stopping_condition = false;


/* Special case where the solution is the trivial x = 0 */
/* This is bound to happen if ||y||_q <= lambda */
if ( LPnorm(y, n, q) <= lambda ) {
Expand Down Expand Up @@ -285,38 +295,50 @@ int PN_LPp(double *y,double lambda,double *x,double *info,int n,double p,Workspa
if(!g || !d || !xnorm || !auxv || !inactive)
{CANCEL("out of memory",info)}

/* As initial points, use an approximation for the closed-form solution */

/* Find close-form solution for TV-L2 */
PN_LP2(y , lambda , x, NULL ,n);
/* Measure gap of this solution in terms of TV-Lp norm */
/* ADDED NOV 15 BY Nathan Allaire - we need x=0 as a first point, not an approximation from another norm. */
memset(x, 0, sizeof(double)*n); // Fill x with zeros
for ( i = 0 ; i < n ; i++ )
auxv[i] = x[i] - y[i];
nx = LPnorm( x, n, p );
stop = PN_LPpGap(x, y, auxv, n, q, lambda, nx);

/* Find close-form solution for TV-L1 or TV-Linf, whichever is closer in norm value */
if ( p < 2 )
PN_LP1(y , lambda , xnorm, NULL ,n);
else
PN_LPinf(y , lambda , xnorm, NULL ,n, ws);
/* Measure gap of this solution in terms of TV-Lp norm */
for ( i = 0 ; i < n ; i++ )
auxv[i] = xnorm[i] - y[i];
nx = LPnorm( xnorm, n, p );
stop2 = PN_LPpGap(xnorm, y, auxv, n, q, lambda, nx);

#ifdef DEBUG
fprintf(DEBUG_FILE,"Starting candidates: p2 = %lf, p%s = %lf\n", stop, p<2 ? "1" : "inf", stop2);
#endif

/* Use as starting point the closed-form solution with smaller dual gap */
if ( stop2 < stop ) {
/* Copy starting point */
memcpy(x, xnorm, sizeof(double)*n);
stop = stop2;
}
stop = PN_LPpGap(x, y, auxv, n, q, lambda, nx); // Compute gap of this solution in terms of TV-Lp norm

// ADDED NOV 15 - BEGINNING OF COMMENTING LINES

// /* As initial points, use an approximation for the closed-form solution */

// /* Find close-form solution for TV-L2 */
// PN_LP2(y , lambda , x, NULL ,n);
// /* Measure gap of this solution in terms of TV-Lp norm */
// for ( i = 0 ; i < n ; i++ )
// auxv[i] = x[i] - y[i];
// nx = LPnorm( x, n, p );
// stop = PN_LPpGap(x, y, auxv, n, q, lambda, nx);

// /* Find close-form solution for TV-L1 or TV-Linf, whichever is closer in norm value */
// if ( p < 2 )
// PN_LP1(y , lambda , xnorm, NULL ,n);
// else
// PN_LPinf(y , lambda , xnorm, NULL ,n, ws);
// /* Measure gap of this solution in terms of TV-Lp norm */
// for ( i = 0 ; i < n ; i++ )
// auxv[i] = xnorm[i] - y[i];
// nx = LPnorm( xnorm, n, p );
// stop2 = PN_LPpGap(xnorm, y, auxv, n, q, lambda, nx);

// #ifdef DEBUG
// fprintf(DEBUG_FILE,"Starting candidates: p2 = %lf, p%s = %lf\n", stop, p<2 ? "1" : "inf", stop2);
// #endif

// /* Use as starting point the closed-form solution with smaller dual gap */
// if ( stop2 < stop ) {
// /* Copy starting point */
// memcpy(x, xnorm, sizeof(double)*n);
// stop = stop2;
// }

// ADDED NOV 15 - END OF COMMENTING LINES
/* If dual gap is good enough, we are finished */

if ( stop < objGap ) {
FREE
if (info) {
Expand Down Expand Up @@ -355,7 +377,6 @@ int PN_LPp(double *y,double lambda,double *x,double *info,int n,double p,Workspa
for ( i = 0 ; i < n ; i++ )
if ( x[i] < epsilon )
x[i] = epsilon;

/* Initial value of the point norm */
nx = LPnorm(x,n,p);
/* Compute differences x - y */
Expand All @@ -364,7 +385,7 @@ int PN_LPp(double *y,double lambda,double *x,double *info,int n,double p,Workspa

/* Projected Newton loop */
stop = gap = DBL_MAX; iters = 0;
for(iters=0 ; stop > STOP_PNLP && iters < MAX_ITERS_PNLP && gap > objGap ; iters++){
for(iters=0 ;iters < MAX_ITERS_PNLP && !stopping_condition; iters++){ // stop > STOP_PNLP &&
#ifdef DEBUG
fprintf(DEBUG_FILE,"Iter %d, x=[ ",iters);
for(i=0;i<n && i<DEBUG_N;i++) fprintf(DEBUG_FILE,"%g ",x[i]);
Expand Down Expand Up @@ -448,15 +469,24 @@ int PN_LPp(double *y,double lambda,double *x,double *info,int n,double p,Workspa
break;

/* Use minimum norm subgradient updating direction */
case PNLP_MNSG:
case PNLP_MNSG: {
/* Compute minimum norm subgradient at x=0 */
/* Since the gradient at x=0 is not to be trusted, the inactive constraint detection is ignored */
nI = n;
double MAX_D = 1e9;
for(i=0;i<n;i++){
d[i] = - pow(y[i] / lambda, 1 / (p-1));

if (isinf(d[i])) {
d[i] = (d[i] > 0) ? MAX_D : -MAX_D;
} else if (fabs(d[i]) > MAX_D) {
d[i] = (d[i] > 0) ? MAX_D : -MAX_D;
}
inactive[i] = i;
}
break;
}


/* Hessian (Newton) updating direction */
case PNLP_HESSIAN:
Expand All @@ -482,6 +512,7 @@ int PN_LPp(double *y,double lambda,double *x,double *info,int n,double p,Workspa
xp1vGrad += auxv[j] * g[j];
}


#ifdef DEBUG
fprintf(DEBUG_FILE,"Iter %d, dDiag=[ ",iters);
for(i=0;i<n && i<DEBUG_N;i++) fprintf(DEBUG_FILE,"%lg ",d[i]);
Expand Down Expand Up @@ -659,6 +690,35 @@ int PN_LPp(double *y,double lambda,double *x,double *info,int n,double p,Workspa

/* Compute dual gap */
gap = PN_LPpGap(x, y, auxv, n, q, lambda, nx);

/* ADDED NOV 26 NATHAN ALLAIRE */
/* If callback and ctx_ptr are not defined, switch to regular update on dual gap. Else, call the callback function from Julia */
if (callback && ctx_ptr) {
double *x_signed = (double*)malloc(sizeof(double) * n);

/* Copy x into x_signed and adjust almost zero entries */
for (i = 0; i < n; i++) {
x_signed[i] = x[i]; // Copy the value from x
if (fabs(x_signed[i]) <= epsilon) {
x_signed[i] = 0; // Set near-zero values to exact zero
}
}

/* Apply signs if input was not all positive */
if (!positive) {
for (i = 0; i < n; i++) {
x_signed[i] = (signs[i] == -1) ? -x_signed[i] : x_signed[i];
}
}

stopping_condition = callback(x_signed, n, gap, ctx_ptr);
free(x_signed);
} else {
stopping_condition = (gap < objGap);
} /* END OF NOV 26 ADDITION */



#ifdef DEBUG
fprintf(DEBUG_FILE,"Iter %d, stop=%lg, gap=%lg\n",iters,stop,gap);
#endif
Expand Down Expand Up @@ -729,8 +789,8 @@ int PN_LPp(double *y,double lambda,double *x,double *info,int n,double p,Workspa

The proximity problem is solved to a default level of accuracy, as given by STOP_GAP_PNLP.
*/
int PN_LPp(double *y,double lambda,double *x,double *info,int n,double p,Workspace *ws,int positive) {
return PN_LPp(y, lambda, x, info, n, p, ws, positive, STOP_GAP_PNLP);
int PN_LPp_v2(double *y,double lambda,double *x,double *info,int n,double p,Workspace *ws,int positive) {
return PN_LPp(y, lambda, x, info, n, p, ws, positive, STOP_GAP_PNLP, NULL, NULL);
}

/** PN_LPpGap
Expand Down Expand Up @@ -948,7 +1008,7 @@ int LPp_project(double *y,double lambda,double *x,double *info,int n,double p,Wo
#endif

/* Invoke Lp prox solver on dual norm */
if(!PN_LPp(y,lambda,x,info,n,q,ws,1))
if(!PN_LPp(y,lambda,x,info,n,q,ws,1, STOP_GAP_PNLP))
{CANCEL("error in internal Lp prox solver",info)}

/* Apply Moreau's decomposition to recover primal problem solution */
Expand Down Expand Up @@ -1057,3 +1117,7 @@ void solveLinearLP(double *z, int n, double p, double lambda, double *s) {
for ( i = 0 ; i < n ; i++ )
s[i] = s[i] / sNorm * lambda;
}

#ifdef __cplusplus
}
#endif
12 changes: 9 additions & 3 deletions src/LPopt.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,21 +38,27 @@
#define MIN_STEP_PNLP 1e-10

/*** Module functions ***/

#ifdef __cplusplus
extern "C" {
#endif
/* Lp norm value */
double LPnorm(double *x, int n, double p);

/* Lp proximity solver */
int PN_LP1(double *y,double lambda,double *x,double *info,int n);
int PN_LP2(double *y,double lambda,double *x,double *info,int n);
int PN_LPinf(double *y,double lambda,double *x,double *info,int n,Workspace *ws);
int PN_LPp(double *y,double lambda,double *x,double *info,int n,double p,Workspace *ws,int positive,double objGap);
int PN_LPp(double *y,double lambda,double *x,double *info,int n,double p,Workspace *ws,int positive);
int PN_LPp(double *y,double lambda,double *x,double *info,int n,double p,Workspace *ws,int positive,double objGap, void* ctx_ptr = NULL,int (*callback)(const double* s_ptr, size_t s_length, double delta_k, void* ctx_ptr)=NULL);
int PN_LPp_v2(double *y,double lambda,double *x,double *info,int n,double p,Workspace *ws,int positive);
/* Lp-ball projections */
int LP1_project(double *y,double lambda,double *x,int n,Workspace *ws);
int LPp_project(double *y,double lambda,double *x,double *info,int n,double p,Workspace *ws);

/* Linear LP constrained solver */
void solveLinearLP(double *z, int n, double p, double lambda, double *s);

#ifdef __cplusplus
}
#endif

#endif
31 changes: 19 additions & 12 deletions src/TV2DWopt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,13 @@
#define LAPACK_ILP64

/* Internal functions */
void DR_proxDiff(size_t n, double* input, double* output, double* W, Workspace *ws);
void DR_columnsPass(size_t M, size_t N, double* input, double* output, double* W, Workspace **ws);
void DR_rowsPass(size_t M, size_t N, double* input, double* output, double* ref, double* W, Workspace **ws);
#ifdef __cplusplus
extern "C" {
#endif

void DR_proxDiff_2DW(size_t n, double* input, double* output, double* W, Workspace *ws);
void DR_columnsPass_2DW(size_t M, size_t N, double* input, double* output, double* W, Workspace **ws);
void DR_rowsPass_2DW(size_t M, size_t N, double* input, double* output, double* ref, double* W, Workspace **ws);

/* DR2L1W_TV
*
Expand Down Expand Up @@ -102,7 +106,7 @@ int DR2L1W_TV(size_t M, size_t N, double*unary, double*W1, double*W2, double*s,
fprintf(DEBUG_FILE,"Dual projection along columns\n"); fflush(DEBUG_FILE);
#endif
// Projection (prox) step
DR_columnsPass(M, N, t, s, W1, ws);
DR_columnsPass_2DW(M, N, t, s, W1, ws);
// Reflection
for (i=0; i < M*N; i++) s[i] = 2*s[i] - t[i];

Expand All @@ -112,7 +116,7 @@ int DR2L1W_TV(size_t M, size_t N, double*unary, double*W1, double*W2, double*s,
fprintf(DEBUG_FILE,"Dual projection along rows\n"); fflush(DEBUG_FILE);
#endif
// Projection (prox) step, taking into account displacemente from reference unary signal
DR_rowsPass(M, N, s, tb, unary, W2, ws);
DR_rowsPass_2DW(M, N, s, tb, unary, W2, ws);
// Reflection
for (i=0; i < M*N; i++) tb[i] = -2*tb[i] - s[i];

Expand All @@ -121,8 +125,8 @@ int DR2L1W_TV(size_t M, size_t N, double*unary, double*W1, double*W2, double*s,
}

// DR is divergent, but with an additional projection we can recover valid solutions
DR_columnsPass(M, N, t, s, W1, ws);
DR_rowsPass(M, N, s, tb, unary, W2, ws);
DR_columnsPass_2DW(M, N, t, s, W1, ws);
DR_rowsPass_2DW(M, N, s, tb, unary, W2, ws);
for (i = 0; i < M*N; i++) s[i] = - s[i] - tb[i];

/* Gather output information */
Expand All @@ -149,7 +153,7 @@ int DR2L1W_TV(size_t M, size_t N, double*unary, double*W1, double*W2, double*s,
@param W regularization weight along cols, as a matrix of size (M-1)xN
@param ws array of Workspaces to use for the computation
*/
void DR_columnsPass(size_t M, size_t N, double* input, double* output, double* W, Workspace **ws) {
void DR_columnsPass_2DW(size_t M, size_t N, double* input, double* output, double* W, Workspace **ws) {
#pragma omp parallel shared(M,N,input,output,W,ws) default(none)
{
int i,j;
Expand All @@ -170,7 +174,7 @@ void DR_columnsPass(size_t M, size_t N, double* input, double* output, double* W
// Prepare inputs
memcpy(wsi->in, input+(M*j), sizeof(double)*M);
// Compute prox difference for this column
DR_proxDiff(M, wsi->in, wsi->out, wline, wsi);
DR_proxDiff_2DW(M, wsi->in, wsi->out, wline, wsi);
// Save output
memcpy(output+(M*j), wsi->out, sizeof(double)*M);
}
Expand All @@ -188,7 +192,7 @@ void DR_columnsPass(size_t M, size_t N, double* input, double* output, double* W
@param W regularization weight along rows, as a matrix of size Mx(N-1)
@param ws array of Workspaces to use for the computation
*/
void DR_rowsPass(size_t M, size_t N, double* input, double* output, double* ref, double* W, Workspace **ws) {
void DR_rowsPass_2DW(size_t M, size_t N, double* input, double* output, double* ref, double* W, Workspace **ws) {
#pragma omp parallel shared(M,N,input,ref,output,W,ws) default(none)
{
int i,j;
Expand All @@ -212,7 +216,7 @@ void DR_rowsPass(size_t M, size_t N, double* input, double* output, double* ref,
for ( idx = j, i = 0 ; i < N ; i++, idx+=M )
wsi->in[i] = ref[idx]-input[idx];
// Compute prox difference for this row
DR_proxDiff(N, wsi->in, wsi->out, wline, wsi);
DR_proxDiff_2DW(N, wsi->in, wsi->out, wline, wsi);
// Save output, recovering displacement from reference signal
for ( idx = j, i = 0 ; i < N ; i++, idx+=M )
output[idx] = wsi->out[i] - ref[idx];
Expand All @@ -229,7 +233,7 @@ void DR_rowsPass(size_t M, size_t N, double* input, double* output, double* ref,
@param W weights of the TV regularization
@param ws Workspace to use for the computation
*/
void DR_proxDiff(size_t n, double* input, double* output, double* W, Workspace *ws) {
void DR_proxDiff_2DW(size_t n, double* input, double* output, double* W, Workspace *ws) {
int i;

// Compute proximity
Expand All @@ -239,3 +243,6 @@ void DR_proxDiff(size_t n, double* input, double* output, double* W, Workspace *
output[i] = input[i] - output[i];
}

#ifdef __cplusplus
}
#endif
Loading