AIToolbox
A library that offers tools for AI problem solving.
FactorGraph.hpp
Go to the documentation of this file.
1 #ifndef AI_TOOLBOX_FACTORED_FACTOR_GRAPH_HEADER_FILE
2 #define AI_TOOLBOX_FACTORED_FACTOR_GRAPH_HEADER_FILE
3 
4 #include <cstddef>
5 #include <list>
6 #include <vector>
7 
10 
11 namespace AIToolbox::Factored {
30  template <typename FactorData>
31  class FactorGraph {
32  public:
34 
35  class FactorNode {
36  friend class FactorGraph;
37 
38  FactorData f_;
39  Variables variables_;
40 
41  public:
42  const Variables & getVariables() const { return variables_; }
43  const FactorData & getData() const { return f_; }
44  FactorData & getData() { return f_; }
45  };
46 
47  using FactorList = std::list<FactorNode>;
48  using FactorIt = typename FactorList::iterator;
49  using CFactorIt = typename FactorList::const_iterator;
50  using FactorItList = std::vector<FactorIt>;
51 
52  using value_type = FactorData;
53  using iterator = FactorIt;
55 
65  FactorGraph(size_t variables);
66 
78  FactorGraph(const FactorGraph & other);
79 
87  FactorGraph & operator=(const FactorGraph &) = delete;
88 
94  void reset(size_t variables);
95 
103  const FactorItList & getFactors(size_t variable) const;
104 
112  const Variables & getVariables(size_t variable) const;
113 
121  const Variables & getVariables(FactorIt factor) const;
122 
130  const Variables & getVariables(CFactorIt factor) const;
131 
143  Variables getVariables(const FactorItList & factors) const;
144 
162  FactorIt getFactor(const Variables & variables);
163 
174  void erase(size_t variable);
175 
185  size_t variableSize() const;
186 
192  size_t factorSize() const;
193 
201  FactorIt begin();
202 
208  CFactorIt begin() const;
209 
215  CFactorIt cbegin() const;
216 
224  FactorIt end();
225 
233  CFactorIt end() const;
234 
242  CFactorIt cend() const;
243 
254  size_t bestVariableToRemove(const Factors & F) const;
255 
256  private:
257  FactorList factorAdjacencies_;
258  static FactorList factorAdjacenciesPool_;
259 
260  auto findFactorByVariables(const FactorItList & list, const Variables & variables) const {
261  return std::find_if(
262  std::begin(list),
263  std::end(list),
264  [&variables](const FactorIt it){ return it->variables_ == variables; }
265  );
266  }
267 
268  struct VariableNode {
269  FactorItList factors;
270  Variables vNeighbors;
271  bool active = true;
272  };
273 
274  std::vector<VariableNode> variableAdjacencies_;
275  size_t activeVariables_;
276  };
277 
278  template <typename FD>
279  typename FactorGraph<FD>::FactorList FactorGraph<FD>::factorAdjacenciesPool_;
280 
281  template <typename FD>
282  FactorGraph<FD>::FactorGraph(const size_t variables) : variableAdjacencies_(variables), activeVariables_(variables) {}
283 
284  template <typename FD>
286  variableAdjacencies_(other.variableAdjacencies_),
287  activeVariables_(other.activeVariables_)
288  {
289  // Try to take as much memory from the pool as possible.
290  auto oIt = std::begin(other.factorAdjacencies_);
291  while (factorAdjacencies_.size() < other.factorAdjacencies_.size()) {
292  if (factorAdjacenciesPool_.size() > 0) {
293  factorAdjacencies_.splice(std::end(factorAdjacencies_), factorAdjacenciesPool_, std::begin(factorAdjacenciesPool_));
294  factorAdjacencies_.back() = *oIt++;
295  } else {
296  factorAdjacencies_.emplace_back(*oIt++);
297  }
298  }
299 
300  // So here it's pretty simple; we just need to rebuild the 'factors'
301  // variable in each VariableNode, as it contains iterators that need to
302  // point to the newly copied lists, rather than the ones in 'other'.
303 
304  // First we cleanup the old iterators.
305  for (auto & a : variableAdjacencies_)
306  a.factors.clear();
307 
308  // Then we rebuild them.
309  for (auto it = factorAdjacencies_.begin(); it != factorAdjacencies_.end(); ++it) {
310  for (auto a : it->getVariables()) {
311  variableAdjacencies_[a].factors.push_back(it);
312  }
313  }
314  }
315 
316  template <typename FD>
317  void FactorGraph<FD>::reset(const size_t variables) {
318  factorAdjacencies_.clear();
319 
320  variableAdjacencies_.clear();
321  variableAdjacencies_.resize(variables);
322  activeVariables_ = variables;
323  }
324 
325  template <typename FD>
326  const typename FactorGraph<FD>::FactorItList & FactorGraph<FD>::getFactors(const size_t variable) const {
327  return variableAdjacencies_[variable].factors;
328  }
329 
330  template <typename FD>
331  const typename FactorGraph<FD>::Variables & FactorGraph<FD>::getVariables(const size_t variable) const {
332  return variableAdjacencies_[variable].vNeighbors;
333  }
334 
335  template <typename FD>
337  return factor->variables_;
338  }
339 
340  template <typename FD>
342  return factor->variables_;
343  }
344 
345  template <typename FD>
347  Variables retval;
348  for (const auto factor : factors)
349  set_union_inplace(retval, factor->variables_);
350 
351  return retval;
352  }
353 
354  template <typename FD>
356  const auto found = findFactorByVariables(variableAdjacencies_[variables[0]].factors, variables);
357  if (found != variableAdjacencies_[variables[0]].factors.end())
358  return *found;
359 
360  FactorIt it;
361  if (!factorAdjacenciesPool_.size()) {
362  factorAdjacencies_.emplace_back(FactorNode());
363  it = --factorAdjacencies_.end();
364  } else {
365  factorAdjacencies_.splice(std::end(factorAdjacencies_), factorAdjacenciesPool_, std::begin(factorAdjacenciesPool_));
366  it = std::prev(factorAdjacencies_.end());
367  // We reset the data; just in case it's a vector we don't want to
368  // move but assign so that it does not clear already allocated
369  // memory.
370  auto tmp = FD{};
371  it->f_ = tmp;
372  }
373 
374  it->variables_ = variables;
375  for (const auto a : variables) {
376  auto & va = variableAdjacencies_[a];
377  va.factors.push_back(it);
378 
379  // Add *other* agents to vNeighbors
380  const auto mid = va.vNeighbors.size();
381  va.vNeighbors.reserve(mid + variables.size() - 1);
382 
383  for (size_t i = 0, j = 0; i < variables.size(); ) {
384  if (variables[i] == a) {
385  ++i;
386  } else if (j == mid || variables[i] < va.vNeighbors[j]) {
387  va.vNeighbors.push_back(variables[i]);
388  ++i;
389  } else {
390  if (variables[i] == va.vNeighbors[j])
391  ++i;
392  ++j;
393  }
394  }
395  std::inplace_merge(std::begin(va.vNeighbors), std::begin(va.vNeighbors)+mid, std::end(va.vNeighbors));
396  }
397  return it;
398  }
399 
400  template <typename FD>
401  void FactorGraph<FD>::erase(const size_t a) {
402  auto & va = variableAdjacencies_[a];
403  if (!va.active) return;
404 
405  for (auto it : va.factors) {
406  for (const auto variable : it->variables_) {
407  if (variable == a) continue;
408 
409  auto & factors = variableAdjacencies_[variable].factors;
410  const auto foundIt = std::find(std::begin(factors), std::end(factors), it);
411 
412  assert(foundIt != std::end(factors));
413  factors.erase(foundIt);
414  }
415  factorAdjacenciesPool_.splice(std::begin(factorAdjacenciesPool_), factorAdjacencies_, it);
416  }
417  for (const auto aa : va.vNeighbors) {
418  auto & vaa = variableAdjacencies_[aa];
419  vaa.vNeighbors.erase(std::find(std::begin(vaa.vNeighbors), std::end(vaa.vNeighbors), a));
420  }
421 
422  va.factors.clear();
423  va.vNeighbors.clear();
424  va.active = false;
425  --activeVariables_;
426  }
427 
428  template <typename FD>
429  size_t FactorGraph<FD>::variableSize() const { return activeVariables_; }
430  template <typename FD>
431  size_t FactorGraph<FD>::factorSize() const { return factorAdjacencies_.size(); }
432  template <typename FD>
433  typename FactorGraph<FD>::FactorIt FactorGraph<FD>::begin() { return std::begin(factorAdjacencies_); }
434  template <typename FD>
435  typename FactorGraph<FD>::FactorIt FactorGraph<FD>::end() { return std::end(factorAdjacencies_); }
436  template <typename FD>
437  typename FactorGraph<FD>::CFactorIt FactorGraph<FD>::begin() const { return std::begin(factorAdjacencies_); }
438  template <typename FD>
439  typename FactorGraph<FD>::CFactorIt FactorGraph<FD>::end() const { return std::end(factorAdjacencies_); }
440  template <typename FD>
441  typename FactorGraph<FD>::CFactorIt FactorGraph<FD>::cbegin() const { return std::begin(factorAdjacencies_); }
442  template <typename FD>
443  typename FactorGraph<FD>::CFactorIt FactorGraph<FD>::cend() const { return std::end(factorAdjacencies_); }
444 
445  template <typename FD>
447  if (activeVariables_ == 0) return 0;
448 
449  // Find first active variable
450  size_t retval = 0;
451  while (!variableAdjacencies_[retval].active) ++retval;
452 
453  // Find the neighbors of this variable, and whether there's a factor
454  // with all of them.
455  const auto & vNeighbors = getVariables(retval);
456 
457  // We want the variable with the minimum size factor, where the factor
458  // already exists (so we don't have to allocate anything).
459  bool factorExists = false;
460  if (vNeighbors.size() > 0) {
461  const auto factorIt = findFactorByVariables(variableAdjacencies_[vNeighbors[0]].factors, vNeighbors);
462  factorExists = factorIt != std::end(variableAdjacencies_[vNeighbors[0]].factors);
463  }
464 
465  size_t minCost = F[retval];
466  for (auto n : vNeighbors)
467  minCost *= F[n];
468 
469  for (size_t next = retval + 1; next < variableAdjacencies_.size(); ++next) {
470  if (!variableAdjacencies_[next].active)
471  continue;
472 
473  const auto & vNeighbors = getVariables(next);
474 
475  bool newExists = false;
476  if (vNeighbors.size() > 0) {
477  const auto factorIt = findFactorByVariables(variableAdjacencies_[vNeighbors[0]].factors, vNeighbors);
478  newExists = factorIt != std::end(variableAdjacencies_[vNeighbors[0]].factors);
479  }
480 
481  // If we already have a factor, there's no point in looking at this
482  // variable.
483  if (!newExists && factorExists) continue;
484 
485  // Otherwise compute its cost
486  size_t newCost = F[next];
487  for (auto n : vNeighbors)
488  newCost *= F[n];
489 
490  // If we didn't have a factor, or the new cost is less than the old
491  // one, we select this variable.
492  if ((newExists && !factorExists) || (newCost < minCost)) {
493  retval = next;
494  minCost = newCost;
495  }
496  }
497  return retval;
498  }
499 }
500 
501 #endif
AIToolbox::Factored::FactorGraph::getVariables
const Variables & getVariables(size_t variable) const
This function returns all variables adjacent to a factor adjacent to the input variable.
Definition: FactorGraph.hpp:331
AIToolbox::Factored::FactorGraph::FactorNode::getData
FactorData & getData()
Definition: FactorGraph.hpp:44
AIToolbox::Factored::FactorGraph::reset
void reset(size_t variables)
This function re-initializes the graph from scratch as if it was just being built.
Definition: FactorGraph.hpp:317
Core.hpp
AIToolbox::Factored::PartialKeys
std::vector< size_t > PartialKeys
Definition: Types.hpp:63
AIToolbox::Factored::FactorGraph::value_type
FactorData value_type
Definition: FactorGraph.hpp:52
AIToolbox::Factored::FactorGraph::erase
void erase(size_t variable)
This function partially removes an variable from the graph.
Definition: FactorGraph.hpp:401
AIToolbox::Factored::FactorGraph::FactorNode::getVariables
const Variables & getVariables() const
Definition: FactorGraph.hpp:42
AIToolbox::Factored::FactorGraph::FactorIt
typename FactorList::iterator FactorIt
Definition: FactorGraph.hpp:48
AIToolbox::Factored::FactorGraph::operator=
FactorGraph & operator=(const FactorGraph &)=delete
Deleted assignment operator.
AIToolbox::Factored::FactorGraph::iterator
FactorIt iterator
Definition: FactorGraph.hpp:53
AIToolbox::Factored::FactorGraph::FactorNode
Definition: FactorGraph.hpp:35
AIToolbox::Factored::FactorGraph::Variables
PartialKeys Variables
Definition: FactorGraph.hpp:33
AIToolbox::Factored::FactorGraph::FactorItList
std::vector< FactorIt > FactorItList
Definition: FactorGraph.hpp:50
AIToolbox::Factored::FactorGraph::getFactors
const FactorItList & getFactors(size_t variable) const
This function returns all factors adjacent to the given variable.
Definition: FactorGraph.hpp:326
AIToolbox::Factored::FactorGraph::FactorList
std::list< FactorNode > FactorList
Definition: FactorGraph.hpp:47
AIToolbox::Factored::FactorGraph::variableSize
size_t variableSize() const
This function returns the number of variables still in the graph.
Definition: FactorGraph.hpp:429
AIToolbox::Factored::FactorGraph::cbegin
CFactorIt cbegin() const
This function provides a const iterator to the beginning of the internal factor list.
Definition: FactorGraph.hpp:441
AIToolbox::Factored::FactorGraph::FactorNode::getData
const FactorData & getData() const
Definition: FactorGraph.hpp:43
AIToolbox::Factored::FactorGraph::cend
CFactorIt cend() const
This function provides a const interactor to the end of the internal factor list.
Definition: FactorGraph.hpp:443
AIToolbox::Factored::FactorGraph
This class offers a minimal interface to manager a factor graph.
Definition: FactorGraph.hpp:31
AIToolbox::Factored::Factors
std::vector< size_t > Factors
Definition: Types.hpp:62
AIToolbox::Factored::FactorGraph::factorSize
size_t factorSize() const
This function returns the number of factors still in the graph.
Definition: FactorGraph.hpp:431
AIToolbox::Factored::FactorGraph::end
FactorIt end()
This function provides an editable interactor to the end of the internal factor list.
Definition: FactorGraph.hpp:435
AIToolbox::Factored::FactorGraph::bestVariableToRemove
size_t bestVariableToRemove(const Factors &F) const
This function returns the variable which is the cheapest to remove with GenericVariableElimination.
Definition: FactorGraph.hpp:446
AIToolbox::Factored::FactorGraph::FactorGraph
FactorGraph(size_t variables)
Basic constructor.
Definition: FactorGraph.hpp:282
AIToolbox::Factored::FactorGraph::CFactorIt
typename FactorList::const_iterator CFactorIt
Definition: FactorGraph.hpp:49
AIToolbox::Factored
Definition: GraphUtils.hpp:12
Types.hpp
AIToolbox::Factored::FactorGraph::getFactor
FactorIt getFactor(const Variables &variables)
This function returns an iterator to a factor adjacent to the given variables.
Definition: FactorGraph.hpp:355
AIToolbox::Factored::FactorGraph::const_iterator
CFactorIt const_iterator
Definition: FactorGraph.hpp:54
AIToolbox::Factored::FactorGraph::begin
FactorIt begin()
This function provides an editable iterator to the beginning of the internal factor list.
Definition: FactorGraph.hpp:433
AIToolbox::set_union_inplace
void set_union_inplace(std::vector< T > &lhs, const std::vector< T > &rhs)
This function performs an inplace union of two sorted sets.
Definition: Core.hpp:288