From 6418a1246937e7f5ea5018a31067247afcf5befd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 21 Mar 2026 21:16:42 +0000 Subject: [PATCH 1/3] Initial plan From b8318135e9969565daa5e394d5aa28134333e199 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 21 Mar 2026 21:30:57 +0000 Subject: [PATCH 2/3] Improve BnB code robustness and efficiency Co-authored-by: darnstrom <55484604+darnstrom@users.noreply.github.com> Agent-Logs-Url: https://github.com/darnstrom/daqp/sessions/563a71cc-c9e9-4483-9ebf-35f6142d2f8d --- src/api.c | 1 + src/bnb.c | 73 +++++++++++++++++++++++++++++-------------------------- 2 files changed, 39 insertions(+), 35 deletions(-) diff --git a/src/api.c b/src/api.c index 8d00ddfe..db2a9453 100644 --- a/src/api.c +++ b/src/api.c @@ -248,6 +248,7 @@ void allocate_daqp_settings(DAQPWorkspace *work){ void free_daqp_bnb(DAQPWorkspace* work){ if(work->bnb != NULL){ + free(work->bnb->bin_ids); free(work->bnb->tree); free(work->bnb->tree_WS); free(work->bnb->fixed_ids); diff --git a/src/bnb.c b/src/bnb.c index 570f7147..a9f25283 100644 --- a/src/bnb.c +++ b/src/bnb.c @@ -5,7 +5,7 @@ int daqp_bnb(DAQPWorkspace* work){ DAQPNode* node; c_float *swp_ptr = NULL; - // Modify upper bound based on absolute/relavtive suboptimality tolerance + // Modify upper bound based on absolute/relative suboptimality tolerance c_float fval_bound0 = work->settings->fval_bound; c_float eps_r = 1/(1+work->settings->rel_subopt); work->settings->fval_bound = (fval_bound0 - work->settings->abs_subopt)*eps_r; @@ -21,6 +21,7 @@ int daqp_bnb(DAQPWorkspace* work){ work->bnb->n_nodes=1; work->bnb->n_clean=work->bnb->neq; + exitflag = DAQP_EXIT_INFEASIBLE; // Start tree exploration while( work->bnb->n_nodes > 0 ){ @@ -28,11 +29,11 @@ int daqp_bnb(DAQPWorkspace* work){ exitflag = daqp_process_node(node,work); // Solve relaxation // Cut conditions if(exitflag==DAQP_EXIT_INFEASIBLE) continue; // Dominance cut - if(exitflag<0) return exitflag; // Inner solver failed => abort + if(exitflag<0) break; // Inner solver failed => exit loop // Find index to branch over branch_id = daqp_get_branch_id(work); - if(branch_id==-1){// Nothing to branch over => integer feasible + if(branch_id==DAQP_EMPTY_IND){// Nothing to branch over => integer feasible work->settings->fval_bound = (0.5*work->fval - work->settings->abs_subopt)*eps_r; swp_ptr=work->xold; work->xold= work->u; work->u=swp_ptr; // Store feasible sol } @@ -43,12 +44,14 @@ int daqp_bnb(DAQPWorkspace* work){ // Exploration completed work->iterations = work->bnb->itercount; - // Correct fval - work->fval = 2*work->settings->fval_bound/eps_r+work->settings->abs_subopt; - work->settings->fval_bound = fval_bound0; - if(swp_ptr==NULL) - return DAQP_EXIT_INFEASIBLE; + if(swp_ptr==NULL){ + work->settings->fval_bound = fval_bound0; + return exitflag < 0 ? exitflag : DAQP_EXIT_INFEASIBLE; + } else{ + // Invert fval_bound = (0.5*fval_best - abs_subopt)*eps_r to recover fval_best + work->fval = 2*work->settings->fval_bound/eps_r + 2*work->settings->abs_subopt; + work->settings->fval_bound = fval_bound0; // Let work->u point to the best feasible solution swp_ptr=work->u; work->u= work->xold; work->xold=swp_ptr; return DAQP_EXIT_OPTIMAL; @@ -69,9 +72,6 @@ int daqp_process_node(DAQPNode* node, DAQPWorkspace* work){ daqp_warmstart_node(node,work); } else{ - //work->bnb->n_clean = node->depth; - //node_cleanup_workspace(work->bnb->n_clean,work); - //warmstart_node(node,work); daqp_add_upper_lower(node->bin_id,work); work->sense[DAQP_REMOVE_LOWER_FLAG(node->bin_id)] |= DAQP_IMMUTABLE; //Equality if(work->sing_ind != DAQP_EMPTY_IND) // Need to cold start to not miss integer feasible @@ -94,32 +94,38 @@ int daqp_process_node(DAQPNode* node, DAQPWorkspace* work){ } int daqp_get_branch_id(DAQPWorkspace* work){ - int i,disp; + int i, j, disp; int branch_id = DAQP_EMPTY_IND; - for(i=0; i < work->bnb->nb; i++){ - // Branch on first inactive constraint - if(DAQP_IS_ACTIVE(work->bnb->bin_ids[i])) continue; - branch_id = work->bnb->bin_ids[i]; - break; - } + int cand_id; + c_float diff, abs_diff, min_frac = DAQP_INF; - if(branch_id == DAQP_EMPTY_IND) return DAQP_EMPTY_IND; // Nothing to branch over (=>integer feasible) + for(i=0; i < work->bnb->nb; i++){ + cand_id = work->bnb->bin_ids[i]; + // Skip fixed binary constraints + if(DAQP_IS_ACTIVE(cand_id)) continue; + + // Compute signed distance from midpoint between bounds + diff = 0.5*(work->dupper[cand_id]+work->dlower[cand_id]); + if(cand_id < work->ms){//Simple bound + if(work->Rinv==NULL) diff -= work->u[cand_id]; //Hessian is identity + else{ + for(j=cand_id,disp=cand_id+DAQP_R_OFFSET(cand_id,work->n);jn;j++) + diff -= work->Rinv[disp++]*work->u[j]; + } + } + else{//General bound + for(j=0,disp=work->n*(cand_id-work->ms);jn;j++) + diff -= work->M[disp++]*work->u[j]; + } - // Determine if upper or lower child should be processed first - // by computing whether the upper or lower bound is closer to be activated - c_float diff = 0.5*(work->dupper[branch_id]+work->dlower[branch_id]); - if(branch_id < work->ms){//Simple bound - if(work->Rinv==NULL) diff-=work->u[branch_id]; //Hessian is identify - else{ - for(i=branch_id,disp=branch_id+DAQP_R_OFFSET(branch_id,work->n);in;i++) - diff-=work->Rinv[disp++]*work->u[i]; + // Select the most fractional variable (smallest absolute deviation from midpoint) + abs_diff = diff < 0 ? -diff : diff; + if(abs_diff < min_frac){ + min_frac = abs_diff; + branch_id = diff < 0 ? cand_id : DAQP_ADD_LOWER_FLAG(cand_id); } } - else{//General bound - for(i=0,disp=work->n*(branch_id-work->ms);in;i++) - diff-=work->M[disp++]*work->u[i]; - } - branch_id = diff<0 ? branch_id : DAQP_ADD_LOWER_FLAG(branch_id); + return branch_id; } @@ -204,9 +210,6 @@ int daqp_add_upper_lower(const int add_id, DAQPWorkspace* work){ void daqp_setup_cold_bnb(DAQPNode* node,DAQPWorkspace *work){ int i; daqp_node_cleanup_workspace(work->bnb->n_clean,work); - work->sing_ind=DAQP_EMPTY_IND; - work->n_active=work->bnb->n_clean; - work->reuse_ind=work->bnb->n_clean; for(i=work->bnb->n_clean - work->bnb->neq; i< node->depth+1;i++){ daqp_add_upper_lower(work->bnb->fixed_ids[i],work); DAQP_SET_IMMUTABLE(DAQP_REMOVE_LOWER_FLAG(work->bnb->fixed_ids[i])); From d5f3aaed18c8f626f21546213295613c133bd2b3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 21 Mar 2026 21:51:33 +0000 Subject: [PATCH 3/3] Revert to first-infeasible branching in daqp_get_branch_id Co-authored-by: darnstrom <55484604+darnstrom@users.noreply.github.com> Agent-Logs-Url: https://github.com/darnstrom/daqp/sessions/2aff5318-cfd3-4539-911b-afd5a86a4ff4 --- src/bnb.c | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/src/bnb.c b/src/bnb.c index a9f25283..ef2c2a96 100644 --- a/src/bnb.c +++ b/src/bnb.c @@ -96,37 +96,32 @@ int daqp_process_node(DAQPNode* node, DAQPWorkspace* work){ int daqp_get_branch_id(DAQPWorkspace* work){ int i, j, disp; int branch_id = DAQP_EMPTY_IND; - int cand_id; - c_float diff, abs_diff, min_frac = DAQP_INF; + c_float diff; for(i=0; i < work->bnb->nb; i++){ - cand_id = work->bnb->bin_ids[i]; + branch_id = work->bnb->bin_ids[i]; // Skip fixed binary constraints - if(DAQP_IS_ACTIVE(cand_id)) continue; + if(DAQP_IS_ACTIVE(branch_id)) continue; // Compute signed distance from midpoint between bounds - diff = 0.5*(work->dupper[cand_id]+work->dlower[cand_id]); - if(cand_id < work->ms){//Simple bound - if(work->Rinv==NULL) diff -= work->u[cand_id]; //Hessian is identity + diff = 0.5*(work->dupper[branch_id]+work->dlower[branch_id]); + if(branch_id < work->ms){//Simple bound + if(work->Rinv==NULL) diff -= work->u[branch_id]; //Hessian is identity else{ - for(j=cand_id,disp=cand_id+DAQP_R_OFFSET(cand_id,work->n);jn;j++) + for(j=branch_id,disp=branch_id+DAQP_R_OFFSET(branch_id,work->n);jn;j++) diff -= work->Rinv[disp++]*work->u[j]; } } else{//General bound - for(j=0,disp=work->n*(cand_id-work->ms);jn;j++) + for(j=0,disp=work->n*(branch_id-work->ms);jn;j++) diff -= work->M[disp++]*work->u[j]; } - // Select the most fractional variable (smallest absolute deviation from midpoint) - abs_diff = diff < 0 ? -diff : diff; - if(abs_diff < min_frac){ - min_frac = abs_diff; - branch_id = diff < 0 ? cand_id : DAQP_ADD_LOWER_FLAG(cand_id); - } + // Branch on the first infeasible binary variable + return diff < 0 ? branch_id : DAQP_ADD_LOWER_FLAG(branch_id); } - return branch_id; + return DAQP_EMPTY_IND; } void daqp_spawn_children(DAQPNode* node, const int branch_id, DAQPWorkspace* work){