[FEATURE] QPS Parser Preliminary Commit.
Notes: - Correctly parses MPS files and makes appropiate calls to RawQP - Containts Debug Code - Does not generate raw bounds from parse - Does not generate Hessian from parse - Does not generate Actual QP from raw QPrelease/4.3a0
parent
1c87f9d07a
commit
b54c897f91
|
@ -21,6 +21,7 @@
|
|||
#include <gtsam/linear/GaussianFactorGraph.h>
|
||||
#include <gtsam_unstable/linear/EqualityFactorGraph.h>
|
||||
#include <gtsam_unstable/linear/InequalityFactorGraph.h>
|
||||
#include <gtsam/slam/dataset.h>
|
||||
|
||||
namespace gtsam {
|
||||
|
||||
|
@ -41,14 +42,9 @@ struct QP {
|
|||
QP(const GaussianFactorGraph& _cost,
|
||||
const EqualityFactorGraph& _linearEqualities,
|
||||
const InequalityFactorGraph& _linearInequalities) :
|
||||
cost(_cost), equalities(_linearEqualities), inequalities(
|
||||
_linearInequalities) {
|
||||
cost(_cost), equalities(_linearEqualities), inequalities(_linearInequalities) {
|
||||
}
|
||||
|
||||
QP(std::string MPS_FileName):
|
||||
cost(), equalities(), inequalities() {}
|
||||
|
||||
|
||||
/** print */
|
||||
void print(const std::string& s = "") {
|
||||
std::cout << s << std::endl;
|
||||
|
|
|
@ -0,0 +1,103 @@
|
|||
//
|
||||
// Created by ivan on 3/5/16.
|
||||
//
|
||||
|
||||
#include <gtsam_unstable/linear/QPSParser.h>
|
||||
#include <boost/spirit/include/qi.hpp>
|
||||
#include <boost/lambda/lambda.hpp>
|
||||
#include <boost/phoenix/bind.hpp>
|
||||
|
||||
#define BOOST_SPIRIT_USE_PHOENIX_V3 1
|
||||
|
||||
using namespace boost::spirit;
|
||||
using namespace boost::spirit::qi;
|
||||
using namespace boost::spirit::qi::labels;
|
||||
|
||||
namespace gtsam {
|
||||
|
||||
struct QPSParser::MPSGrammar: grammar<basic_istream_iterator<char>> {
|
||||
MPSGrammar(RawQP * rqp) :
|
||||
MPSGrammar::base_type(start), rqp_(rqp) {
|
||||
character = lexeme[char_("a-zA-Z") | char_('_')];
|
||||
title = lexeme[+character
|
||||
>> *(blank | character | char_('-') | char_('.') | char_("0-9"))];
|
||||
word = lexeme[(character
|
||||
>> *(char_("0-9") | char_('-') | char_('.') | character))];
|
||||
name =
|
||||
lexeme[lit("NAME") >> *blank
|
||||
>> title[boost::phoenix::bind(&RawQP::setName, rqp_, qi::_1)]
|
||||
>> +space];
|
||||
row = lexeme[*blank
|
||||
>> (character >> +blank >> word)[boost::phoenix::bind(&RawQP::addRow,
|
||||
rqp_, qi::_1, qi::_3)] >> *blank];
|
||||
rhs_single = lexeme[*blank
|
||||
>> (word >> +blank >> word >> +blank >> double_)[boost::phoenix::bind(
|
||||
&RawQP::addRHS, rqp_, qi::_1, qi::_3, qi::_5)] >> *blank];
|
||||
rhs_double = lexeme[*blank
|
||||
>> (word >> +blank >> word >> +blank >> double_ >> +blank >> word
|
||||
>> +blank >> double_)[boost::phoenix::bind(&RawQP::addRHS, rqp_,
|
||||
qi::_1, qi::_3, qi::_5, qi::_7, qi::_9)] >> *blank];
|
||||
col_single = lexeme[*blank
|
||||
>> (word >> +blank >> word >> +blank >> double_)[boost::phoenix::bind(
|
||||
&RawQP::addColumn, rqp_, qi::_1, qi::_3, qi::_5)] >> *blank];
|
||||
col_double = lexeme[*blank
|
||||
>> (word >> +blank >> word >> +blank >> double_ >> +blank >> word
|
||||
>> +blank >> double_)[boost::phoenix::bind(&RawQP::addColumn, rqp_,
|
||||
qi::_1, qi::_3, qi::_5, qi::_7, qi::_9)] >> *blank];
|
||||
quad_l = lexeme[*blank
|
||||
>> (word >> +blank >> word >> +blank >> double_)[boost::phoenix::bind(
|
||||
&RawQP::addQuadTerm, rqp_, qi::_1, qi::_3, qi::_5)] >> *blank];
|
||||
bound =
|
||||
lexeme[*blank
|
||||
>> (word >> +blank >> word >> +blank >> word >> +blank >> double_)[boost::phoenix::bind(
|
||||
&RawQP::addBound, rqp_, qi::_1, qi::_5, qi::_7)] >> *blank];
|
||||
bound_fr = lexeme[*blank
|
||||
>> (word >> +blank >> word >> +blank >> word)[boost::phoenix::bind(
|
||||
&RawQP::addBound, rqp_, qi::_1, qi::_5)] >> *blank];
|
||||
rows = lexeme[lit("ROWS") >> *blank >> eol >> +(row >> eol)];
|
||||
rhs = lexeme[lit("RHS") >> *blank >> eol
|
||||
>> +((rhs_double | rhs_single) >> eol)];
|
||||
cols = lexeme[lit("COLUMNS") >> *blank >> eol
|
||||
>> +((col_double | col_single) >> eol)];
|
||||
quad = lexeme[lit("QUADOBJ") >> *blank >> eol >> +(quad_l >> eol)];
|
||||
bounds = lexeme[lit("BOUNDS") >> +space >> +((bound | bound_fr) >> eol)];
|
||||
ranges = lexeme[lit("RANGES") >> +space];
|
||||
start = lexeme[name >> rows >> cols >> rhs >> ranges >> bounds >> quad
|
||||
>> lit("ENDATA") >> +space];
|
||||
}
|
||||
RawQP * rqp_;
|
||||
|
||||
rule<basic_istream_iterator<char>, char()> character;
|
||||
rule<basic_istream_iterator<char>, std::vector<char>()> word;
|
||||
rule<basic_istream_iterator<char>, std::vector<char>()> title;
|
||||
rule<basic_istream_iterator<char> > row;
|
||||
rule<basic_istream_iterator<char> > col_single;
|
||||
rule<basic_istream_iterator<char> > col_double;
|
||||
rule<basic_istream_iterator<char> > rhs_single;
|
||||
rule<basic_istream_iterator<char> > rhs_double;
|
||||
rule<basic_istream_iterator<char> > ranges;
|
||||
rule<basic_istream_iterator<char> > bound;
|
||||
rule<basic_istream_iterator<char> > bound_fr;
|
||||
rule<basic_istream_iterator<char> > bounds;
|
||||
rule<basic_istream_iterator<char> > quad;
|
||||
rule<basic_istream_iterator<char> > quad_l;
|
||||
rule<basic_istream_iterator<char> > rows;
|
||||
rule<basic_istream_iterator<char> > cols;
|
||||
rule<basic_istream_iterator<char> > rhs;
|
||||
rule<basic_istream_iterator<char> > name;
|
||||
rule<basic_istream_iterator<char> > start;
|
||||
};
|
||||
|
||||
QP QPSParser::Parse() {
|
||||
RawQP rawData;
|
||||
boost::spirit::basic_istream_iterator<char> begin(stream);
|
||||
boost::spirit::basic_istream_iterator<char> last;
|
||||
|
||||
if (!parse(begin, last, MPSGrammar(&rawData)) && begin == last) {
|
||||
throw QPSParserException();
|
||||
}
|
||||
|
||||
return rawData.makeQP();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/**
|
||||
* @file LPSolver.cpp
|
||||
* @brief QPS parser implementation
|
||||
* @author Ivan Dario Jimenez
|
||||
* @date 1/26/16
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <gtsam_unstable/linear/QP.h>
|
||||
#include <gtsam_unstable/linear/QPSParserException.h>
|
||||
#include <gtsam_unstable/linear/RawQP.h>
|
||||
#include <fstream>
|
||||
|
||||
namespace gtsam {
|
||||
|
||||
class QPSParser {
|
||||
|
||||
private:
|
||||
std::fstream stream;
|
||||
struct MPSGrammar;
|
||||
public:
|
||||
|
||||
QPSParser(const std::string& fileName) :
|
||||
stream(findExampleDataFile(fileName).c_str()) {
|
||||
stream.unsetf(std::ios::skipws);
|
||||
}
|
||||
|
||||
QP Parse();
|
||||
};
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
/**
|
||||
* @file QPSParserException
|
||||
* @brief Exception thrown if there is an error parsing a QPS file
|
||||
* @date jan 24, 2015
|
||||
* @author Duy-Nguyen Ta
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace gtsam {
|
||||
|
||||
class QPSParserException: public ThreadsafeException<QPSParserException> {
|
||||
public:
|
||||
QPSParserException() {
|
||||
}
|
||||
|
||||
virtual ~QPSParserException() throw () {
|
||||
}
|
||||
|
||||
virtual const char *what() const throw () {
|
||||
if (description_.empty())
|
||||
description_ = "There is a problem parsing the QPS file.\n";
|
||||
return description_.c_str();
|
||||
}
|
||||
|
||||
private:
|
||||
mutable std::string description_;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
|
||||
#include <gtsam_unstable/linear/RawQP.h>
|
|
@ -0,0 +1,184 @@
|
|||
#pragma once
|
||||
|
||||
#include <gtsam_unstable/linear/QP.h>
|
||||
#include <gtsam/base/Matrix.h>
|
||||
#include <gtsam/inference/Key.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
#include <unordered_map>
|
||||
#include <gtsam/inference/Symbol.h>
|
||||
|
||||
|
||||
namespace gtsam {
|
||||
class RawQP {
|
||||
typedef std::vector<std::pair<Key, Matrix> > coefficient_v;
|
||||
|
||||
std::vector<std::pair<Key, Matrix> > g;
|
||||
std::unordered_map<std::string, std::unordered_map<std::string, coefficient_v > > row_to_map;
|
||||
std::unordered_map<std::string, Key> varname_to_key;
|
||||
std::unordered_map<std::string, coefficient_v > IL;
|
||||
std::unordered_map<std::string, coefficient_v > IG;
|
||||
std::unordered_map<std::string, coefficient_v > E;
|
||||
std::unordered_map<std::string, double> b;
|
||||
std::unordered_map<std::string, std::unordered_map<std::string, double> > H;
|
||||
unsigned int varNumber;
|
||||
double f;
|
||||
std::string obj_name;
|
||||
std::string name_;
|
||||
const bool debug = true;
|
||||
public:
|
||||
RawQP() : g(), row_to_map(), varname_to_key(), IL(), IG(), E(), b(), H(), varNumber(1){
|
||||
}
|
||||
|
||||
void setName(std::vector<char> name) {
|
||||
name_ = std::string(name.begin(), name.end());
|
||||
if (debug) {
|
||||
std::cout << "Parsing file: " << name_ << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void addColumn(std::vector<char> var, std::vector<char> row,
|
||||
double coefficient) {
|
||||
std::string var_(var.begin(), var.end()), row_(row.begin(), row.end());
|
||||
|
||||
if(!varname_to_key.count(var_))
|
||||
varname_to_key.insert({var_, Symbol('X', varNumber++)});
|
||||
|
||||
if(row_ == obj_name){
|
||||
g.push_back(std::make_pair(varname_to_key[var_], coefficient * ones(1,1)));
|
||||
return;
|
||||
}
|
||||
row_to_map[row_][row_].push_back({varname_to_key[var_], coefficient * ones(1,1)});
|
||||
|
||||
if (debug) {
|
||||
std::cout << "Added Column for Var: " << var_ << " Row: " << row_
|
||||
<< " Coefficient: " << coefficient << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void addColumn(std::vector<char> var, std::vector<char> row1,
|
||||
double coefficient1, std::vector<char> row2, double coefficient2) {
|
||||
std::string var_(var.begin(), var.end()), row1_(row1.begin(), row1.end()),
|
||||
row2_(row2.begin(), row2.end());
|
||||
if(!varname_to_key.count(var_))
|
||||
varname_to_key.insert({var_, Symbol('X', varNumber++)});
|
||||
|
||||
if(row1_ == obj_name)
|
||||
row_to_map[row1_][row2_].push_back({varname_to_key[var_], coefficient1 * ones(1,1)});
|
||||
else
|
||||
row_to_map[row1_][row1_].push_back({varname_to_key[var_], coefficient1 * ones(1,1)});
|
||||
|
||||
if(row2_ == obj_name)
|
||||
row_to_map[row2_][row2_].push_back({varname_to_key[var_], coefficient2 * ones(1,1)});
|
||||
else
|
||||
row_to_map[row2_][row2_].push_back({varname_to_key[var_], coefficient2 * ones(1,1)});
|
||||
|
||||
if (debug) {
|
||||
std::cout << "Added Column for Var: " << var_ << " Row: " << row1_
|
||||
<< " Coefficient: " << coefficient1 << std::endl;
|
||||
std::cout << " " << "Row: " << row2_
|
||||
<< " Coefficient: " << coefficient2 << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void addRHS(std::vector<char> var, std::vector<char> row,
|
||||
double coefficient) {
|
||||
std::string var_(var.begin(), var.end()), row_(row.begin(), row.end());
|
||||
if(row_ == obj_name)
|
||||
f = -coefficient;
|
||||
else
|
||||
b.insert({row_, coefficient});
|
||||
|
||||
if (debug) {
|
||||
std::cout << "Added RHS for Var: " << var_ << " Row: " << row_
|
||||
<< " Coefficient: " << coefficient << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void addRHS(std::vector<char> var, std::vector<char> row1,
|
||||
double coefficient1, std::vector<char> row2, double coefficient2) {
|
||||
std::string var_(var.begin(), var.end()), row1_(row1.begin(), row1.end()),
|
||||
row2_(row2.begin(), row2.end());
|
||||
|
||||
if(row1_ == obj_name)
|
||||
f = -coefficient1;
|
||||
else
|
||||
b.insert({row1_, coefficient1});
|
||||
|
||||
if(row2_ == obj_name)
|
||||
f = -coefficient2;
|
||||
else
|
||||
b.insert({row2_, coefficient2});
|
||||
|
||||
if (debug) {
|
||||
std::cout << "Added RHS for Var: " << var_ << " Row: " << row1_
|
||||
<< " Coefficient: " << coefficient1 << std::endl;
|
||||
std::cout << " " << "Row: " << row2_
|
||||
<< " Coefficient: " << coefficient2 << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void addRow(char type, std::vector<char> name) {
|
||||
std::string name_(name.begin(), name.end());
|
||||
switch(type){
|
||||
case 'N':
|
||||
obj_name = name_;
|
||||
break;
|
||||
case 'L':
|
||||
row_to_map.insert({name_, IL});
|
||||
IL.insert({name_, coefficient_v()});
|
||||
break;
|
||||
case 'G':
|
||||
row_to_map.insert({name_, IG});
|
||||
IG.insert({name_, coefficient_v()});
|
||||
break;
|
||||
case 'E':
|
||||
row_to_map.insert({name_, E});
|
||||
E.insert({name_, coefficient_v()});
|
||||
break;
|
||||
default:
|
||||
std::cout << "invalid type: " << type << std::endl;
|
||||
break;
|
||||
}
|
||||
if (debug) {
|
||||
std::cout << "Added Row Type: " << type << " Name: " << name_
|
||||
<< std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void addBound(std::vector<char> type, std::vector<char> var, double number) {
|
||||
std::string type_(type.begin(), type.end()), var_(var.begin(), var.end());
|
||||
|
||||
|
||||
if (debug) {
|
||||
std::cout << "Added Bound Type: " << type_ << " Var: " << var_
|
||||
<< " Amount: " << number << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void addBound(std::vector<char> type, std::vector<char> var) {
|
||||
std::string type_(type.begin(), type.end()), var_(var.begin(), var.end());
|
||||
if (debug) {
|
||||
std::cout << "Added Free Bound Type: " << type_ << " Var: " << var_
|
||||
<< " Amount: " << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void addQuadTerm(std::vector<char> var1, std::vector<char> var2,
|
||||
double coefficient) {
|
||||
std::string var1_(var1.begin(), var1.end()), var2_(var2.begin(),
|
||||
var2.end());
|
||||
if (debug) {
|
||||
std::cout << "Added QuadTerm for Var: " << var1_ << " Row: " << var2_
|
||||
<< " Coefficient: " << coefficient << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
QP makeQP() {
|
||||
return QP();
|
||||
}
|
||||
};
|
||||
}
|
|
@ -19,7 +19,7 @@
|
|||
#include <gtsam/base/Testable.h>
|
||||
#include <gtsam/inference/Symbol.h>
|
||||
#include <gtsam_unstable/linear/QPSolver.h>
|
||||
|
||||
#include <gtsam_unstable/linear/QPSParser.h>
|
||||
#include <CppUnitLite/TestHarness.h>
|
||||
#include <gtsam_unstable/linear/InfeasibleInitialValues.h>
|
||||
#include <gtsam_unstable/linear/InfeasibleOrUnboundedProblem.h>
|
||||
|
@ -213,39 +213,42 @@ TEST(QPSolver, optimizeForst10book_pg171Ex5) {
|
|||
CHECK(assert_equal(expectedSolution, solution, 1e-100));
|
||||
}
|
||||
|
||||
QP createExampleQP(){
|
||||
QP createExampleQP() {
|
||||
QP exampleqp;
|
||||
exampleqp.cost.push_back(
|
||||
HessianFactor(X(1),Y(1),
|
||||
8.0 *ones(1,1), 2.0 * ones(1,1), 1.5*ones(1),
|
||||
10.0 *ones(1,1), -2.0 *ones(1), 4.0));
|
||||
// 2x + y >= 2
|
||||
exampleqp.inequalities.push_back(
|
||||
LinearInequality(X(1), -2.0*ones(1,1), Y(1), -ones(1,1), -2, 0));
|
||||
// -x + 2y <= 6
|
||||
exampleqp.inequalities.push_back(
|
||||
LinearInequality(X(1), -ones(1,1), Y(1), 2.0* ones(1,1), 6, 1));
|
||||
//x >= 0
|
||||
exampleqp.inequalities.push_back(
|
||||
LinearInequality(X(1), -ones(1,1), 0, 2));
|
||||
// y > = 0
|
||||
exampleqp.inequalities.push_back(
|
||||
LinearInequality(Y(1), -ones(1,1), 0, 3));
|
||||
// x<= 20
|
||||
exampleqp.inequalities.push_back(
|
||||
LinearInequality(X(1), ones(1,1), 20, 4));
|
||||
|
||||
return exampleqp;
|
||||
};
|
||||
}
|
||||
;
|
||||
|
||||
TEST(QPSolver, QPExampleData){
|
||||
QP exampleqp("QPExample.QPS");
|
||||
TEST(QPSolver, QPExampleData) {
|
||||
QPSParser parser("QPExample.QPS");
|
||||
// QPSParser parser("AUG2D.QPS");
|
||||
// QPSParser parser("CONT-050.QPS");
|
||||
|
||||
QP expectedqp = createExampleQP();
|
||||
// min f(x,y) = 4 + 1.5x -y + 0.5(8x^2 + 2xy + 2yx + 10y^2
|
||||
QP exampleqp = parser.Parse();
|
||||
|
||||
// CHECK(expectedqp.cost.equals(exampleqp.cost, 1e-7));
|
||||
// CHECK(expectedqp.inequalities.equals(exampleqp.inequalities, 1e-7));
|
||||
// CHECK(expectedqp.equalities.equals(exampleqp.equalities, 1e-7));
|
||||
// QP expectedqp = createExampleQP();
|
||||
QP expectedqp;
|
||||
// min f(x,y) = 4 + 1.5x -y + 0.58x^2 + 2xy + 2yx + 10y^2
|
||||
expectedqp.cost.push_back(
|
||||
HessianFactor(X(1), X(2), 8.0 * ones(1, 1), 2.0 * ones(1, 1),
|
||||
1.5 * ones(1), 10.0 * ones(1, 1), -2.0 * ones(1), 4.0));
|
||||
// 2x + y >= 2
|
||||
expectedqp.inequalities.push_back(
|
||||
LinearInequality(X(1), -2.0 * ones(1, 1), X(2), -ones(1, 1), -2, 0));
|
||||
// -x + 2y <= 6
|
||||
expectedqp.inequalities.push_back(
|
||||
LinearInequality(X(1), -ones(1, 1), X(2), 2.0 * ones(1, 1), 6, 1));
|
||||
//x >= 0
|
||||
expectedqp.inequalities.push_back(LinearInequality(X(1), -ones(1, 1), 0, 2));
|
||||
// y > = 0
|
||||
expectedqp.inequalities.push_back(LinearInequality(X(2), -ones(1, 1), 0, 3));
|
||||
// x<= 20
|
||||
expectedqp.inequalities.push_back(LinearInequality(X(1), ones(1, 1), 20, 4));
|
||||
|
||||
CHECK(expectedqp.cost.equals(exampleqp.cost, 1e-7));
|
||||
CHECK(expectedqp.inequalities.equals(exampleqp.inequalities, 1e-7));
|
||||
CHECK(expectedqp.equalities.equals(exampleqp.equalities, 1e-7));
|
||||
}
|
||||
|
||||
/* ************************************************************************* */
|
||||
|
|
Loading…
Reference in New Issue