![]() | |||||||||||||
| |||||||||||||
Solving a non-linear problem by recursion Description A non-linear problem (quadratic terms in the constraints) is
solved via successive linear
programming (SLP, also referred to as recursion). The constraint coefficients are changed iteratively.
Source Files By clicking on a file name, a preview is opened at the bottom of this page.
RecursiveFinancialPlanning.cpp // (c) 2024-2024 Fair Isaac Corporation /** * Recursion solving a non-linear financial planning problem. The problem is to * solve * <pre> * net(t) = Payments(t) - interest(t) * balance(t) = balance(t-1) - net(t) * interest(t) = (92/365) * balance(t) * interest_rate * where * balance(0) = 0 * balance[T] = 0 * for interest_rate * </pre> */ #include <iostream> #include <xpress.hpp> using namespace xpress; using namespace xpress::objects; using xpress::objects::utils::sum; int const T = 6; /* Data */ /* An INITIAL GUESS as to interest rate x */ double const X = 0.00; /* An INITIAL GUESS as to balances b(t) */ std::vector<double> B{1, 1, 1, 1, 1, 1}; std::vector<double> P{-1000, 0, 0, 0, 0, 0}; /* Payments */ std::vector<double> R{206.6, 206.6, 206.6, 206.6, 206.6, 0}; /* " */ std::vector<double> V{-2.95, 0, 0, 0, 0, 0}; /* " */ struct RecursiveFinancialPlanning { /** The optimizer instance. */ XpressProblem prob; // Variables and constraints std::vector<Variable> b; /**< Balance */ Variable x; /**< Interest rate */ Variable dx; /**< Change to x */ // Constraints that will be modified. std::vector<Inequality> interest; Inequality ctrd; void printIteration(int it, double variation) { auto sol = prob.getSolution(); std::cout << "---------------- Iteration " << it << " ----------------" << std::endl; std::cout << "Objective: " << prob.attributes.getObjVal() << std::endl; std::cout << "Variation: " << variation << std::endl; std::cout << "x: " << x.getValue(sol) << std::endl; std::cout << "----------------------------------------------" << std::endl; } void printProblemSolution() { auto sol = prob.getSolution(); std::cout << "Objective: " << prob.attributes.getObjVal() << std::endl; std::cout << "Interest rate: " << (x.getValue(sol) * 100) << " percent" << std::endl; std::cout << "Variables:" << std::endl << "t"; for (Variable const &v : prob.getVariables()) { std::cout << "[" << v.getName() << ": " << v.getValue(sol) << "] "; } std::cout << std::endl; } /***********************************************************************/ void modFinNLP() { interest = std::vector<Inequality>(T); // Balance b = prob.addVariables(T) .withName("b_%d") .withLB(XPRS_MINUSINFINITY) .toArray(); // Interest rate x = prob.addVariable("x"); // Interest rate change dx = prob.addVariable(XPRS_MINUSINFINITY, XPRS_PLUSINFINITY, ColumnType::Continuous, "dx"); std::vector<Variable> i = prob.addVariables(T).withName("i_%d").toArray(); std::vector<Variable> n = prob.addVariables(T) .withName("n_%d") .withLB(XPRS_MINUSINFINITY) .toArray(); std::vector<Variable> epl = prob.addVariables(T).withName("epl_%d").toArray(); std::vector<Variable> emn = prob.addVariables(T).withName("emn_%d").toArray(); // Fixed variable values i[0].fix(0); b[T - 1].fix(0); // Objective prob.setObjective(sum(epl) + sum(emn), ObjSense::Minimize); // Constraints // net = payments - interest prob.addConstraints(T, [&](auto t) { return (n[t] == (P[t] + R[t] + V[t]) - i[t]) .setName(xpress::format("net_%d", t)); }); // Money balance across periods prob.addConstraints(T, [&](auto t) { if (t > 0) return (b[t] == b[t - 1]).setName(xpress::format("bal_%d", t)); else return (b[t] == 0.0).setName(xpress::format("bal_%d", t)); }); // i(t) = (92/365)*( b(t-1)*X + B(t-1)*dx ) approx. for (int t = 1; t < T; ++t) { LinExpression iepx = LinExpression::create(); iepx.addTerm(b[t - 1], X); iepx.addTerm(dx, B[t - 1]); iepx.addTerm(epl[t], 1.0); iepx.addTerm(emn[t], 1.0); interest[t] = prob.addConstraint((365 / 92.0) * i[t] == iepx) .setName(xpress::format("int_%d", t)); } // x = dx + X ctrd = prob.addConstraint((x == dx + X).setName("def")); prob.writeProb("Recur.lp", "l"); } /**************************************************************************/ /* Recursion loop (repeat until variation of x converges to 0): */ /* save the current basis and the solutions for variables b[t] and x */ /* set the balance estimates B[t] to the value of b[t] */ /* set the interest rate estimate X to the value of x */ /* reload the problem and the saved basis */ /* solve the LP and calculate the variation of x */ /**************************************************************************/ void solveFinNLP() { double variation = 1.0; prob.callbacks.addMessageCallback(XpressProblem::console); prob.controls.setMipLog(0); // Switch automatic cut generation off prob.controls.setCutStrategy(XPRS_CUTSTRATEGY_NONE); // Solve the problem prob.optimize(); if (prob.attributes.getSolStatus() != SolStatus::Optimal) throw std::runtime_error("failed to optimize with status " + to_string(prob.attributes.getSolStatus())); for (int it = 1; variation > 1e-6; ++it) { // Optimization solution auto sol = prob.getSolution(); printIteration(it, variation); printProblemSolution(); // Change coefficients in interest[t] // Note: when inequalities are added to a problem then all variables are // moved to the left-hand side and all constants are moved to the // right-hand side. Since we are changing these extracted inequalities // directly, we have to use negative coefficients below. for (int t = 1; t < T; ++t) { prob.chgCoef(interest[t], dx, -b[t - 1].getValue(sol)); prob.chgCoef(interest[t], b[t - 1], -x.getValue(sol)); } // Change constant term of ctrd ctrd.setRhs(x.getValue(sol)); // Solve the problem prob.optimize(); auto newsol = prob.getSolution(); if (prob.attributes.getSolStatus() != SolStatus::Optimal) throw std::runtime_error("failed to optimize with status " + to_string(prob.attributes.getSolStatus())); variation = fabs(x.getValue(newsol) - x.getValue(sol)); } printProblemSolution(); } }; int main() { RecursiveFinancialPlanning planning; planning.modFinNLP(); planning.solveFinNLP(); return 0; }
| |||||||||||||
© Copyright 2025 Fair Isaac Corporation. |