vector<const Match *> OptimalConstrainedMatches::calculateSubset()
{
  _score = -1;
  vector<const Match*> result;

  if (_matches.size() == 0)
  {
    return result;
  }

  // figure out all the pairs of matches that conflict.
  _calculateMatchConflicts();

  // if there are no conflicts, then there is nothing to solve.
  if (_conflicts.size() == 0)
  {
    LOG_DEBUG("No match conflicts found.");
    return _matches;
  }

  // populate the solver with
  IntegerProgrammingSolver solver;
  _populateSolver(solver);
  if (_timeLimit > 0)
  {
    solver.setTimeLimit(_timeLimit);
  }

  LOG_INFO("Calculating optimal match conflicts with an Integer Programming solution...");
  // solve the Integer Programming problem.
  solver.solve();
  _score = solver.getObjectiveValue();

  result.reserve(_matches.size());

  // go through all the columns (variables)
  for (int i = 0; i < solver.getNumColumns(); i++)
  {
    // if the value is close to 1 (as opposed to 0)
    if (solver.getColumnPrimalValue(i + 1) > 0.99)
    {
      // it is a keeper
      result.push_back(_matches[i]);
    }
    else
    {
      LOG_TRACE("Removing match: " << _matches[i]);
    }
  }

  return result;
}
void OptimalConstrainedMatches::_populateSolver(IntegerProgrammingSolver& solver)
{
  // try to maximize the score.
  solver.setObjectiveDirection(GLP_MAX);

  // each match is a column (variable) in the function that we want to maximize.
  solver.addColumns(_matches.size());
  for (size_t i = 0; i < _matches.size(); i++)
  {
    solver.setColumnKind(i + 1, GLP_BV);
    //solver.setColumnBounds(i + 1, GLP_DB, 0.0, 1.0);
    // The score of a match is the coefficient. This makes higher score matches worth
    // more in the function.
    solver.setObjectiveCoefficient(i + 1, _matches[i]->getScore() + EPSILON);
  }

  // there is one row (constraint) for each conflict.
  solver.addRows(_conflicts.size());
  vector<int> ia(_conflicts.size() * 2 + 1);
  vector<int> ja(_conflicts.size() * 2 + 1);
  vector<double> ra(_conflicts.size() * 2 + 1);
  int i = 0;
  for (MatchConflicts::ConflictMap::const_iterator it = _conflicts.constBegin();
       it != _conflicts.constEnd(); ++it)
  {
    // Set the coefficients to 1 for each of the conflicting pairs and set the max value to 1. This
    // will make it so only one of the values can be 1 at a time, or they can both be 0.
    solver.setRowBounds(i + 1, GLP_DB, 0.0, 1.0);
    ia[i * 2 + 1] = i + 1;
    ja[i * 2 + 1] = (int)it.key() + 1;
    ra[i * 2 + 1] = 1.0;
    ia[i * 2 + 2] = i + 1;
    ja[i * 2 + 2] = (int)it.value() + 1;
    ra[i * 2 + 2] = 1.0;
    i++;
  }

  // Populate the row coefficients.
  solver.loadMatrix(ia, ja, ra);
}
  void runTest()
  {
    double z, x1, x2, x3;

    IntegerProgrammingSolver solver;
    solver.setProblemName("sample");
    solver.setObjectiveDirection(GLP_MAX);
    solver.addRows(2);

    solver.setRowName(1, "p");
    solver.setRowBounds(1, GLP_DB, 0.0, 1.0);
    solver.setRowName(2, "q");
    solver.setRowBounds(2, GLP_DB, 0.0, 1.0);

    solver.addColumns(3);
    solver.setColumnName(1, "x1");
    solver.setColumnKind(1, GLP_BV);
    solver.setColumnBounds(1, GLP_DB, 0.0, 1.0);
    solver.setObjectiveCoefficient(1, 0.4);

    solver.setColumnName(2, "x2");
    solver.setColumnKind(2, GLP_BV);
    solver.setColumnBounds(2, GLP_DB, 0.0, 1.0);
    solver.setObjectiveCoefficient(2, 0.9);

    solver.setColumnName(3, "x3");
    solver.setColumnKind(3, GLP_BV);
    solver.setColumnBounds(3, GLP_DB, 0.0, 1.0);
    solver.setObjectiveCoefficient(3, 0.6);

    vector<int> ia, ja;
    vector<double> ar;
    ia.push_back(-1);
    ja.push_back(-1);
    ar.push_back(-1);

    ia.push_back(1); ja.push_back(1); ar.push_back(1.0);
    ia.push_back(1); ja.push_back(2); ar.push_back(1.0);
    ia.push_back(1); ja.push_back(3); ar.push_back(0.0);

    //ia.push_back(2); ja.push_back(1); ar.push_back(0.0);
    ia.push_back(2); ja.push_back(2); ar.push_back(1.0);
    ia.push_back(2); ja.push_back(3); ar.push_back(1.0);
    solver.loadMatrix(ia, ja, ar);
    solver.solve();

    z = solver.getObjectiveValue();
    x1 = solver.getColumnPrimalValue(1);
    x2 = solver.getColumnPrimalValue(2);
    x3 = solver.getColumnPrimalValue(3);

    //LOG_INFO("z: " << z << " x1: " << x1 << " x2: " << x2 << " x3: " << x3);
    CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, z, 1e-5);
    CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, x1, 1e-5);
    CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, x2, 1e-5);
    CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, x3, 1e-5);

    solver.setObjectiveCoefficient(3, 0.4);
    solver.solve();

    z = solver.getObjectiveValue();
    x1 = solver.getColumnPrimalValue(1);
    x2 = solver.getColumnPrimalValue(2);
    x3 = solver.getColumnPrimalValue(3);

    //LOG_INFO("z: " << z << " x1: " << x1 << " x2: " << x2 << " x3: " << x3);
    CPPUNIT_ASSERT_DOUBLES_EQUAL(0.9, z, 1e-5);
    CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, x1, 1e-5);
    CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, x2, 1e-5);
    CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, x3, 1e-5);
  }
  void runTest2()
  {
    double z, x1, x2, x3;

    IntegerProgrammingSolver solver;
    solver.setProblemName("sample");
    solver.setObjectiveDirection(GLP_MAX);
    solver.addRows(1);

    // 0.0 <= p <= 1.1
    solver.setRowName(1, "p");
    // make sure it properly solves an integer problem.
    solver.setRowBounds(1, GLP_DB, 0.0, 1.1);

    // z = x1 + 2 * x2 + x3
    solver.addColumns(3);
    solver.setColumnName(1, "x1");
    solver.setColumnKind(1, GLP_BV);
    solver.setColumnBounds(1, GLP_DB, 0.0, 1.0);
    solver.setObjectiveCoefficient(1, 1.0);

    solver.setColumnName(2, "x2");
    solver.setColumnKind(2, GLP_BV);
    solver.setColumnBounds(2, GLP_DB, 0.0, 1.0);
    solver.setObjectiveCoefficient(2, 2.0);

    solver.setColumnName(3, "x3");
    solver.setColumnKind(3, GLP_BV);
    solver.setColumnBounds(3, GLP_DB, 0.0, 1.0);
    solver.setObjectiveCoefficient(3, 1.0);

    vector<int> ia, ja;
    vector<double> ar;
    ia.push_back(-1);
    ja.push_back(-1);
    ar.push_back(-1);

    // subject to constraint:
    // p = x1 + x2 + 0 * x3
    ia.push_back(1); ja.push_back(1); ar.push_back(1.0);
    ia.push_back(1); ja.push_back(2); ar.push_back(1.0);
    ia.push_back(1); ja.push_back(3); ar.push_back(0.0);

    solver.loadMatrix(ia, ja, ar);
    solver.solve();

    z = solver.getObjectiveValue();
    x1 = solver.getColumnPrimalValue(1);
    x2 = solver.getColumnPrimalValue(2);
    x3 = solver.getColumnPrimalValue(3);

    //LOG_INFO("z: " << z << " x1: " << x1 << " x2: " << x2 << " x3: " << x3);
    CPPUNIT_ASSERT_DOUBLES_EQUAL(3.0, z, 1e-5);
    CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, x1, 1e-5);
    CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, x2, 1e-5);
    CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, x3, 1e-5);
  }