354 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
			
		
		
	
	
			354 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
| /* ----------------------------------------------------------------------------
 | |
| 
 | |
|  * GTSAM Copyright 2010, Georgia Tech Research Corporation,
 | |
|  * Atlanta, Georgia 30332-0415
 | |
|  * All Rights Reserved
 | |
|  * Authors: Frank Dellaert, et al. (see THANKS for the full author list)
 | |
| 
 | |
|  * See LICENSE for the license information
 | |
| 
 | |
|  * -------------------------------------------------------------------------- */
 | |
| 
 | |
| /**
 | |
|  * @file     QPSVisitor.cpp
 | |
|  * @brief    As the QPS parser reads a file, it call functions in QPSVistor.
 | |
|  * This visitor in turn stores what the parser has read in a way that can be
 | |
|  * later used to build the Factor Graph for the QP Constraints and cost. This
 | |
|  * intermediate representation is required because an expression in the QPS file
 | |
|  * doesn't necessarily correspond to a single constraint or term in the cost
 | |
|  * function.
 | |
|  * @author   Ivan Dario Jimenez
 | |
|  * @date     3/5/16
 | |
|  */
 | |
| 
 | |
| #include <gtsam_unstable/linear/QPSVisitor.h>
 | |
| 
 | |
| #include <algorithm>
 | |
| #include <iostream>
 | |
| #include <map>
 | |
| 
 | |
| using boost::fusion::at_c;
 | |
| using namespace std;
 | |
| 
 | |
| using Chars = std::vector<char>;
 | |
| 
 | |
| // Get a string from a fusion vector of Chars
 | |
| template <size_t I, class FusionVector>
 | |
| static string fromChars(const FusionVector &vars) {
 | |
|   const Chars &chars = at_c<I>(vars);
 | |
|   return string(chars.begin(), chars.end());
 | |
| }
 | |
| 
 | |
| namespace gtsam {
 | |
| 
 | |
| void QPSVisitor::setName(
 | |
|     boost::fusion::vector<Chars, Chars, Chars> const &name) {
 | |
|   name_ = fromChars<1>(name);
 | |
|   if (debug) {
 | |
|     cout << "Parsing file: " << name_ << endl;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void QPSVisitor::addColumn(
 | |
|     boost::fusion::vector<Chars, Chars, Chars, Chars, Chars, double,
 | |
|                           Chars> const &vars) {
 | |
|   string var_ = fromChars<1>(vars);
 | |
|   string row_ = fromChars<3>(vars);
 | |
|   Matrix11 coefficient = at_c<5>(vars) * I_1x1;
 | |
|   if (debug) {
 | |
|     cout << "Added Column for Var: " << var_ << " Row: " << row_
 | |
|          << " Coefficient: " << coefficient << endl;
 | |
|   }
 | |
|   if (!varname_to_key.count(var_))
 | |
|     varname_to_key[var_] = Symbol('X', numVariables++);
 | |
|   if (row_ == obj_name) {
 | |
|     g[varname_to_key[var_]] = coefficient;
 | |
|     return;
 | |
|   }
 | |
|   (*row_to_constraint_v[row_])[row_][varname_to_key[var_]] = coefficient;
 | |
| }
 | |
| 
 | |
| void QPSVisitor::addColumnDouble(
 | |
|     boost::fusion::vector<Chars, Chars, Chars, Chars, double, Chars, Chars,
 | |
|                           Chars, double> const &vars) {
 | |
|   string var_ = fromChars<0>(vars);
 | |
|   string row1_ = fromChars<2>(vars);
 | |
|   string row2_ = fromChars<6>(vars);
 | |
|   Matrix11 coefficient1 = at_c<4>(vars) * I_1x1;
 | |
|   Matrix11 coefficient2 = at_c<8>(vars) * I_1x1;
 | |
|   if (!varname_to_key.count(var_))
 | |
|     varname_to_key.insert({var_, Symbol('X', numVariables++)});
 | |
|   if (row1_ == obj_name)
 | |
|     g[varname_to_key[var_]] = coefficient1;
 | |
|   else
 | |
|     (*row_to_constraint_v[row1_])[row1_][varname_to_key[var_]] = coefficient1;
 | |
|   if (row2_ == obj_name)
 | |
|     g[varname_to_key[var_]] = coefficient2;
 | |
|   else
 | |
|     (*row_to_constraint_v[row2_])[row2_][varname_to_key[var_]] = coefficient2;
 | |
| }
 | |
| 
 | |
| void QPSVisitor::addRangeSingle(
 | |
|     boost::fusion::vector<Chars, Chars, Chars, Chars, Chars, double,
 | |
|                           Chars> const &vars) {
 | |
|   string var_ = fromChars<1>(vars);
 | |
|   string row_ = fromChars<3>(vars);
 | |
|   double range = at_c<5>(vars);
 | |
|   ranges[row_] = range;
 | |
|   if (debug) {
 | |
|     cout << "SINGLE RANGE ADDED" << endl;
 | |
|     cout << "VAR:" << var_ << " ROW: " << row_ << " RANGE: " << range << endl;
 | |
|   }
 | |
| }
 | |
| void QPSVisitor::addRangeDouble(
 | |
|     boost::fusion::vector<Chars, Chars, Chars, Chars, Chars, double, Chars,
 | |
|                           Chars, Chars, double> const &vars) {
 | |
|   string var_ = fromChars<1>(vars);
 | |
|   string row1_ = fromChars<3>(vars);
 | |
|   string row2_ = fromChars<7>(vars);
 | |
|   double range1 = at_c<5>(vars);
 | |
|   double range2 = at_c<9>(vars);
 | |
|   ranges[row1_] = range1;
 | |
|   ranges[row2_] = range2;
 | |
|   if (debug) {
 | |
|     cout << "DOUBLE RANGE ADDED" << endl;
 | |
|     cout << "VAR: " << var_ << " ROW1: " << row1_ << " RANGE1: " << range1
 | |
|          << " ROW2: " << row2_ << " RANGE2: " << range2 << endl;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void QPSVisitor::addRHS(boost::fusion::vector<Chars, Chars, Chars, Chars, Chars,
 | |
|                                               double, Chars> const &vars) {
 | |
|   string var_ = fromChars<1>(vars);
 | |
|   string row_ = fromChars<3>(vars);
 | |
|   double coefficient = at_c<5>(vars);
 | |
|   if (row_ == obj_name)
 | |
|     f = -coefficient;
 | |
|   else
 | |
|     b[row_] = coefficient;
 | |
| 
 | |
|   if (debug) {
 | |
|     cout << "Added RHS for Var: " << var_ << " Row: " << row_
 | |
|          << " Coefficient: " << coefficient << endl;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void QPSVisitor::addRHSDouble(
 | |
|     boost::fusion::vector<Chars, Chars, Chars, Chars, Chars, double, Chars,
 | |
|                           Chars, Chars, double> const &vars) {
 | |
|   string var_ = fromChars<1>(vars);
 | |
|   string row1_ = fromChars<3>(vars);
 | |
|   string row2_ = fromChars<7>(vars);
 | |
|   double coefficient1 = at_c<5>(vars);
 | |
|   double coefficient2 = at_c<9>(vars);
 | |
|   if (row1_ == obj_name)
 | |
|     f = -coefficient1;
 | |
|   else
 | |
|     b[row1_] = coefficient1;
 | |
| 
 | |
|   if (row2_ == obj_name)
 | |
|     f = -coefficient2;
 | |
|   else
 | |
|     b[row2_] = coefficient2;
 | |
| 
 | |
|   if (debug) {
 | |
|     cout << "Added RHS for Var: " << var_ << " Row: " << row1_
 | |
|          << " Coefficient: " << coefficient1 << endl;
 | |
|     cout << "                      "
 | |
|          << "Row: " << row2_ << " Coefficient: " << coefficient2 << endl;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void QPSVisitor::addRow(
 | |
|     boost::fusion::vector<Chars, char, Chars, Chars, Chars> const &vars) {
 | |
|   string name_ = fromChars<3>(vars);
 | |
|   char type = at_c<1>(vars);
 | |
|   switch (type) {
 | |
|     case 'N':
 | |
|       obj_name = name_;
 | |
|       break;
 | |
|     case 'L':
 | |
|       row_to_constraint_v[name_] = &IL;
 | |
|       break;
 | |
|     case 'G':
 | |
|       row_to_constraint_v[name_] = &IG;
 | |
|       break;
 | |
|     case 'E':
 | |
|       row_to_constraint_v[name_] = &E;
 | |
|       break;
 | |
|     default:
 | |
|       cout << "invalid type: " << type << endl;
 | |
|       break;
 | |
|   }
 | |
|   if (debug) {
 | |
|     cout << "Added Row Type: " << type << " Name: " << name_ << endl;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void QPSVisitor::addBound(
 | |
|     boost::fusion::vector<Chars, Chars, Chars, Chars, Chars, Chars, Chars,
 | |
|                           double> const &vars) {
 | |
|   string type_ = fromChars<1>(vars);
 | |
|   string var_ = fromChars<5>(vars);
 | |
|   double number = at_c<7>(vars);
 | |
|   if (type_.compare(string("UP")) == 0)
 | |
|     up[varname_to_key[var_]] = number;
 | |
|   else if (type_.compare(string("LO")) == 0)
 | |
|     lo[varname_to_key[var_]] = number;
 | |
|   else if (type_.compare(string("FX")) == 0)
 | |
|     fx[varname_to_key[var_]] = number;
 | |
|   else
 | |
|     cout << "Invalid Bound Type: " << type_ << endl;
 | |
| 
 | |
|   if (debug) {
 | |
|     cout << "Added Bound Type: " << type_ << " Var: " << var_
 | |
|          << " Amount: " << number << endl;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void QPSVisitor::addFreeBound(
 | |
|     boost::fusion::vector<Chars, Chars, Chars, Chars, Chars, Chars, Chars> const
 | |
|         &vars) {
 | |
|   string type_ = fromChars<1>(vars);
 | |
|   string var_ = fromChars<5>(vars);
 | |
|   free.push_back(varname_to_key[var_]);
 | |
|   if (debug) {
 | |
|     cout << "Added Free Bound Type: " << type_ << " Var: " << var_
 | |
|          << " Amount: " << endl;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void QPSVisitor::addQuadTerm(
 | |
|     boost::fusion::vector<Chars, Chars, Chars, Chars, Chars, double,
 | |
|                           Chars> const &vars) {
 | |
|   string var1_ = fromChars<1>(vars);
 | |
|   string var2_ = fromChars<3>(vars);
 | |
|   Matrix11 coefficient = at_c<5>(vars) * I_1x1;
 | |
| 
 | |
|   H[varname_to_key[var1_]][varname_to_key[var2_]] = coefficient;
 | |
|   H[varname_to_key[var2_]][varname_to_key[var1_]] = coefficient;
 | |
|   if (debug) {
 | |
|     cout << "Added QuadTerm for Var: " << var1_ << " Row: " << var2_
 | |
|          << " Coefficient: " << coefficient << endl;
 | |
|   }
 | |
| }
 | |
| 
 | |
| QP QPSVisitor::makeQP() {
 | |
|   // Create the keys from the variable names
 | |
|   KeyVector keys;
 | |
|   for (auto kv : varname_to_key) {
 | |
|     keys.push_back(kv.second);
 | |
|   }
 | |
| 
 | |
|   // Fill the G matrices and g vectors from
 | |
|   vector<Matrix> Gs;
 | |
|   vector<Vector> gs;
 | |
|   sort(keys.begin(), keys.end());
 | |
|   for (size_t i = 0; i < keys.size(); ++i) {
 | |
|     for (size_t j = i; j < keys.size(); ++j) {
 | |
|       if (H.count(keys[i]) > 0 && H[keys[i]].count(keys[j]) > 0) {
 | |
|         Gs.emplace_back(H[keys[i]][keys[j]]);
 | |
|       } else {
 | |
|         Gs.emplace_back(Z_1x1);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   for (Key key1 : keys) {
 | |
|     if (g.count(key1) > 0) {
 | |
|       gs.emplace_back(-g[key1]);
 | |
|     } else {
 | |
|       gs.emplace_back(Z_1x1);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // Construct the quadratic program
 | |
|   QP madeQP;
 | |
|   auto obj = HessianFactor(keys, Gs, gs, 2 * f);
 | |
|   madeQP.cost.push_back(obj);
 | |
| 
 | |
|   // Add equality and inequality constraints into the QP
 | |
|   size_t dual_key_num = keys.size() + 1;
 | |
|   for (auto kv : E) {
 | |
|     map<Key, Matrix11> keyMatrixMapPos;
 | |
|     map<Key, Matrix11> keyMatrixMapNeg;
 | |
|     if (ranges.count(kv.first) == 1) {
 | |
|       for (auto km : kv.second) {
 | |
|         keyMatrixMapPos.insert(km);
 | |
|         km.second = -km.second;
 | |
|         keyMatrixMapNeg.insert(km);
 | |
|       }
 | |
|       if (ranges[kv.first] > 0) {
 | |
|         madeQP.inequalities.push_back(
 | |
|             LinearInequality(keyMatrixMapNeg, -b[kv.first], dual_key_num++));
 | |
|         madeQP.inequalities.push_back(LinearInequality(
 | |
|             keyMatrixMapPos, b[kv.first] + ranges[kv.first], dual_key_num++));
 | |
|       } else if (ranges[kv.first] < 0) {
 | |
|         madeQP.inequalities.push_back(
 | |
|             LinearInequality(keyMatrixMapPos, b[kv.first], dual_key_num++));
 | |
|         madeQP.inequalities.push_back(LinearInequality(
 | |
|             keyMatrixMapNeg, ranges[kv.first] - b[kv.first], dual_key_num++));
 | |
|       } else {
 | |
|         cerr << "ERROR: CANNOT ADD A RANGE OF ZERO" << endl;
 | |
|         throw;
 | |
|       }
 | |
|       continue;
 | |
|     }
 | |
|     map<Key, Matrix11> keyMatrixMap;
 | |
|     for (auto km : kv.second) {
 | |
|       keyMatrixMap.insert(km);
 | |
|     }
 | |
|     madeQP.equalities.push_back(
 | |
|         LinearEquality(keyMatrixMap, b[kv.first] * I_1x1, dual_key_num++));
 | |
|   }
 | |
| 
 | |
|   for (auto kv : IG) {
 | |
|     map<Key, Matrix11> keyMatrixMapNeg;
 | |
|     map<Key, Matrix11> keyMatrixMapPos;
 | |
|     for (auto km : kv.second) {
 | |
|       keyMatrixMapPos.insert(km);
 | |
|       km.second = -km.second;
 | |
|       keyMatrixMapNeg.insert(km);
 | |
|     }
 | |
|     madeQP.inequalities.push_back(
 | |
|         LinearInequality(keyMatrixMapNeg, -b[kv.first], dual_key_num++));
 | |
|     if (ranges.count(kv.first) == 1) {
 | |
|       madeQP.inequalities.push_back(LinearInequality(
 | |
|           keyMatrixMapPos, b[kv.first] + ranges[kv.first], dual_key_num++));
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   for (auto kv : IL) {
 | |
|     map<Key, Matrix11> keyMatrixMapPos;
 | |
|     map<Key, Matrix11> keyMatrixMapNeg;
 | |
|     for (auto km : kv.second) {
 | |
|       keyMatrixMapPos.insert(km);
 | |
|       km.second = -km.second;
 | |
|       keyMatrixMapNeg.insert(km);
 | |
|     }
 | |
|     madeQP.inequalities.push_back(
 | |
|         LinearInequality(keyMatrixMapPos, b[kv.first], dual_key_num++));
 | |
|     if (ranges.count(kv.first) == 1) {
 | |
|       madeQP.inequalities.push_back(LinearInequality(
 | |
|           keyMatrixMapNeg, ranges[kv.first] - b[kv.first], dual_key_num++));
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   for (Key k : keys) {
 | |
|     if (find(free.begin(), free.end(), k) != free.end()) continue;
 | |
|     if (fx.count(k) == 1)
 | |
|       madeQP.equalities.push_back(
 | |
|           LinearEquality(k, I_1x1, fx[k] * I_1x1, dual_key_num++));
 | |
|     if (up.count(k) == 1)
 | |
|       madeQP.inequalities.push_back(
 | |
|           LinearInequality(k, I_1x1, up[k], dual_key_num++));
 | |
|     if (lo.count(k) == 1)
 | |
|       madeQP.inequalities.push_back(
 | |
|           LinearInequality(k, -I_1x1, -lo[k], dual_key_num++));
 | |
|     else
 | |
|       madeQP.inequalities.push_back(
 | |
|           LinearInequality(k, -I_1x1, 0, dual_key_num++));
 | |
|   }
 | |
|   return madeQP;
 | |
| }
 | |
| }  // namespace gtsam
 |