SolverResult KojimaSolver::solve(const LCP & lcp, vec & x, vec & y) const{ superlu_opts opts; opts.equilibrate = true; opts.permutation = superlu_opts::COLAMD; opts.refine = superlu_opts::REF_SINGLE; vec q = lcp.q; sp_mat M = lcp.M + regularizer * speye(size(lcp.M)); uint N = q.n_elem; assert(N == x.n_elem); assert(N == y.n_elem); // Figure out what is free (-inf,inf) // and what is bound to be non-negative [0,inf) bvec free_vars = lcp.free_vars; uvec bound_idx = find(0 == free_vars); uvec free_idx = find(1 == free_vars); assert(N == bound_idx.n_elem + free_idx.n_elem); uint NB = bound_idx.n_elem; // number of bound vars uint NF = free_idx.n_elem; // number of free vars /* In what follows, the primal variables x are partitioned into free variables and bound variables x = [f;b]' Likewise, the dual variables are partitioned into y = [0,s]' */ /* The Newton system: [M_ff M_fb 0][df] [M_f x + q_f] [M_bf M_bb -I][db] + [M_b x + q_b - s] [0 S B][dv] [u1 + VBe] Where "M_ff" is the free-free block Overwrite the S,B diagonals every iteration*/ // Split M matrix into blocks based on free and bound indicies block_sp_mat M_part = sp_partition(M,free_idx,bound_idx); sp_mat M_recon = block_mat(M_part); assert(PRETTY_SMALL > norm(M_recon - M)); vec qf = q(free_idx); vec qb = q(bound_idx); vec b = x(bound_idx); vec f = x(free_idx); vec s = y(bound_idx); // Build the Newton matrix vector<vector<sp_mat>> block_G; block_G.push_back(block_sp_vec{sp_mat(),sp_mat(),sp_mat(NB,NB)}); block_G.push_back(block_sp_vec{-M_part[0][0],-M_part[0][1],sp_mat()}); block_G.push_back(block_sp_vec{-M_part[1][0],-M_part[1][1],speye(NB,NB)}); // Start iteration double mean_comp, steplen; double sigma = initial_sigma; uint iter; for(iter = 0; iter < max_iter; iter++){ if(verbose or iter_verbose) cout << "---Iteration " << iter << "---" << endl; assert(all(0 == y(free_idx))); // Mean complementarity mean_comp = dot(b,s) / (double) NB; if(mean_comp < comp_thresh) break; block_G[0][1] = spdiag(s); block_G[0][2] = spdiag(b); sp_mat G = block_mat(block_G); assert(size(N + NB,N + NB) == size(G)); // Form RHS from residual and complementarity vec h = vec(N + NB); vec res_f = M_part[0][0]*f + M_part[0][1]*b + qf; vec res_b = M_part[1][0]*f + M_part[1][1]*b + qb - s; h.head(NB) = sigma * mean_comp - b % s; h.subvec(NB,size(res_f)) = res_f; h.tail(NB) = res_b; //Archiver arch; //arch.add_sp_mat("G",G); //arch.add_vec("h",h); //arch.write("test.sys"); // Solve and extract directions vec dir = spsolve(G,h,"superlu",opts); assert((N+NB) == dir.n_elem); vec df = dir.head(NF); vec db = dir.subvec(NF,N-1); assert(NB == db.n_elem); vec ds = dir.tail(NB); vec dir_recon = join_vert(df,join_vert(db,ds)); assert(PRETTY_SMALL > norm(dir_recon-dir)); steplen = steplen_heuristic(b,s,db,ds,0.9); sigma = sigma_heuristic(sigma,steplen); f += steplen * df; b += steplen * db; s += steplen * ds; if(verbose){ double res = norm(join_vert(res_f,res_b)); cout <<"\t Mean complementarity: " << mean_comp <<"\n\t Residual norm: " << res <<"\n\t |df|: " << norm(df) <<"\n\t |db|: " << norm(db) <<"\n\t |ds|: " << norm(ds) <<"\n\t Step length: " << steplen <<"\n\t Centering sigma: " << sigma << endl; } } if(verbose){ cout << "Finished" <<"\n\t Final mean complementarity: " << mean_comp << endl; } x(free_idx) = f; x(bound_idx) = b; y(free_idx).fill(0); y(bound_idx) = s; return SolverResult(x,y,iter); }
SolverResult MultiSolver::solveNaive(MultiSolverInput &input) { Vector location = NLLeastSquareSolver(&input); return SolverResult(location, input.data); }
SolverResult ProjectiveSolver::solve(const PLCP & plcp, vec & x, vec & y, vec & w) const{ sp_mat P = plcp.P; sp_mat U = plcp.U; vec q = plcp.q; uint N = P.n_rows; uint K = P.n_cols; assert(size(P.t()) == size(U)); bvec free_vars = plcp.free_vars; uvec bound_idx = find(0 == free_vars); uvec free_idx = find(1 == free_vars); assert(N == bound_idx.n_elem + free_idx.n_elem); uint NB = bound_idx.n_elem; // number of bound vars uint NF = free_idx.n_elem; // number of free vars assert(NF == accu(conv_to<uvec>::from(free_vars))); assert(N == x.n_elem); assert(N == y.n_elem); assert(K == w.n_elem); assert(ALMOST_ZERO > norm(y(free_idx))); if(verbose) cout << "Free variables: \t" << NF << endl << "Non-neg variables:\t" << NB << endl; sp_mat J = join_vert(sp_mat(NF,NB),speye(NB,NB)); assert(size(N,NB) == size(J)); assert(PRETTY_SMALL > norm(y(free_idx))); if(verbose) cout << "Forming pre-computed products..." << endl; mat PtP = mat(P.t() * P); assert(size(K,K) == size(PtP)); mat PtPU = PtP*U; assert(size(K,N) == size(PtPU)); mat Pt_PtPU = P.t() - PtPU; assert(size(K,N) == size(Pt_PtPU)); mat PtPUP = PtPU * P; assert(size(K,K) == size(PtPUP)); vec Ptq = P.t() * q; if(verbose) cout << "Done..." << endl; double sigma = initial_sigma; uint iter; double mean_comp; for(iter = 0; iter < max_iter; iter++){ if(verbose or iter_verbose) cout << "---Iteration " << iter << "---" << endl; // Mean complementarity vec s = y(bound_idx); vec b = x(bound_idx); mean_comp = dot(s,b) / (double) NB; if(mean_comp < comp_thresh) break; // Generate reduced Netwon system mat C = s+b; vec g = sigma * mean_comp - s % b; assert(NB == g.n_elem); // NB: A,G,and h have opposite sign from python version mat A = Pt_PtPU * J * spdiag(1.0 / C); assert(size(K,NB) == size(A)); mat G = PtPUP + (A * spdiag(s)) * J.t() * P; assert(size(K,K) == size(G)); vec Ptr = P.t() * (J * s) - PtPU*x - Ptq; vec h = Ptr + A*g; assert(K == h.n_elem); // Options don't make much difference vec dw = arma::solve(G+1e-15*eye(K,K),h, solve_opts::equilibrate); assert(K == dw.n_elem); // Recover dy vec Pdw = P * dw; vec JtPdw = J.t() * Pdw; assert(NB == JtPdw.n_elem); vec ds = (g - s % JtPdw) / C; assert(NB == ds.n_elem); // Recover dx vec dx = (J * ds) + (Pdw); assert(N == dx.n_elem); double steplen = steplen_heuristic(x(bound_idx),s,dx(bound_idx),ds,0.9); sigma = sigma_heuristic(sigma,steplen); x += steplen * dx; s += steplen * ds; y(bound_idx) = s; w += steplen * dw; if(verbose){ cout <<"\tMean complementarity: " << mean_comp <<"\n\tStep length: " << steplen <<"\n\tCentering sigma: " << sigma << endl; } } if(verbose){ cout << "Finished" <<"\n\t Final mean complementarity: " << mean_comp << endl; } return SolverResult(x,y,iter); }
// Start the optimization SolverResult SolverGurobi::runOptimizer() { if (!getInitialized()) initialize(); try { // Create Gurobi environment and set parameters GRBEnv env = GRBEnv(); env.set(GRB_IntParam_OutputFlag, 0); GRBModel model = GRBModel(env); // Get problem info int numVars = constraints->getNumVariables(); int numConstraints = constraints->getNumConstraints(); // Get variables auto variables = constraints->getVariables(); // Create array of model variables GRBVar vars[numVars]; for (int i = 0; i < numVars; i++) { //vars[i] = model.addVar(0.0, 1.0, 0.0, GRB_BINARY); // Set variable type char type = GRB_CONTINUOUS; if (variables.at(i)->getType() == VariableType::BINARY) { type = GRB_BINARY; } else if (variables.at(i)->getType() == VariableType::INTEGER) { type = GRB_INTEGER; } vars[i] = model.addVar(variables.at(i)->getLowerBound(), variables.at(i)->getUpperBound(), variables.at(i)->getCost(), type); } // Integrate variables into model model.update(); // Set starting points (does not help much...) for (int i = 0; i < numVars; i++) vars[i].set(GRB_DoubleAttr_Start, variables.at(i)->getValue()); /* * Add constraints Ax <= b (or Ax = b) * by evaluating gradient and build A matrix */ DenseVector x = DenseVector::Zero(numVars); DenseVector dx = constraints->evalJacobian(x); // Get constraint bounds std::vector<double> clb; std::vector<double> cub; constraints->getConstraintBounds(clb,cub); std::vector<int> rowGradient, colGradient; constraints->structureJacobian(rowGradient, colGradient); int nnzJacobian = constraints->getNumNonZerosJacobian(); // Add constraints one row at the time for (int row = 0; row < numConstraints; row++) { // Build constraint GRBLinExpr expr = 0; // Loop through all non-zeros (inefficient) for (int i = 0; i < nnzJacobian; i++) { if (rowGradient.at(i) == row) { int j = colGradient.at(i); expr += dx(i)*vars[j]; } } // Add constraint to model if (clb.at(row) == cub.at(row)) { model.addConstr(expr, GRB_EQUAL, cub.at(row)); } else { model.addConstr(expr, GRB_LESS_EQUAL, cub.at(row)); } } // More efficient method - avoids dense matrix // std::vector<int> rows = {1,1,1,2,2,3,4,4,4,4,4,5}; // std::vector<int>::iterator start,stop; // start = rows.begin(); // stop = start; // while (start != rows.end()) // { // while (stop != rows.end()) // { // if (*stop == *start) // ++stop; // else // break; // } // for (std::vector<int>::iterator it = start; it != stop; ++it) // cout << *it << endl; // start = stop; // } model.update(); assert(numVars == model.get(GRB_IntAttr_NumVars)); assert(numConstraints == model.get(GRB_IntAttr_NumConstrs)); // Optimize model model.optimize(); // Check status int optimstatus = model.get(GRB_IntAttr_Status); if (optimstatus == GRB_INF_OR_UNBD) { model.getEnv().set(GRB_IntParam_Presolve, 0); model.optimize(); optimstatus = model.get(GRB_IntAttr_Status); } // Create result object SolverResult result(SolverStatus::ERROR, INF, std::vector<double>(numVars,0)); // Check Gurobi status if (optimstatus == GRB_OPTIMAL) { result.status = SolverStatus::OPTIMAL; // Get solution info result.objectiveValue = model.get(GRB_DoubleAttr_ObjVal); std::vector<double> optimalSolution; for (int i = 0; i < numVars; i++) { optimalSolution.push_back(vars[i].get(GRB_DoubleAttr_X)); } result.primalVariables = optimalSolution; /* * Reduced costs and constraint duals are * only available for continuous models */ std::vector<double> reducedCosts; std::vector<double> constraintDuals; if (!model.get(GRB_IntAttr_IsMIP)) { for (int i = 0; i < numVars; i++) { // Get reduced costs (related to range constraint duals) reducedCosts.push_back(vars[i].get(GRB_DoubleAttr_RC)); } for (int i = 0; i < numConstraints; i++) { GRBConstr c = model.getConstr(i); double pi = c.get(GRB_DoubleAttr_Pi); constraintDuals.push_back(pi); } } result.lowerBoundDualVariables = reducedCosts; result.upperBoundDualVariables = reducedCosts; result.constraintDualVariables = constraintDuals; return result; } else if (optimstatus == GRB_INFEASIBLE) { result.status = SolverStatus::INFEASIBLE; result.objectiveValue = INF; // compute and write out IIS // model.computeIIS(); // model.write("problem.lp"); return result; } else if (optimstatus == GRB_UNBOUNDED) { result.status = SolverStatus::UNBOUNDED; result.objectiveValue = -INF; return result; } else { result.status = SolverStatus::ERROR; result.objectiveValue = INF; return result; } } catch(GRBException e) { cout << "SolverGurobi: Error code = " << e.getErrorCode() << endl; cout << e.getMessage() << endl; return SolverResult(SolverStatus::ERROR, INF, std::vector<double>(constraints->getNumVariables(),0)); } catch (...) { cout << "SolverGurobi: Error during optimization!" << endl; return SolverResult(SolverStatus::ERROR, INF, std::vector<double>(constraints->getNumVariables(),0)); } }