void dy_orig_soln (double *x, double *y) /* This routine unscales the primal and dual variable values associated with the rows of the active system before returning them to the user. The necessary unscaling is as follows: primal architectural: x<j>S<j> primal logical: x<i>/R<i> dual: y<i>R<i> The vectors are indexed by basis position. This routine isn't really general purpose --- it's called only from dylp_utils:buildsoln and assumes that x and y are already populated with scaled values. It should get a makeover to match the interface conventions of the other routines in the package. Parameters: x: basic primal variables y: dual variables Returns: undefined. */ { int i,j,i_orig,j_orig ; double xi,yi ; const double *rscale,*cscale ; /* Did we scale? If not, return right off. Otherwise, acquire the scaling vectors. */ if (dy_isscaled() == FALSE) return ; dy_scaling_vectors(&rscale,&cscale) ; /* Since we're only dealing with duals and basic primal variables, it suffices to step through the constraints (equivalently, basis positions). */ for (i = 1 ; i <= dy_sys->concnt ; i++) { i_orig = dy_actcons[i] ; j = dy_basis[i] ; xi = x[i] ; if (j <= dy_sys->concnt) xi /= rscale[i_orig] ; else { j_orig = dy_actvars[j] ; xi *= cscale[j_orig] ; } setcleanzero(xi,dy_tols->zero) ; x[i] = xi ; yi = y[i] ; yi *= rscale[i_orig] ; setcleanzero(yi,dy_tols->cost) ; y[i] = yi ; } return ; }
void dy_setfinalstatus (void) /* This code is common to all three start routines (coldstart, warmstart, hotstart). It scans the newly calculated basic variables and assigns them their final status. In the process, it calculates the number of infeasible variables, and the total infeasibility. Parameters: none Returns: undefined */ { int aindx, xkndx ; double xk,lbk,ubk ; # ifdef DYLP_PARANOIA const char *rtnnme = "dy_setfinalstatus" ; # endif # ifndef DYLP_NDEBUG if (dy_opts->print.crash >= 2) dyio_outfmt(dy_logchn,dy_gtxecho,"\n\testablishing final status ...") ; # endif dy_lp->infeas = 0.0 ; dy_lp->infeascnt = 0 ; /* Step through the constraints, and have a look at the basic variable in each position. The paranoid check will complain if the basis is corrupt, but since nothing can go wrong if we're not paranoid, it just complains and moves to the next entry. */ for (aindx = 1 ; aindx <= dy_sys->concnt ; aindx++) { xkndx = dy_basis[aindx] ; xk = dy_xbasic[aindx] ; lbk = dy_sys->vlb[xkndx] ; ubk = dy_sys->vub[xkndx] ; # ifdef DYLP_PARANOIA if (xkndx <= 0 || xkndx > dy_sys->varcnt) { errmsg(303,rtnnme,dy_sys->nme,aindx,1,xkndx,dy_sys->varcnt) ; continue ; } # endif switch (dy_status[xkndx]) { case vstatB: { if (atbnd(xk,lbk)) { dy_status[xkndx] = vstatBLB ; } else if (belowbnd(xk,lbk)) { dy_lp->infeascnt++ ; dy_lp->infeas += lbk-xk ; dy_status[xkndx] = vstatBLLB ; } else if (atbnd(xk,ubk)) { dy_status[xkndx] = vstatBUB ; } else if (abovebnd(xk,ubk)) { dy_lp->infeascnt++ ; dy_lp->infeas += xk-ubk ; dy_status[xkndx] = vstatBUUB ; } break ; } case vstatBFX: { if (!atbnd(xk,lbk)) { if (belowbnd(xk,lbk)) { dy_lp->infeascnt++ ; dy_lp->infeas += lbk-xk ; dy_status[xkndx] = vstatBLLB ; } else { dy_lp->infeascnt++ ; dy_lp->infeas += xk-ubk ; dy_status[xkndx] = vstatBUUB ; } } break ; } } # ifndef DYLP_NDEBUG if (dy_opts->print.crash >= 4) { dyio_outfmt(dy_logchn,dy_gtxecho,"\n\t %s (%d) %s", consys_nme(dy_sys,'v',xkndx,FALSE,NULL),xkndx, dy_prtvstat(dy_status[xkndx])) ; if (lbk > -dy_tols->inf) dyio_outfmt(dy_logchn,dy_gtxecho,", lb = %g",lbk) ; dyio_outfmt(dy_logchn,dy_gtxecho,", val = %g",xk) ; if (ubk < dy_tols->inf) dyio_outfmt(dy_logchn,dy_gtxecho,", ub = %g",ubk) ; if (flgon(dy_status[xkndx],vstatBLLB|vstatBUUB)) { dyio_outfmt(dy_logchn,dy_gtxecho,", infeasibility = ") ; if (flgon(dy_status[xkndx],vstatBLLB)) dyio_outfmt(dy_logchn,dy_gtxecho,"%g",lbk-xk) ; else dyio_outfmt(dy_logchn,dy_gtxecho,"%g",xk-ubk) ; } dyio_outchr(dy_logchn,dy_gtxecho,'.') ; } # endif } setcleanzero(dy_lp->infeas,dy_tols->zero) ; return ; }
void dy_logStatus (lpprob_struct *orig_lp, flags **p_logstat) /* This routine returns the status of the primal logical variables, in row order for the original system. The routine reports out the full set of dylp status codes. It's actually a fair bit of work to get the status right for inactive constraints. Because we're reporting the full set of dylp status codes, and the client might be calling in a situation where the outcome was infeasible or unbounded, we need to calculate the value and assign the appropriate status code. Parameters: orig_lp: the original lp problem p_logstat: (i) vector to hold the status of the primal logical variables; if NULL, a vector of appropriate size will be allocated (o) status of the primal logical variables, in the original system frame of reference Returns: undefined */ { int i,m,i_orig,m_orig ; flags stati ; double rhsi,rhslowi,lhsi,xi,lbi,ubi ; consys_struct *orig_sys ; flags *logstat ; double *x ; char *rtnnme = "dy_logStatus" ; # ifndef DYLP_NDEBUG int v,n_orig ; # endif # ifdef DYLP_PARANOIA if (dy_std_paranoia(orig_lp,rtnnme) == FALSE) { return ; } if (p_logstat == NULL) { errmsg(2,rtnnme,"logstat") ; return ; } # endif orig_sys = orig_lp->consys ; m_orig = orig_sys->concnt ; m = dy_sys->concnt ; /* If we're not playing with a full deck, we'll need the values of the architecturals to determine the appropriate status for the logical. */ x = NULL ; if (m < m_orig) { dy_colPrimals(orig_lp,&x) ; } /* Do we need a vector? */ if (*p_logstat != NULL) { logstat = *p_logstat ; memset(logstat,0,(m_orig+1)*sizeof(flags)) ; } else { logstat = (flags *) CALLOC((m_orig+1),sizeof(flags)) ; } /* Walk the rows of the original system. For active constraints, copy the status of the logical from dy_status. For inactive constraints, we need to actually calculate the value of the logical and assign the appropriate status. This is more work than you'd think, because we need to determine the appropriate bounds for the logical based on the constraint type, and we need to allow for the possibility that the problem was infeasible or unbounded and the logical is not within bounds. We also need to allow for the possibility that dylp deactivated a tight constraint with y<i> = 0. The convention for logicals in the original system is that all have a coefficient of 1.0. Thus we have bounds of (0,infty) for a slack (contypLE), (0,0) for an artificial (contypEQ), (-infty,0) for a surplus (contypGE), and (0,rhs-rhslow) for a bounded slack (contypRNG). */ for (i_orig = 1 ; i_orig <= m_orig ; i_orig++) { if (ACTIVE_CON(i_orig)) { i = dy_origcons[i_orig] ; stati = dy_status[i] ; } else { lhsi = consys_dotrow(orig_sys,i_orig,x) ; rhsi = orig_sys->rhs[i_orig] ; xi = rhsi-lhsi ; setcleanzero(xi,dy_tols->zero) ; lbi = -dy_tols->inf ; ubi = dy_tols->inf ; switch (orig_sys->ctyp[i_orig]) { case contypLE: { lbi = 0.0 ; break ; } case contypEQ: { lbi = 0.0 ; ubi = 0.0 ; break ; } case contypGE: { ubi = 0.0 ; break ; } case contypRNG: { rhslowi = orig_sys->rhslow[i_orig] ; lbi = 0 ; ubi = rhsi-rhslowi ; break ; } case contypNB: { continue ; } default: { errmsg(1,rtnnme,__LINE__) ; break ; } } if (belowbnd(xi,lbi)) { stati = vstatBLLB ; } else if (atbnd(xi,lbi)) { stati = vstatBLB ; } else if (atbnd(xi,ubi)) { stati = vstatBUB ; } else if (abovebnd(xi,ubi)) { stati = vstatBUUB ; } else { stati = vstatB ; } } logstat[i_orig] = stati ; } if (x != NULL) FREE(x) ; # ifndef DYLP_NDEBUG if (dy_opts->print.soln >= 3) { dyio_outfmt(dy_logchn,dy_gtxecho,"\n\trowstat =") ; n_orig = orig_sys->varcnt ; v = 0 ; for (i_orig = 1 ; i_orig <= m_orig ; i_orig++) { if ((++v)%3 == 0) { v = 0 ; dyio_outfmt(dy_logchn,dy_gtxecho,"\n\t ") ; } dyio_outfmt(dy_logchn,dy_gtxecho," (%s %d %s)", consys_nme(orig_sys,'v',i_orig+n_orig,FALSE,NULL),i_orig, dy_prtvstat(logstat[i_orig])) ; } } # endif /* That's it. Return the vector. */ *p_logstat = logstat ; return ; }
void dy_logPrimals (lpprob_struct *orig_lp, double **p_logx) /* This routine returns the values of the primal logical variables, unscaled, in the frame of reference of the original system (i.e., the value of the logical for constraint i is in position i of the vector). Unscaling is straightforward: sc_x<B> = sc_inv(B)sc_b = inv(S<B>)inv(B)inv(R)Rb = inv(S<B>)(inv(B)b) so all that's needed to recover x<B> = inv(B)b is to multiply by S<B>. We just have to remember that for a logical, S<i> = 1/R<i>. It's more work to get the value of the logical for an inactive constraint --- we have to actually calculate b - dot(a<i>,x). Parameters: orig_lp: the original lp problem p_logx: (i) vector to hold the primal logical variables; if NULL, a vector of appropriate size will be allocated (o) values of the primal logical variables, unscaled, in the original system frame of reference Returns: undefined */ { int j,m,i_orig,m_orig ; double xj,lhs ; consys_struct *orig_sys ; double *logx,*x ; bool scaled ; const double *rscale,*cscale ; # ifndef DYLP_NDEBUG int v,n_orig ; # endif # ifdef DYLP_PARANOIA char *rtnnme = "dy_logPrimals" ; if (dy_std_paranoia(orig_lp,rtnnme) == FALSE) { return ; } if (p_logx == NULL) { errmsg(2,rtnnme,"logx") ; return ; } # endif /* Is unscaling required? Acquire the scaling vectors. If we have inactive constraints, we'll need the values of the architecturals in order to calculate the value of the associated logical. */ scaled = dy_isscaled() ; if (scaled == TRUE) { dy_scaling_vectors(&rscale,&cscale) ; } orig_sys = orig_lp->consys ; m_orig = orig_sys->concnt ; m = dy_sys->concnt ; x = NULL ; if (m < m_orig) { dy_colPrimals(orig_lp,&x) ; } /* Do we need a vector? */ if (*p_logx != NULL) { logx = *p_logx ; memset(logx,0,(m_orig+1)*sizeof(double)) ; } else { logx = (double *) CALLOC((m_orig+1),sizeof(double)) ; } /* Walk the rows of the original system. For each constraint that's active, we can obtain the value of the associated logical from dy_x. For each constraint that's inactive, we have to actually calculate the row activity dot(x,a<i>) and do the arithmetic. */ for (i_orig = 1 ; i_orig <= m_orig ; i_orig++) { if (ACTIVE_CON(i_orig)) { j = dy_origcons[i_orig] ; if (scaled == TRUE) { xj = (1/rscale[i_orig])*dy_x[j] ; } else { xj = dy_x[j] ; } } else { lhs = consys_dotrow(orig_sys,i_orig,x) ; xj = orig_sys->rhs[i_orig]-lhs ; } setcleanzero(xj,dy_tols->zero) ; logx[i_orig] = xj ; } if (x != NULL) FREE(x) ; # ifndef DYLP_NDEBUG if (dy_opts->print.soln >= 3) { dyio_outfmt(dy_logchn,dy_gtxecho,"\n\tlogx =") ; n_orig = orig_sys->varcnt ; v = 0 ; for (i_orig = 1 ; i_orig <= m_orig ; i_orig++) { if (logx[i_orig] != 0) { if ((++v)%3 == 0) { v = 0 ; dyio_outfmt(dy_logchn,dy_gtxecho,"\n\t ") ; } dyio_outfmt(dy_logchn,dy_gtxecho," (%d %g %s)", i_orig,logx[i_orig], consys_nme(orig_sys,'v',n_orig+i_orig,FALSE,NULL)) ; } } } # endif /* That's it. Return the vector. */ *p_logx = logx ; return ; }
void dy_rowPrimals (lpprob_struct *orig_lp, double **p_xB, int **p_indB) /* This routine returns the values of the primal basic variables, unscaled, in row (basis) order in the frame of reference of the original system. Unscaling is straightforward: sc_x<B> = sc_inv(B)sc_b = inv(S<B>)inv(B)inv(R)Rb = inv(S<B>)(inv(B)b) so all that's needed to recover x<B> = inv(B)b is to multiply by S<B>. For logicals, recall that S<i> = 1/R<i>. By construction, the basic variable for inactive constraints is the logical for the constraint. Generating beta<i> = [ -a<B,i>inv(B) 1 ] for an inactive row, correcting b<i> for nonbasic, nonzero variables (active and inactive), and calculating dot(beta<i>,b) is a lot of work. Much easier to call colPrimals for the complete solution vector and calculate b<i> - dot(a<i>,x) in the original system. Parameters: orig_lp: the original lp problem p_xB: (i) vector to hold the values of the primal basic variables; if NULL, a vector of appropriate size will be allocated (o) values of the primal basic variables, unscaled, in the original system frame of reference p_indB: (i) vector to hold the indices of the primal basic variables; if NULL, a vector of appropriate size will be allocated (o) indices of the primal basic variables, unscaled, in the original system frame of reference; indices of logical variables are encoded as the negative of the constraint index Returns: undefined */ { int i,j,m,i_orig,j_orig,m_orig,n_orig ; double xj,lhs ; consys_struct *orig_sys ; double *x,*xB ; int *indB ; bool scaled ; const double *rscale,*cscale ; # ifndef DYLP_NDEBUG int v ; # endif # ifdef DYLP_PARANOIA char *rtnnme = "dy_rowPrimals" ; if (dy_std_paranoia(orig_lp,rtnnme) == FALSE) { return ; } if (p_xB == NULL) { errmsg(2,rtnnme,"x") ; return ; } if (p_indB == NULL) { errmsg(2,rtnnme,"x") ; return ; } # endif /* Is unscaling required? Acquire the scaling vectors. If there are inactive constraints, we'll need the primal architecturals in order to calculate the value of the associated (basic) logical. */ scaled = dy_isscaled() ; if (scaled == TRUE) { dy_scaling_vectors(&rscale,&cscale) ; } orig_sys = orig_lp->consys ; n_orig = orig_sys->varcnt ; m_orig = orig_sys->concnt ; m = dy_sys->concnt ; x = NULL ; if (m < m_orig) { dy_colPrimals(orig_lp,&x) ; } /* Do we need vectors? Do the necessary setup. */ if (*p_xB != NULL) { xB = *p_xB ; memset(xB,0,(m_orig+1)*sizeof(double)) ; } else { xB = (double *) CALLOC((m_orig+1),sizeof(double)) ; } if (*p_indB != NULL) { indB = *p_indB ; memset(indB,0,(m_orig+1)*sizeof(int)) ; } else { indB = (int *) CALLOC((m_orig+1),sizeof(int)) ; } /* Walk the constraints of the original system. For each constraint that's active, we can obtain the value from dy_xbasic. For each inactive constraint, we need to calculate the value of the logical. Indices of logicals are recorded in indB as the negative of the constraint index. */ for (i_orig = 1 ; i_orig <= m_orig ; i_orig++) { if (ACTIVE_CON(i_orig)) { i = dy_origcons[i_orig] ; j = dy_basis[i] ; if (j <= m) { j_orig = dy_actcons[j] ; } else { j_orig = dy_actvars[j] ; } if (scaled == TRUE) { if (j <= m) { xj = (1/rscale[j_orig])*dy_xbasic[i] ; } else { xj = cscale[j_orig]*dy_xbasic[i] ; } } else { xj = dy_xbasic[i] ; } if (j <= m) { indB[i_orig] = -j_orig ; } else { indB[i_orig] = j_orig ; } } else { lhs = consys_dotrow(orig_sys,i_orig,x) ; xj = orig_sys->rhs[i_orig]-lhs ; indB[i_orig] = -i_orig ; } setcleanzero(xj,dy_tols->zero) ; xB[i_orig] = xj ; } if (x != NULL) FREE(x) ; # ifndef DYLP_NDEBUG if (dy_opts->print.soln >= 3) { dyio_outfmt(dy_logchn,dy_gtxecho,"\n\txB =") ; v = 0 ; for (i_orig = 1 ; i_orig <= m_orig ; i_orig++) { if ((++v)%3 == 0) { v = 0 ; dyio_outfmt(dy_logchn,dy_gtxecho,"\n\t ") ; } j_orig = indB[i_orig] ; if (j_orig < 0) { j = n_orig-j_orig ; } else { j = j_orig ; } dyio_outfmt(dy_logchn,dy_gtxecho," (%d %g %s %d)", i_orig,xB[i_orig], consys_nme(orig_sys,'v',j,FALSE,NULL),j_orig) ; } } # endif /* That's it. Return the vectors. */ *p_xB = xB ; *p_indB = indB ; return ; }
void dy_colPrimals (lpprob_struct *orig_lp, double **p_x) /* This routine returns the values of the primal architectural variables (basic and nonbasic), unscaled, in the frame of reference of the original system. Unscaling is straightforward. For basic variables, we have sc_x<B> = sc_inv(B)sc_b = inv(S<B>)inv(B)inv(R)Rb = inv(S<B>)(inv(B)b) so all that's needed to recover x<B> = inv(B)b is to multiply by S<B>. Upper and lower bounds on variables have the same scaling (inv(S)). Parameters: orig_lp: the original lp problem p_x: (i) vector to hold the primal architectural variables; if NULL, a vector of appropriate size will be allocated (o) values of the primal architectural variables, unscaled, in the original system frame of reference Returns: undefined */ { int j,j_orig,n_orig ; double xj ; flags statj ; consys_struct *orig_sys ; double *x ; bool scaled ; const double *rscale,*cscale ; char *rtnnme = "dy_colPrimals" ; # ifndef DYLP_NDEBUG int v ; # endif # ifdef DYLP_PARANOIA if (dy_std_paranoia(orig_lp,rtnnme) == FALSE) { return ; } if (p_x == NULL) { errmsg(2,rtnnme,"x") ; return ; } # endif /* Is unscaling required? Acquire the scaling vectors. accordingly. */ scaled = dy_isscaled() ; if (scaled == TRUE) { dy_scaling_vectors(&rscale,&cscale) ; } orig_sys = orig_lp->consys ; n_orig = orig_sys->varcnt ; /* Do we need a vector? */ if (*p_x != NULL) { x = *p_x ; memset(x,0,(n_orig+1)*sizeof(double)) ; } else { x = (double *) CALLOC((n_orig+1),sizeof(double)) ; } /* Walk the columns of the original system. For each variable that's active (basic or nonbasic), we can obtain the value from dy_x and unscale. For each variable that's inactive, we have to do a bit of work to decode the status and look up the appropriate bound value. */ for (j_orig = 1 ; j_orig <= n_orig ; j_orig++) { if (ACTIVE_VAR(j_orig)) { j = dy_origvars[j_orig] ; if (scaled == TRUE) { xj = cscale[j_orig]*dy_x[j] ; } else { xj = dy_x[j] ; } } else { statj = (flags)(-dy_origvars[j_orig]) ; switch (statj) { case vstatNBFX: case vstatNBLB: { xj = orig_sys->vlb[j_orig] ; break ; } case vstatNBUB: { xj = orig_sys->vub[j_orig] ; break ; } case vstatNBFR: { xj = 0 ; break ; } default: { warn(359,rtnnme,orig_sys->nme, consys_nme(orig_sys,'v',j_orig,FALSE,NULL),j_orig, dy_prtvstat(statj)) ; xj = 0.0 ; break ; } } } setcleanzero(xj,dy_tols->zero) ; x[j_orig] = xj ; } # ifndef DYLP_NDEBUG if (dy_opts->print.soln >= 3) { dyio_outfmt(dy_logchn,dy_gtxecho,"\n\tx =") ; v = 0 ; for (j_orig = 1 ; j_orig <= n_orig ; j_orig++) { if (x[j_orig] != 0) { if ((++v)%3 == 0) { v = 0 ; dyio_outfmt(dy_logchn,dy_gtxecho,"\n\t ") ; } dyio_outfmt(dy_logchn,dy_gtxecho," (%d %g %s)", j_orig,x[j_orig], consys_nme(orig_sys,'v',j_orig,FALSE,NULL)) ; } } } # endif /* That's it. Return the vector. */ *p_x = x ; return ; }
void dy_rowDuals (lpprob_struct *orig_lp, double **p_y, bool trueDuals) /* This routine returns the unscaled vector of row duals, commonly referred to as the dual variables, c<B>inv(B). The values are unscaled and returned in a vector matching the original system frame of reference. Duals associated with inactive rows are always zero. In dylp's min primal <=> min dual pairing, the duals have the wrong sign for the true dual variables used by the min dual problem. If you'd prefer that the duals have a sign convention appropriate for the min dual problem, specify trueDuals = false. The relevant bit of unscaling is: sc_y<i> = sc_c<B>sc_inv(B) = c<B>S<B>inv(S<B>)inv(B)inv(R) = c<B>inv(B)inv(R) So, to recover y<i> we need to postmultiply by inv(R). The appropriate row factor is the one associated with the original row. Parameters: orig_lp: the original lp problem p_y: (i) vector to hold the dual variables; if NULL, a vector of appropriate size will be allocated (o) values of the dual variables, unscaled, in the original system frame of reference Returns: undefined */ { int i,m,n,i_orig,m_orig,n_orig ; double yi ; double *y ; consys_struct *orig_sys ; contyp_enum *ctyp ; bool scaled ; const double *rscale,*cscale ; # ifndef DYLP_NDEBUG int j,v ; # endif # ifdef DYLP_PARANOIA char *rtnnme = "dy_rowDuals" ; if (dy_std_paranoia(orig_lp,rtnnme) == FALSE) { return ; } if (p_y == NULL) { errmsg(2,rtnnme,"y") ; return ; } # endif /* Is unscaling required? Acquire the scaling vectors. accordingly. */ scaled = dy_isscaled() ; if (scaled == TRUE) { dy_scaling_vectors(&rscale,&cscale) ; } orig_sys = orig_lp->consys ; n_orig = orig_sys->varcnt ; m_orig = orig_sys->concnt ; n = dy_sys->varcnt ; m = dy_sys->concnt ; ctyp = orig_sys->ctyp ; /* Do we need a vector? */ if (*p_y != NULL) { y = *p_y ; memset(y,0,(m_orig+1)*sizeof(double)) ; } else { y = (double *) CALLOC((m_orig+1),sizeof(double)) ; } /* Step through the constraints of the original system. For active constraints, acquire and unscale the dual value. */ for (i_orig = 1 ; i_orig <= m_orig ; i_orig++) { if (ACTIVE_CON(i_orig)) { i = dy_origcons[i_orig] ; yi = dy_y[i] ; if (scaled == TRUE) { yi *= rscale[i_orig] ; } setcleanzero(yi,dy_tols->cost) ; } else { yi = 0.0 ; } /* The true duals are the negative of the minimisation duals here. */ if (trueDuals == TRUE) y[i_orig] = -yi ; else y[i_orig] = yi ; } # ifndef DYLP_NDEBUG if (dy_opts->print.soln >= 3) { dyio_outfmt(dy_logchn,dy_gtxecho,"\n\ty =") ; v = 0 ; for (i_orig = 1 ; i_orig <= m_orig ; i_orig++) { if (y[i_orig] != 0) { if ((++v)%3 == 0) { v = 0 ; dyio_outfmt(dy_logchn,dy_gtxecho,"\n\t ") ; } i = dy_origcons[i_orig] ; j = dy_basis[i] ; dyio_outfmt(dy_logchn,dy_gtxecho," (%d %g %s %d)", i_orig,y[i_orig], consys_nme(dy_sys,'v',j,FALSE,NULL),j) ; } } } # endif /* That's it. Return the vector. */ *p_y = y ; return ; }
void dy_colDuals (lpprob_struct *orig_lp, double **p_cbar, bool trueDuals) /* Returns the unscaled vector of duals associated with architectural columns (aka reduced costs), in the original system frame of reference. These are the duals associated with implicit bound constraints. See dy_rowDuals for the duals associated with explicit (architectural) constraints. (These latter are the usual notion of dual variables, and also correspond to the reduced costs of logical variables.) In dylp's min primal <=> min dual pairing, the reduced costs have the correct sign for the true dual variables used by the min dual problem, except that the values associated with NBUB variables need to be negated. If you'd prefer that the duals have a sign convention appropriate for a min primal, specify trueDuals = false. The algorithm is to walk the columns of orig_sys, copying over the reduced cost from dy_cbar when the variable is active, otherwise calculting cbar<j> on the spot. For active variables, we have sc_cbar<j> = sc_c<j> - sc_c<B>sc_inv(B)sc_a<j> = c<j>S<j> - c<B>S<B>inv(S<B>)inv(B)inv(R)Ra<j>S<j> = c<j>S<j> - c<B>inv(B)a<j>S<j> = cbar<j>S<j> To unscale sc_cbar<j>, we simply multiply by 1/S<j>, keeping in mind that if x<j> is a logical for row i, the appropriate factor is R<i>. For inactive variables, we calculate dot(y,a<j>) using the scaled version of the original system, which leaves us with the same sc_abar<j>. Why not use the client's original system and the vector of unscaled duals returned by dy_rowDuals? That would certainly be an option. One argument against it is the additional work involved to get the unscaled duals. The other argument is that maximising the independence of the two calculations means that the test routine (which confirms cbar<j> = c<j> - dot(y,a<j>) in the external frame) is marginally more convincing. Parameters: orig_lp: the original lp problem p_cbar: (i) pointer to vector; if NULL, a vector of the appropriate size will be allocated (o) vector of reduced costs trueDuals: true to return values with a sign convention appropriate for the min dual problem, false to use a sign convention that matches the min primal. Returns: undefined */ { int i,j,m,n,i_orig,j_orig,m_orig,n_orig ; flags statj ; consys_struct *orig_sys ; double *orig_y ; consys_struct *scaled_orig_sys ; bool scaled ; const double *rscale,*cscale ; double cbarj ; double *cbar ; # ifdef DYLP_PARANOIA char *rtnnme = "dy_colDuals" ; if (dy_std_paranoia(orig_lp,rtnnme) == FALSE) { return ; } if (p_cbar == NULL) { errmsg(2,rtnnme,"cbar") ; return ; } # endif /* Is unscaling required? Acquire the scaling vectors and set up scaled_orig_sys accordingly. We'll also need the constraint type vector so that we don't overcompensate for >= constraints when returning true duals. */ scaled = dy_isscaled() ; if (scaled == TRUE) { dy_scaling_vectors(&rscale,&cscale) ; scaled_orig_sys = dy_scaled_origsys() ; } else { scaled_orig_sys = NULL ; } orig_sys = orig_lp->consys ; n_orig = orig_sys->varcnt ; m_orig = orig_sys->concnt ; n = dy_sys->varcnt ; m = dy_sys->concnt ; /* Do we need a vector? */ if (*p_cbar != NULL) { cbar = *p_cbar ; memset(cbar,0,(n_orig+1)*sizeof(double)) ; } else { cbar = (double *) CALLOC((n_orig+1),sizeof(double)) ; } /* Make a vector of duals that matches orig_sys, for efficient pricing of inactive columns. */ orig_y = (double *) CALLOC((m_orig+1),sizeof(double)) ; for (i = 1 ; i <= m ; i++) { i_orig = dy_actcons[i] ; orig_y[i_orig] = dy_y[i] ; } /* Get on with the calculation. For an active variable, we can pull the value from dy_cbar. For an inactive variable, we need to calculate dot(y,a<j>). Then we unscale and drop the result into the proper place in the result vector. Since we're starting from orig_sys, we'll never reference a column for a logical variable. */ for (j_orig = 1 ; j_orig <= n_orig ; j_orig++) { if (ACTIVE_VAR(j_orig)) { j = dy_origvars[j_orig] ; statj = getflg(dy_status[j],vstatSTATUS) ; if (flgon(statj,vstatBASIC)) { cbarj = 0.0 ; } else { if (scaled == TRUE) { cbarj = dy_cbar[j]/cscale[j_orig] ; } else { cbarj = dy_cbar[j] ; } } } else { statj = (flags) -dy_origvars[j_orig] ; if (scaled == TRUE) { cbarj = scaled_orig_sys->obj[j_orig] ; cbarj -= consys_dotcol(scaled_orig_sys,j_orig,orig_y) ; cbarj /= cscale[j_orig] ; } else { cbarj = orig_sys->obj[j_orig] ; cbarj -= consys_dotcol(orig_sys,j_orig,orig_y) ; } } setcleanzero(cbarj,dy_tols->cost) ; /* What's our sign convention? If these values are to work with the imaginary true dual problem, we need to flip the sign on variables that are NBUB. If we're just going for the min primal convention, they're already correct. */ if (trueDuals == TRUE) { if (flgon(statj,vstatNBUB)) cbar[j_orig] = -cbarj ; else cbar[j_orig] = cbarj ; } else cbar[j_orig] = cbarj ; } /* Clean up a bit and we're done. */ if (orig_y != NULL) FREE(orig_y) ; *p_cbar = cbar ; return ; }