Initializing help system before first use

Folio - Examples from 'Getting started'


Type: Portfolio optimization
Rating: 2 (easy-medium)
Description:
  • Chapter 10 Inputting and Solving a Linear Programming problem
    • foliolp.cpp: modeling and solving a small LP problem
    • folioinit.cpp: performing explicit initialization
    • foliodata.cpp (data file: foliocpplp.dat): data input from file
  • Chapter 11 Mixed Integer Programming
    • foliomip1.cpp: modeling and solving a small MIP problem (binary variables)
    • foliomip2.cpp: modeling and solving a small MIP problem (semi-continuous variables)
  • Chapter 12 Quadratic Programming
    • folioqp.cppmodeling and solving a QP and a MIQP problem
    • folioqc.cpp (data file: foliocppqp.dat): modeling and solving a QCQP
  • Chapter 13 Heuristics
    • folioheur.cpp: heuristic solution of a MIP problem
File(s): foliolp.cpp, folioinit.cpp, foliodata.cpp, foliomip1.cpp, foliomip2.cpp, folioqp.cpp, folioheur.cpp
Data file(s): foliocpplp.dat, foliocppqp.dat

foliolp.cpp
/********************************************************
  Xpress BCL C++ Example Problems
  ===============================

  file foliolp.cpp
  ````````````````
  Modeling a small LP problem 
  to perform portfolio optimization.

  (c) 2008 Fair Isaac Corporation
      author: S.Heipcke, Aug. 2003, rev. Mar. 2011
********************************************************/

#include <iostream>
#include "xprb_cpp.h"

using namespace std;
using namespace ::dashoptimization;

#define NSHARES 10                   // Number of shares
#define NRISK 5                      // Number of high-risk shares
#define NNA 4                        // Number of North-American shares

double RET[] = {5,17,26,12,8,9,7,6,31,21};  // Estimated return in investment
int RISK[] = {1,2,3,8,9};            // High-risk values among shares
int NA[] = {0,1,2,3};                // Shares issued in N.-America

int main(int argc, char **argv)
{
 int s;
 XPRBprob p("FolioLP");              // Initialize a new problem in BCL
 XPRBexpr Risk,Na,Return,Cap;
 XPRBvar frac[NSHARES];              // Fraction of capital used per share

// Create the decision variables
 for(s=0;s<NSHARES;s++) frac[s] = p.newVar("frac");  //, XPRB_PL, 0, 0.3);
 
// Objective: total return
 for(s=0;s<NSHARES;s++) Return += RET[s]*frac[s]; 
 p.setObj(Return);                   // Set the objective function

// Limit the percentage of high-risk values
 for(s=0;s<NRISK;s++) Risk += frac[RISK[s]]; 
 p.newCtr("Risk", Risk <= 1.0/3);

/* Equivalent:
 XPRBctr CRisk;
 CRisk = p.newCtr("Risk");
 for(s=0;s<NRISK;s++) CRisk.addTerm(frac[RISK[s]], 1);
 CRisk.setType(XPRB_L);
 CRisk.addTerm(1.0/3);
*/

// Minimum amount of North-American values
 for(s=0;s<NNA;s++) Na += frac[NA[s]]; 
 p.newCtr("NA", Na >= 0.5);

// Spend all the capital
 for(s=0;s<NSHARES;s++) Cap += frac[s]; 
 p.newCtr("Cap", Cap == 1);
 
// Upper bounds on the investment per share
 for(s=0;s<NSHARES;s++) frac[s].setUB(0.3);

// Export matrix to a file
/* p.exportProb(XPRB_MPS, "Folio");
 p.setSense(XPRB_MAXIM);
 p.exportProb(XPRB_LP, "Folio");
*/

// Disable all BCL and Optimizer message printing, except error messages
// p.setMsgLevel(1);

// Solve the problem
 p.setSense(XPRB_MAXIM);
 p.lpOptimize("");

 char *LPSTATUS[] = {"not loaded", "optimal", "infeasible", 
          "worse than cutoff", "unfinished", "unbounded", "cutoff in dual",
	  "unsolved", "nonconvex"};
 
 cout << "Problem status: " << LPSTATUS[p.getLPStat()] << endl;
 
// Solution printing
 cout << "Total return: " << p.getObjVal() << endl;
 for(s=0;s<NSHARES;s++) 
  cout << s << ": " << frac[s].getSol()*100 << "%" << endl;  

 return 0;
} 

folioinit.cpp
/********************************************************
  Xpress BCL C++ Example Problems
  ===============================

  file folioinit.cpp
  ``````````````````
  Modeling a small LP problem 
  to perform portfolio optimization.
  Explicit initialization.

  (c) 2008 Fair Isaac Corporation
      author: S.Heipcke, Aug. 2003, rev. Mar. 2011
********************************************************/

#include <iostream>
#include "xprb_cpp.h"

using namespace std;
using namespace ::dashoptimization;

#define NSHARES 10                   // Number of shares
#define NRISK 5                      // Number of high-risk shares
#define NNA 4                        // Number of North-American shares

double RET[] = {5,17,26,12,8,9,7,6,31,21};  // Estimated return in investment
int RISK[] = {1,2,3,8,9};            // High-risk values among shares
int NA[] = {0,1,2,3};                // Shares issued in N.-America

void solveProb()
{
 int s;
 XPRBprob p("FolioLP");              // Initialize a new problem in BCL
 XPRBexpr Risk,Na,Return,Cap;
 XPRBvar frac[NSHARES];              // Fraction of capital used per share

// Create the decision variables
 for(s=0;s<NSHARES;s++) frac[s] = p.newVar("frac");
 
// Objective: total return
 for(s=0;s<NSHARES;s++) Return += RET[s]*frac[s]; 
 p.setObj(Return);                   // Set the objective function

// Limit the percentage of high-risk values
 for(s=0;s<NRISK;s++) Risk += frac[RISK[s]]; 
 p.newCtr("Risk", Risk <= 1.0/3);

// Minimum amount of North-American values
 for(s=0;s<NNA;s++) Na += frac[NA[s]]; 
 p.newCtr("NA", Na >= 0.5);

// Spend all the capital
 for(s=0;s<NSHARES;s++) Cap += frac[s]; 
 p.newCtr("Cap", Cap == 1);
 
// Upper bounds on the investment per share
 for(s=0;s<NSHARES;s++) frac[s].setUB(0.3);

// Solve the problem
 p.setSense(XPRB_MAXIM);
 p.lpOptimize("");

 char *LPSTATUS[] = {"not loaded", "optimal", "infeasible", 
          "worse than cutoff", "unfinished", "unbounded", "cutoff in dual",
	  "unsolved", "nonconvex"};
 
 cout << "Problem status: " << LPSTATUS[p.getLPStat()] << endl;
 
// Solution printing
 cout << "Total return: " << p.getObjVal() << endl;
 for(s=0;s<NSHARES;s++) 
  cout << s << ": " << frac[s].getSol()*100 << "%" << endl;  
}

int main(int argc, char **argv)
{
 if(XPRB::init() != 0)
 {
   cout << "Initialization failed." << endl;
   return 1;
 }
 solveProb();
 return 0;
} 

foliodata.cpp
/********************************************************
  Xpress BCL C++ Example Problems
  ===============================

  file foliodata.cpp
  ``````````````````
  Modeling a small LP problem 
  to perform portfolio optimization.
  -- Data input from file --

  (c) 2008 Fair Isaac Corporation
      author: S.Heipcke, Aug. 2003, rev. Mar. 2011
********************************************************/

#include <iostream>
#include <cstdio>
#include "xprb_cpp.h"

using namespace std;
using namespace ::dashoptimization;

#define DATAFILE "foliocpplp.dat"

#define NSHARES 10                   // Number of shares
#define NRISK 5                      // Number of high-risk shares
#define NNA 4                        // Number of North-American shares

double RET[NSHARES];                 // Estimated return in investment
char RISK[][100] = {"hardware", "theater", "telecom", "software", 
                    "electronics"};  // High-risk values among shares
char NA[][100] = {"treasury", "hardware", "theater", "telecom"};   
                                     // Shares issued in N.-America

XPRBindexSet SHARES;                 // Set of shares 

XPRBprob p("FolioLP");               // Initialize a new problem in BCL

void readData(void)
{
 double value;
 int s;
 FILE *datafile;
 char name[100];

 SHARES=p.newIndexSet("Shares",NSHARES);  // Create the `SHARES' index set

// Read `RET' data from file
 datafile=fopen(DATAFILE,"r");
 for(s=0;s<NSHARES;s++)
 {
  XPRBreadlinecb(XPRB_FGETS, datafile, 200, "T g", name, &value);
  RET[SHARES+=name]=value;
 }
 fclose(datafile);
 
 SHARES.print();                     // Print out the set contents 
}

int main(int argc, char **argv)
{
 int s;
 XPRBexpr Risk,Na,Return,Cap;
 XPRBvar frac[NSHARES];              // Fraction of capital used per share

// Read data from file
 readData();

// Create the decision variables
 for(s=0;s<NSHARES;s++) frac[s] = p.newVar("frac");
 
// Objective: total return
 for(s=0;s<NSHARES;s++) Return += RET[s]*frac[s]; 
 p.setObj(Return);                   // Set the objective function

// Limit the percentage of high-risk values
 for(s=0;s<NRISK;s++) Risk += frac[SHARES[RISK[s]]]; 
 p.newCtr("Risk", Risk <= 1.0/3);

// Minimum amount of North-American values
 for(s=0;s<NNA;s++) Na += frac[SHARES[NA[s]]]; 
 p.newCtr("NA", Na >= 0.5);

// Spend all the capital
 for(s=0;s<NSHARES;s++) Cap += frac[s]; 
 p.newCtr("Cap", Cap == 1);
 
// Upper bounds on the investment per share
 for(s=0;s<NSHARES;s++) frac[s].setUB(0.3);

// Solve the problem
 p.setSense(XPRB_MAXIM);
 p.lpOptimize("");
 
// Solution printing
 cout << "Total return: " << p.getObjVal() << endl;
 for(s=0;s<NSHARES;s++) 
  cout << SHARES[s] << ": " << frac[s].getSol()*100 << "%" << endl;  

 return 0;
} 

foliomip1.cpp
/********************************************************
  Xpress BCL C++ Example Problems
  ===============================

  file foliomip1.cpp
  ``````````````````
  Modeling a small MIP problem 
  to perform portfolio optimization.
   -- Limiting the total number of assets --

  (c) 2008 Fair Isaac Corporation
      author: S.Heipcke, Aug. 2003, rev. Mar. 2011
********************************************************/

#include <iostream>
#include "xprb_cpp.h"

using namespace std;
using namespace ::dashoptimization;

#define MAXNUM 4                   // Max. number of different assets

#define NSHARES 10                 // Number of shares
#define NRISK 5                    // Number of high-risk shares
#define NNA 4                      // Number of North-American shares

double RET[] = {5,17,26,12,8,9,7,6,31,21};  // Estimated return in investment
int RISK[] = {1,2,3,8,9};          // High-risk values among shares
int NA[] = {0,1,2,3};              // Shares issued in N.-America

int main(int argc, char **argv)
{
 int s;
 XPRBprob p("FolioMIP1");          // Initialize a new problem in BCL
 XPRBexpr Risk,Na,Return,Cap,Num;
 XPRBvar frac[NSHARES];            // Fraction of capital used per share
 XPRBvar buy[NSHARES];             // 1 if asset is in portfolio, 0 otherwise

// Create the decision variables (including upper bounds for `frac')
 for(s=0;s<NSHARES;s++) 
 {
  frac[s] = p.newVar("frac", XPRB_PL, 0, 0.3);
  buy[s] = p.newVar("buy", XPRB_BV);
 } 
 
// Objective: total return
 for(s=0;s<NSHARES;s++) Return += RET[s]*frac[s]; 
 p.setObj(Return);                // Set the objective function

// Limit the percentage of high-risk values
 for(s=0;s<NRISK;s++) Risk += frac[RISK[s]]; 
 p.newCtr(Risk <= 1.0/3);

// Minimum amount of North-American values
 for(s=0;s<NNA;s++) Na += frac[NA[s]]; 
 p.newCtr(Na >= 0.5);

// Spend all the capital
 for(s=0;s<NSHARES;s++) Cap += frac[s]; 
 p.newCtr(Cap == 1);

// Limit the total number of assets
 for(s=0;s<NSHARES;s++) Num += buy[s];
 p.newCtr(Num <= MAXNUM);

// Linking the variables
 for(s=0;s<NSHARES;s++) p.newCtr(frac[s] <= buy[s]);

// Solve the problem
 p.setSense(XPRB_MAXIM);
 p.mipOptimize("");


 char *MIPSTATUS[] = {"not loaded", "not optimized", "LP optimized",  
		      "unfinished (no solution)", 
		      "unfinished (solution found)", "infeasible", "optimal",
                      "unbounded"};
 
 cout << "Problem status: " << MIPSTATUS[p.getMIPStat()] << endl;

 
// Solution printing
 cout << "Total return: " << p.getObjVal() << endl;
 for(s=0;s<NSHARES;s++) 
  cout << s << ": " << frac[s].getSol()*100 << "% (" << buy[s].getSol()
       << ")" << endl;  

 return 0;
} 

foliomip2.cpp
/********************************************************
  Xpress BCL C++ Example Problems
  ===============================

  file foliomip2.cpp
  ``````````````````
  Modeling a small MIP problem 
  to perform portfolio optimization.
   -- Imposing a minimum investment per share --

  (c) 2008 Fair Isaac Corporation
      author: S.Heipcke, Aug. 2003, rev. Mar. 2011
********************************************************/

#include <iostream>
#include "xprb_cpp.h"

using namespace std;
using namespace ::dashoptimization;

#define NSHARES 10                   // Number of shares
#define NRISK 5                      // Number of high-risk shares
#define NNA 4                        // Number of North-American shares

double RET[] = {5,17,26,12,8,9,7,6,31,21};  // Estimated return in investment
int RISK[] = {1,2,3,8,9};            // High-risk values among shares
int NA[] = {0,1,2,3};                // Shares issued in N.-America

int main(int argc, char **argv)
{
 int s;
 XPRBprob p("FolioSC");              // Initialize a new problem in BCL
 XPRBexpr Risk,Na,Return,Cap;
 XPRBvar frac[NSHARES];              // Fraction of capital used per share

// Create the decision variables
 for(s=0;s<NSHARES;s++)
 {
  frac[s] = p.newVar("frac", XPRB_SC, 0, 0.3);
  frac[s].setLim(0.1);
 } 
 
// Objective: total return
 for(s=0;s<NSHARES;s++) Return += RET[s]*frac[s]; 
 p.setObj(Return);                   // Set the objective function

// Limit the percentage of high-risk values
 for(s=0;s<NRISK;s++) Risk += frac[RISK[s]]; 
 p.newCtr(Risk <= 1.0/3);

// Minimum amount of North-American values
 for(s=0;s<NNA;s++) Na += frac[NA[s]]; 
 p.newCtr(Na >= 0.5);

// Spend all the capital
 for(s=0;s<NSHARES;s++) Cap += frac[s]; 
 p.newCtr(Cap == 1);

// Solve the problem
 p.setSense(XPRB_MAXIM);
 p.mipOptimize("");
 
// Solution printing
 cout << "Total return: " << p.getObjVal() << endl;
 for(s=0;s<NSHARES;s++) 
  cout << s << ": " << frac[s].getSol()*100 << "%" << endl;  

 return 0;
} 

folioqp.cpp
/********************************************************
  Xpress BCL C++ Example Problems
  ===============================

  file folioqp.cpp
  ````````````````
  Modeling a small QP problem 
  to perform portfolio optimization.
   -- 1. QP: minimize variance
      2. MIQP: limited number of assets ---

  (c) 2008 Fair Isaac Corporation
      author: S.Heipcke, Aug. 2003, rev. Mar. 2011
********************************************************/

#include <iostream>
#include <cstdio>
#include "xprb_cpp.h"

using namespace std;
using namespace ::dashoptimization;

#define DATAFILE "foliocppqp.dat"

#define TARGET 9                   // Target yield
#define MAXNUM 4                   // Max. number of different assets

#define NSHARES 10                 // Number of shares
#define NNA 4                      // Number of North-American shares

double RET[] = {5,17,26,12,8,9,7,6,31,21};  // Estimated return in investment
int NA[] = {0,1,2,3};              // Shares issued in N.-America
double VAR[NSHARES][NSHARES];      // Variance/covariance matrix of
                                   // estimated returns

int main(int argc, char **argv)
{
 int s,t;
 XPRBprob p("FolioQP");            // Initialize a new problem in BCL
 XPRBexpr Na,Return,Cap,Num,Variance;
 XPRBvar frac[NSHARES];            // Fraction of capital used per share
 XPRBvar buy[NSHARES];             // 1 if asset is in portfolio, 0 otherwise
 FILE *datafile;

// Read `VAR' data from file
 datafile=fopen(DATAFILE,"r");
 for(s=0;s<NSHARES;s++)
  XPRBreadarrlinecb(XPRB_FGETS, datafile, 200, "g ", VAR[s], NSHARES);
 fclose(datafile);

// **** First problem: unlimited number of assets ****

// Create the decision variables
 for(s=0;s<NSHARES;s++) 
  frac[s] = p.newVar(XPRBnewname("frac(%d)",s+1), XPRB_PL, 0, 0.3);
 
// Objective: mean variance
 for(s=0;s<NSHARES;s++)
  for(t=0;t<NSHARES;t++) Variance += VAR[s][t]*frac[s]*frac[t]; 
 p.setObj(Variance);                 // Set the objective function

// Minimum amount of North-American values
 for(s=0;s<NNA;s++) Na += frac[NA[s]]; 
 p.newCtr(Na >= 0.5);

// Spend all the capital
 for(s=0;s<NSHARES;s++) Cap += frac[s]; 
 p.newCtr(Cap == 1);
 
// Target yield 
 for(s=0;s<NSHARES;s++) Return += RET[s]*frac[s]; 
 p.newCtr(Return >= TARGET);

// Solve the problem
 p.setSense(XPRB_MINIM);
 p.lpOptimize("");
 
// Solution printing
 cout << "With a target of " << TARGET << " minimum variance is " << 
         p.getObjVal() << endl;
 for(s=0;s<NSHARES;s++) 
  cout << s << ": " << frac[s].getSol()*100 << "%" << endl;  

// **** Second problem: limit total number of assets ****

// Create the decision variables
 for(s=0;s<NSHARES;s++) 
  buy[s] = p.newVar(XPRBnewname("buy(%d)",s+1), XPRB_BV);
 
// Limit the total number of assets
 for(s=0;s<NSHARES;s++) Num += buy[s];
 p.newCtr(Num <= MAXNUM);

// Linking the variables
 for(s=0;s<NSHARES;s++) p.newCtr(frac[s] <= buy[s]);

// Solve the problem
 p.mipOptimize("");
 
// Solution printing
 cout << "With a target of " << TARGET << " and at most " << MAXNUM <<
         " assets, minimum variance is " << p.getObjVal() << endl;
 for(s=0;s<NSHARES;s++) 
  cout << s << ": " << frac[s].getSol()*100 << "% (" << buy[s].getSol()
       << ")" << endl;  

 return 0;
} 


folioheur.cpp
/********************************************************
  Xpress BCL C++ Example Problems
  ===============================

  file folioheur.cpp
  ``````````````````
  Modeling a small MIP problem 
  to perform portfolio optimization.
   -- Heuristic solution --

  (c) 2008 Fair Isaac Corporation
      author: S.Heipcke, Aug. 2003, rev. Mar. 2011
********************************************************/

#include <iostream>
#include "xprb_cpp.h"
#include "xprs.h"

using namespace std;
using namespace ::dashoptimization;

#define MAXNUM 4                   // Max. number of shares to be selected

#define NSHARES 10                 // Number of shares
#define NRISK 5                    // Number of high-risk shares
#define NNA 4                      // Number of North-American shares

void solveHeur();

double RET[] = {5,17,26,12,8,9,7,6,31,21};  // Estimated return in investment
int RISK[] = {1,2,3,8,9};          // High-risk values among shares
int NA[] = {0,1,2,3};              // Shares issued in N.-America

XPRBprob p("FolioMIPHeur");        // Initialize a new problem in BCL
XPRBvar frac[NSHARES];             // Fraction of capital used per share
XPRBvar buy[NSHARES];              // 1 if asset is in portfolio, 0 otherwise

int main(int argc, char **argv)
{
 int s;
 XPRBexpr Risk,Na,Return,Cap,Num;

// Create the decision variables (including upper bounds for `frac')
 for(s=0;s<NSHARES;s++) 
 {
  frac[s] = p.newVar("frac", XPRB_PL, 0, 0.3);
  buy[s] = p.newVar("buy", XPRB_BV);
 } 
 
// Objective: total return
 for(s=0;s<NSHARES;s++) Return += RET[s]*frac[s]; 
 p.setObj(Return);                 // Set the objective function

// Limit the percentage of high-risk values
 for(s=0;s<NRISK;s++) Risk += frac[RISK[s]]; 
 p.newCtr(Risk <= 1.0/3);

// Minimum amount of North-American values
 for(s=0;s<NNA;s++) Na += frac[NA[s]]; 
 p.newCtr(Na >= 0.5);

// Spend all the capital
 for(s=0;s<NSHARES;s++) Cap += frac[s]; 
 p.newCtr(Cap == 1);

// Limit the total number of assets
 for(s=0;s<NSHARES;s++) Num += buy[s];
 p.newCtr(Num <= MAXNUM);

// Linking the variables
 for(s=0;s<NSHARES;s++) p.newCtr(frac[s] <= buy[s]);

// Solve problem heuristically
 p.setSense(XPRB_MAXIM);
 solveHeur();

// Solve the problem
 p.mipOptimize("");
 
// Solution printing
 if(p.getMIPStat()==XPRB_MIP_SOLUTION || p.getMIPStat()==XPRB_MIP_OPTIMAL)
 {
  cout << "Exact solution: Total return: " << p.getObjVal() << endl;
  for(s=0;s<NSHARES;s++) 
   cout << s << ": " << frac[s].getSol()*100 << "%" << endl;
 }    
 else
  cout << "Heuristic solution is optimal." << endl;

 return 0;
} 

void solveHeur()
{
 XPRBbasis basis;
 int s, ifgsol;
 double solval, fsol[NSHARES],TOL;

 XPRSsetintcontrol(p.getXPRSprob(), XPRS_CUTSTRATEGY, 0); 
                                   // Disable automatic cuts
 XPRSsetintcontrol(p.getXPRSprob(), XPRS_PRESOLVE, 0);
                                   // Switch presolve off
 XPRSgetdblcontrol(p.getXPRSprob(), XPRS_FEASTOL, &TOL);
                                   // Get feasibility tolerance

 p.mipOptimize("l");               // Solve the LP-relaxation
 basis=p.saveBasis();              // Save the current basis

// Fix all variables `buy' for which `frac' is at 0 or at a relatively
// large value
 for(s=0;s<NSHARES;s++)
 {
  fsol[s]=frac[s].getSol();        // Get the solution values of `frac'
  if(fsol[s] < TOL)  buy[s].setUB(0);
  else if(fsol[s] > 0.2-TOL)  buy[s].setLB(1);
 }

 p.mipOptimize("c");               // Solve the MIP-problem
 ifgsol=0;
 if(p.getMIPStat()==XPRB_MIP_SOLUTION || p.getMIPStat()==XPRB_MIP_OPTIMAL)
 {                                 // If an integer feas. solution was found
  ifgsol=1;
  solval=p.getObjVal();            // Get the value of the best solution
  cout << "Heuristic solution: Total return: " << p.getObjVal() << endl;
  for(s=0;s<NSHARES;s++) 
   cout << s << ": " << frac[s].getSol()*100 << "%" << endl;  
 }

// XPRSpostsolve(p.getXPRSprob());   // Re-initialize the global search

// Reset variables to their original bounds
 for(s=0;s<NSHARES;s++)
  if((fsol[s] < TOL) || (fsol[s] > 0.2-TOL))
  {
   buy[s].setLB(0);
   buy[s].setUB(1);
  }

 p.loadBasis(basis);        /* Load the saved basis: bound changes are
                               immediately passed on from BCL to the
                               Optimizer if the problem has not been modified
                               in any other way, so that there is no need to 
                               reload the matrix */
 basis.reset();             // No need to store the saved basis any longer 
 if(ifgsol==1)
  XPRSsetdblcontrol(p.getXPRSprob(), XPRS_MIPABSCUTOFF, solval+TOL);
                            // Set the cutoff to the best known solution

} 

© 2001-2020 Fair Isaac Corporation. All rights reserved. This documentation is the property of Fair Isaac Corporation (“FICO”). Receipt or possession of this documentation does not convey rights to disclose, reproduce, make derivative works, use, or allow others to use it except solely for internal evaluation purposes to determine whether to purchase a license to the software described in this documentation, or as otherwise set forth in a written software license agreement between you and FICO (or a FICO affiliate). Use of this documentation and the software described in it must conform strictly to the foregoing permitted uses, and no other use is permitted.