static int32 ipopt_eligible_solver(slv_system_t server){ struct rel_relation **rp; struct var_variable **vp; rel_filter_t rfilt; var_filter_t vfilt; rfilt.matchbits = (REL_CONDITIONAL | REL_INWHEN); rfilt.matchvalue = (REL_CONDITIONAL | REL_INWHEN); vfilt.matchbits = (VAR_BINARY); vfilt.matchvalue = (VAR_BINARY); /// @todo check that there is a MAXIMIZE or MINIMIZE statement if (slv_get_obj_relation(server) == NULL) ERROR_REPORTER_HERE(ASC_USER_ERROR,"No objective function found"); /// @todo check that there are no WHENs or CONDITIONALs for( rp=slv_get_solvers_rel_list(server); *rp != NULL ; ++rp ) { if(rel_apply_filter(*rp,&rfilt)){ ERROR_REPORTER_NOLINE(ASC_USER_ERROR,"WHEN and CONDITIONAL Statements are not supported."); return(FALSE); } } /// @todo check that there are no discrete-valued variables for( vp=slv_get_solvers_var_list(server); *vp != NULL ; ++vp ) { if(var_apply_filter(*vp,&vfilt)){ ERROR_REPORTER_NOLINE(ASC_USER_ERROR,"Discrete Variables not supported."); return(FALSE); } } /// @todo check anything else? return 1; }
int mr_bisect_partition(mr_reorder_t *sys, mtx_region_t *reg, int top, MRBlockReorderF rfunc) { #if MRDEBUG FILE *fptr; static mtx_region_t graph_reg; static int fnum; static char fname[80]; #endif mtx_block_t mb; mtx_block_t *mbptr; mtx_region_t *block; struct rel_relation **rp; mtx_coord_t coord; int partition, newpart,size,threshold, status = 0; int32 k,row,mod=0,rmax,stop,nrows; /* check all our pointers */ if (sys==NULL || sys->slv == NULL || sys->mtx == NULL || sys->local == NULL || sys->active == NULL || sys->tmpmodlist == NULL || sys->activelen == 0 || sys->cutoff < 10 || rfunc == NULL ) { #if MRDEBUG FPRINTF(stderr,"mr_bisect_partition miscalled.\n"); #endif return 1; } rp = slv_get_solvers_rel_list(sys->slv); /* CREATE block list as needed */ if (top==1) { mbptr = &mb; mb.nblocks = 1; mb.block = reg; #if MRDEBUG graph_reg = *reg; fnum=0; #endif } else { mbptr = mtx_block_partition(sys->mtx,reg); if (mbptr == NULL || (mbptr->block == NULL && mbptr->nblocks) ) { if (mbptr != NULL) ascfree(mbptr); return 1; } sys->nblts++; } #if MRDEBUG FPRINTF(stderr,"mtx_bisect_partition2 (%d,%d - %d,%d).\n", reg->row.low,reg->col.low,reg->row.high,reg->col.high); sprintf(fname,"/tmp/bisect1.plot.%d",fnum); fptr = fopen(fname,"w+"); mtx_write_region_plot(fptr,sys->mtx,&graph_reg); fclose(fptr); fnum++; #endif /* tear (or skip) the k remaining partitions */ for (k=0; k < mbptr->nblocks; k++) { block = &(mbptr->block[k]); size = block->row.high - block->row.low +1; /* skip 1x1, 2x2 blocks which must be full */ if (block->row.high - block->row.low < 3) { continue; /* next k */ } /* blocks below cutoff the user reorders instead */ if (size < sys->cutoff) { #if MRDEBUG FPRINTF(stderr,"Calling user reorder on rows %d - %d.\n", block->row.low,block->row.high); #endif rfunc(sys->slv,sys->mtx,block); continue; /* next k */ } /* ok, here's where we do the work */ threshold = size/2; /* we want to divide in approximately half */ rmax = block->row.high; /* tag in block models and count in block relations */ for (row = block->row.low; row <= rmax; row++) { mod = rel_model(rp[mtx_row_to_org(sys->mtx,row)]); if (!(sys->active[mod])) { sys->active[mod] = 1; sys->tmpmodlist[sys->tmpmodused++] = mod; } sys->local[mod]++; } /* if block is all one MODEL, tell user to eat it */ if (sys->tmpmodused < 2) { #if MRDEBUG FPRINTF(stderr,"Found oversized MODEL including relation\n"); rel_write_name(sys->slv,rp[mtx_row_to_org(sys->mtx,rmax)],stderr); FPRINTF(stderr,"\nCalling user reorder on rows %d - %d.\n", block->row.low,block->row.high); #endif /* rezero active and local arrays, just 1! */ sys->local[0] = 0; sys->active[0] = 0; rfunc(sys->slv,sys->mtx,block); continue; /* next k */ } /* sort the MODEL indices in tmpmodlist. using this fact later * enables us to punt the rel_partition flag in favor of just * checking MODEL index of a row against the last MODEL in the * first partition when hunting in incidence for complicating cols. * We might want to reverse things if the tree was numbered top * down rather than bottom up. * It would be nice if we could prove this sort is not needed, * but I haven't had the time yet to try. * A slightly different, probably bigger, data structure * might also allow the sort to be avoided. * Probably we should create a doubly linked list version * of tmpmodlist and insert sorted as it is created, * then map the final thing down to the vector needed. * Who knows? It's a small list in any case. */ qsort(sys->tmpmodlist,sys->tmpmodused,sizeof(int), (int (*)(const void *, const void *))intcompare); /* now try to split tmpmodlist approximately in relation count half */ stop = 0; nrows = 0; /* local[mod] should be the size of the last MODEL in the first half * on loop exit. */ while(stop < sys->tmpmodused && nrows < threshold) { /* stop at the first MODEL division after the halfway point */ mod = sys->tmpmodlist[stop++]; nrows += sys->local[mod]; } /* check if the division BEFORE the halfway point is closer to center * but not at beginning. * This should help with large, centered blocks that OTHERWISE * would put us way past half and result in overtearing. * |------d1---------|t|---------------------d2------------| * d1: threshold-(nrows-local[mod]) d2: nrows-threshold * the abs should not be needed, but to be safe. */ if ( stop > 1 && abs(nrows - threshold) > abs(threshold - (nrows - sys->local[mod])) ) { nrows -= sys->local[mod]; stop--; mod = sys->tmpmodlist[stop-1]; } /* now all relations with MODEL number > mod are in the second partition with MODEL number <= mod are in the first. */ /* now we're going to identify and permute out the tears, diddling with the block. rmax preserves block->row.high */ coord.col = block->row.low; while (coord.col <= block->row.high) { coord.row = mtx_FIRST; partition = -1; /* neither partition seen */ while ( mtx_next_in_col(sys->mtx,&coord,&(block->row)), coord.row != mtx_LAST) { /* check if this column crossed partitions */ newpart = (rel_model(rp[mtx_row_to_org(sys->mtx,coord.row)]) > mod); if (partition >= 0 && partition != newpart) { /* this is a tear. */ sys->ntears++; /* symmetrically permute tear to block outer edge */ mtx_swap_cols(sys->mtx,coord.col,block->row.high); mtx_swap_rows(sys->mtx,coord.col,block->row.high); rel_set_torn(rp[mtx_row_to_org(sys->mtx,block->row.high)],1); /* reduce the block row range, break next_in loop. * with any luck, this stops us from double tearing, though * in a completely arbitrary way. Another covert tie-breaking. */ block->row.high--; break; /* next coord.col. */ } /* it doesn't cross a partition yet, keep looking */ partition = newpart; /* slightly redundant. cheaper than checking */ } coord.col++; } block->col.high = block->row.high; /* rezero active and local arrays so as not to confuse recursion */ while (sys->tmpmodused > 0) { mod = sys->tmpmodlist[--(sys->tmpmodused)]; sys->local[mod] = 0; sys->active[mod] = 0; } /* there are no flags on rels to rezero */ /* attack block left after tearing. */ status += mr_bisect_partition(sys,block,0,rfunc); } /* if block list is locally created, destroy it */ if (!top) { mtx_destroy_blocklist(mbptr); } return status; }
int mr_bisect_partition2(mr_reorder_t *sys, mtx_region_t *reg, int top, MRBlockReorderF rfunc) { #if MRDEBUG FILE *fptr; static mtx_region_t graph_reg; static int fnum; static char fname[80]; #endif mtx_block_t mb; mtx_block_t *mbptr; mtx_region_t *block; struct rel_relation **rp; mtx_coord_t coord; int partition, newpart,size,threshold, status = 0; int32 k,row,mod=0,rmax,stop,nrows,nexttear; /* check all our pointers */ if (sys==NULL || sys->slv == NULL || sys->mtx == NULL || sys->local == NULL || sys->active == NULL || sys->tmpmodlist == NULL || sys->activelen == 0 || sys->cutoff < 10 || rfunc == NULL ) { #if MRDEBUG FPRINTF(stderr,"mr_bisect_partition2 miscalled.\n"); #endif return 1; } rp = slv_get_solvers_rel_list(sys->slv); /* CREATE block list as needed */ if (top==1) { mbptr = &mb; mb.nblocks = 1; mb.block = reg; #if MRDEBUG graph_reg = *reg; fnum=0; #endif } else { #if MRDEBUG FPRINTF(stderr,"mtx_bisect_partition2 (%d,%d - %d,%d).\n", reg->row.low,reg->col.low,reg->row.high,reg->col.high); #endif mbptr = mtx_block_partition(sys->mtx,reg); if (mbptr == NULL || (mbptr->block == NULL && mbptr->nblocks) ) { if (mbptr != NULL) ascfree(mbptr); return 1; } #if MRDEBUG FPRINTF(stderr,"BLT List:\n"); for (k=0;k < mbptr->nblocks; k++) { FPRINTF(stderr,"B: %d,%d - %d,%d\n", mbptr->block[k].row.low,mbptr->block[k].col.low, mbptr->block[k].row.high,mbptr->block[k].col.high); } #endif sys->nblts++; } #if MRDEBUG FPRINTF(stderr,"mtx_bisect_partition2 (%d,%d - %d,%d).\n", reg->row.low,reg->col.low,reg->row.high,reg->col.high); sprintf(fname,"/tmp/bisect2.plot.%d",fnum); fptr = fopen(fname,"w+"); mtx_write_region_plot(fptr,sys->mtx,&graph_reg); fclose(fptr); fnum++; #endif /* tear (or skip) the k remaining partitions */ for (k=0; k < mbptr->nblocks; k++) { block = &(mbptr->block[k]); size = block->row.high - block->row.low +1; /* skip 1x1, 2x2 blocks which must be full */ if (block->row.high - block->row.low < 3) { continue; /* next k */ } /* blocks below cutoff the user reorders instead */ if (size < sys->cutoff) { #if MRDEBUG FPRINTF(stderr,"Calling user reorder on rows %d - %d.\n", block->row.low,block->row.high); #endif rfunc(sys->slv,sys->mtx,block); continue; /* next k */ } /* ok, here's where we do the work */ threshold = size/2; /* we want to divide in approximately half */ rmax = block->row.high; #if MRDEBUG FPRINTF(stderr,"Splitting block with threshold %d.\n", threshold); #endif /* tag in block models and count in block relations */ for (row = block->row.low; row <= rmax; row++) { mod = rel_model(rp[mtx_row_to_org(sys->mtx,row)]); if (!(sys->active[mod])) { sys->active[mod] = 1; sys->tmpmodlist[sys->tmpmodused++] = mod; } sys->local[mod]++; } /* if block is all one MODEL, tell user to eat it */ if (sys->tmpmodused < 2) { #if MRDEBUG FPRINTF(stderr,"Found oversized MODEL including relation\n"); rel_write_name(sys->slv,rp[mtx_row_to_org(sys->mtx,rmax)],stderr); FPRINTF(stderr,"\nCalling user reorder on rows %d - %d.\n", block->row.low,block->row.high); #endif /* rezero active and local arrays, just 1! */ sys->local[0] = 0; sys->active[0] = 0; rfunc(sys->slv,sys->mtx,block); continue; /* next k */ } #if MRDEBUG FPRINTF(stderr,"Number of models in block %d\n",sys->tmpmodused); FPRINTF(stderr,"tearing block %d-%d\n",block->row.low,block->row.high); #endif /* sort the MODEL indices in tmpmodlist. using this fact later * enables us to punt the rel_partition flag in favor of just * checking MODEL index of a row against the last MODEL in the * first partition. * We might want to reverse things if the tree was numbered top * down rather than bottom up. */ qsort(sys->tmpmodlist,sys->tmpmodused,sizeof(int), (int (*)(const void *, const void *))intcompare); /* now try to split tmpmodlist approximately in relation count half */ stop = 0; nrows = 0; while(stop < sys->tmpmodused && nrows < threshold) { mod = sys->tmpmodlist[stop++]; nrows += sys->local[mod]; } /* Now all relations with MODEL number > mod are in the * second partition with MODEL number <= mod are in the first. */ #if MRDEBUG FPRINTF(stderr,"Number of rows in first half %d\n",nrows); FPRINTF(stderr,"Number of models in first half %d\n",stop); #endif /* now we're going to identify and permute out the tears, * diddling with the block. rmax preserves block->row.high */ coord.col = block->row.low; nexttear = block->row.high; while (coord.col <= nexttear) { coord.row = mtx_FIRST; partition = -1; /* neither partition seen */ while ( mtx_next_in_col(sys->mtx,&coord,&(block->row)), coord.row != mtx_LAST) { /* check if this column crossed partitions */ newpart = (rel_model(rp[mtx_row_to_org(sys->mtx,coord.row)]) > mod); #if MRDEBUG if (newpart <0) { FPRINTF(stderr,"unexpect newpart < 0 (%d)\n",coord.row); } #endif if (partition >= 0 && partition != newpart) { /* this is a tear. */ sys->ntears++; /* symmetrically permute tear to block outer edge */ mtx_swap_cols(sys->mtx,coord.col,nexttear); mtx_swap_rows(sys->mtx,coord.col,nexttear); rel_set_torn(rp[mtx_row_to_org(sys->mtx,nexttear)],1); /* reduce the block row range, break next_in loop. * with any luck, this stops us from double tearing, though * in a completely arbitrary way. Another covert tie-breaking. */ #if MRDEBUG FPRINTF(stderr,"tear row %d\n",nexttear); #endif nexttear--; break; /* next coord.col. */ } /* it doesn't cross a partition yet, keep looking */ partition = newpart; /* slightly redundant. cheaper than checking */ } coord.col++; } #if MRDEBUG FPRINTF(stderr,"TORE %d rows\n", block->col.high-nexttear); #endif block->col.high = block->row.high = nexttear; /* rezero active and local arrays so as not to confuse recursion */ while (sys->tmpmodused > 0) { mod = sys->tmpmodlist[--(sys->tmpmodused)]; sys->local[mod] = 0; sys->active[mod] = 0; } /* there are no flags on rels to rezero */ /* attack block left after tearing. */ status += mr_bisect_partition2(sys,block,0,rfunc); } /* if block list is locally created, destroy it */ if (!top) { mtx_destroy_blocklist(mbptr); } return status; }
static SlvClientToken ipopt_create(slv_system_t server, int32 *statusindex){ IpoptSystem *sys; sys = ASC_NEW_CLEAR(IpoptSystem); if(sys==NULL){ *statusindex = 1; return sys; } sys->p.parms = sys->pa; sys->p.dynamic_parms = 0; ipopt_get_default_parameters(server,(SlvClientToken)sys,&(sys->p)); sys->p.whose = (*statusindex); sys->presolved = 0; sys->resolve = 0; sys->n = -1; sys->m = -1; sys->s.ok = TRUE; sys->s.calc_ok = TRUE; sys->s.costsize = 0; sys->s.cost = NULL; /*redundant, but sanity preserving */ sys->s.block.number_of = 1; sys->s.block.current_block = 0; sys->s.block.current_reordered_block = 0; sys->s.block.current_size = 0; sys->s.block.previous_total_size = 0; sys->s.block.iteration = 0; sys->s.block.funcs = 0; sys->s.block.jacs = 0; sys->s.block.cpu_elapsed = 0; sys->s.block.functime = 0; sys->s.block.jactime = 0; sys->s.block.residual = 0; sys->rfilt.matchbits = (REL_INCLUDED | REL_ACTIVE); sys->rfilt.matchvalue = (REL_INCLUDED | REL_ACTIVE); sys->vfilt.matchbits = (VAR_ACTIVE | VAR_INCIDENT | VAR_SVAR | VAR_FIXED); sys->vfilt.matchvalue = (VAR_ACTIVE | VAR_INCIDENT | VAR_SVAR); sys->vlist = slv_get_solvers_var_list(server); sys->rlist = slv_get_solvers_rel_list(server); sys->rtot = slv_get_num_solvers_rels(server); sys->vtot = slv_get_num_solvers_vars(server); sys->obj = slv_get_obj_relation(server); sys->slv = server; /*char *tmp = rel_make_name(sys->slv,sys->obj); //CONSOLE_DEBUG("Objective relation is '%s'",tmp); ASC_FREE(tmp);*/ //CONSOLE_DEBUG("There are %d constraint relations.", sys->rtot); if(sys->vlist == NULL) { ASC_FREE(sys); ERROR_REPORTER_HERE(ASC_PROG_ERR,"IPOPT called with no variables."); *statusindex = -2; return NULL; } if(sys->rlist == NULL && sys->obj == NULL) { ASC_FREE(sys); ERROR_REPORTER_HERE(ASC_PROG_ERR,"IPOPT called with no relations or objective."); *statusindex = -1; return NULL; } /* do nothing with the objective list, pars, bounds, extrels, etc etc */ slv_check_var_initialization(server); *statusindex = 0; return((SlvClientToken)sys); }
//------------------------------------------------------------------------- void Prg_ASCEND::setup() { int n, me, m; int i, j, row_idx, idx; SPMAT *J; int nincidences; const struct var_variable **incidences; // obtain ASCEND system // todo: should check that system can be solved with HQP (e.g. no integers) _nvars = slv_get_num_solvers_vars(_slv_system); _vars = slv_get_solvers_var_list(_slv_system); _nrels = slv_get_num_solvers_rels(_slv_system); _rels = slv_get_solvers_rel_list(_slv_system); _obj = slv_get_obj_relation(_slv_system); // count number of optimization variables and bounds _var_lb = v_resize(_var_lb, _nvars); _var_ub = v_resize(_var_ub, _nvars); _var_asc2hqp = iv_resize(_var_asc2hqp, _nvars); _derivatives = v_resize(_derivatives, _nvars); _var_master_idxs = iv_resize(_var_master_idxs, _nvars); _var_solver_idxs = iv_resize(_var_solver_idxs, _nvars); n = 0; me = 0; m = 0; for (i = 0; i < _nvars; i++) { _var_lb[i] = var_lower_bound(_vars[i]); _var_ub[i] = var_upper_bound(_vars[i]); /* var_write_name(_slv_system, _vars[i], stderr); fprintf(stderr, ":\t%i,\t%g,\t%g\n", var_fixed(_vars[i]), _var_lb[i], _var_ub[i]); */ if (var_fixed(_vars[i])) { _var_asc2hqp[i] = -1; } else { _var_asc2hqp[i] = n++; if (_var_lb[i] == _var_ub[i]) ++me; else { if (_var_lb[i] > -_Inf) ++m; if (_var_ub[i] < _Inf) ++m; } } } // consider bounds as linear constraints (i.e. no Jacobian update) _me_bounds = me; _m_bounds = m; // count number of HQP constraints for (i = 0; i < _nrels; i++) { if (rel_equal(_rels[i])) ++me; // equality constraint else ++m; // inequality constraint } // allocate QP approximation and optimization variables vector _qp->resize(n, me, m); _x = v_resize(_x, n); // allocate sparse structure for bounds // (write constant elements in Jacobians) me = m = 0; for (i = 0; i < _nvars; i++) { idx = _var_asc2hqp[i]; if (idx < 0) continue; if (_var_lb[i] == _var_ub[i]) { row_idx = me++; sp_set_val(_qp->A, row_idx, idx, 1.0); } else { if (_var_lb[i] > -_Inf) { row_idx = m++; sp_set_val(_qp->C, row_idx, idx, 1.0); } if (_var_ub[i] < _Inf) { row_idx = m++; sp_set_val(_qp->C, row_idx, idx, -1.0); } } } // allocate sparse structure for general constraints // (just insert dummy values; actual values are set in update method) for (i = 0; i < _nrels; i++) { if (rel_equal(_rels[i])) { row_idx = me++; J = _qp->A; } else { row_idx = m++; J = _qp->C; } nincidences = rel_n_incidences(_rels[i]); incidences = rel_incidence_list(_rels[i]); for (j = 0; j < nincidences; j++) { idx = _var_asc2hqp[var_sindex(incidences[j])]; if (idx >= 0) sp_set_val(J, row_idx, idx, 1.0); } } // todo: setup sparse structure of Hessian // for now initialize something resulting in dense BFGS update for (j = 0; j < n-1; j++) { sp_set_val(_qp->Q, j, j, 0.0); sp_set_val(_qp->Q, j, j+1, 0.0); } sp_set_val(_qp->Q, j, j, 0.0); }