Graphical Model + BOOST

This commit is contained in:
tqlong
2010-08-15 20:11:20 +00:00
parent 862c4f6df6
commit b8d2cf5048
11 changed files with 251 additions and 248 deletions
@@ -8,6 +8,7 @@ set(SOURCES
assignment
factor_template
factor_graph
naive_inference
inference
logarithm
gm
@@ -6,29 +6,34 @@ BEGIN_GRAPHICAL_MODEL_NAMESPACE;
// check if the values are in variables' value set
bool Assignment::checkFiniteValueIntegrity() const
{
for (Assignment::const_iterator it = this->begin(); it != this->end(); it++)
BOOST_FOREACH (const value_type& pair, *this)
{
if (it->first->type() != VARIABLE_FINITE) return false;
const Variable* var = (const Variable*) it->first;
const Value& val = it->second;
if (pair.first->type() != VARIABLE_FINITE) return false;
const Variable* var = (const Variable*) pair.first;
const Value& val = pair.second;
if ((int) val < var->cardinality()) return false;
}
return true;
// for (Assignment::const_iterator it = this->begin(); it != this->end(); it++)
// {
// if (it->first->type() != VARIABLE_FINITE) return false;
// const Variable* var = (const Variable*) it->first;
// const Value& val = it->second;
// if ((int) val < var->cardinality()) return false;
// }
// return true;
}
void Assignment::print(const std::string& name) const
{
cout << name; if (!name.empty()) cout << " = ";
const Assignment& a = *this;
if (a.begin() == a.end())
int i = 0;
cout << "(";
BOOST_FOREACH (const value_type& p, (*this))
{
cout << "()" << endl;
return;
if (i++ > 0) cout << ", ";
cout << p.first->name() << " = " << FINITE_VALUE(p.second);
}
gm::Assignment::const_iterator jt = a.begin();
cout << "(" << jt->first->name() << " = " << FINITE_VALUE(jt->second);
for (jt++; jt != a.end(); jt++)
cout << ", " << jt->first->name() << " = " << FINITE_VALUE(jt->second);
cout << ")" << endl;
}
@@ -37,10 +42,10 @@ void Assignment::print(const std::string& name) const
bool Assignment::agree(const Assignment& a) const
{
ValueCompare less;
for (Assignment::const_iterator it = this->begin(); it != this->end(); it++)
BOOST_FOREACH (const value_type& p, (*this))
{
const Variable* var = it->first;
const Value& val = it->second;
const Variable* var = p.first;
const Value& val = p.second;
Assignment::const_iterator aIt = a.find(var);
if (aIt == a.end()) continue; // a does not have assignment for var
@@ -4,7 +4,6 @@
#include <map>
#include <vector>
#include <set>
#include <cassert>
#include "gm.h"
BEGIN_GRAPHICAL_MODEL_NAMESPACE;
@@ -25,9 +24,9 @@ template<typename _Tp, typename _Alloc = std::allocator<_Tp> >
class Vector : public std::vector<_Tp, _Alloc>
{
public:
typedef typename std::vector<_Tp, _Alloc>::value_type value_type;
typedef typename std::vector<_Tp, _Alloc>::size_type size_type;
typedef typename std::vector<_Tp, _Alloc>::allocator_type allocator_type;
typedef typename std::vector<_Tp, _Alloc>::value_type value_type;
typedef typename std::vector<_Tp, _Alloc>::size_type size_type;
typedef typename std::vector<_Tp, _Alloc>::allocator_type allocator_type;
public:
Vector () : std::vector<_Tp, _Alloc>() {}
Vector(const allocator_type& __a) : std::vector<_Tp, _Alloc>(__a) {}
@@ -35,6 +34,10 @@ public:
const allocator_type& __a = allocator_type()) : std::vector<_Tp, _Alloc>(__n, __value, __a) {}
Vector& operator << (const value_type& x) { this->push_back(x); return *this; }
void fill(const value_type& x)
{
for (unsigned int i = 0; i < this->size(); i++) (*this)[i] = x;
}
};
/** Augment the std::Map with contains() */
@@ -43,9 +46,9 @@ template <typename _Key, typename _Tp, typename _Compare = std::less<_Key>,
class Map : public std::map<_Key, _Tp, _Compare, _Alloc>
{
public:
typedef typename std::map<_Key, _Tp, _Compare, _Alloc>::key_type key_type;
typedef typename std::map<_Key, _Tp, _Compare, _Alloc>::value_type value_type;
typedef typename std::map<_Key, _Tp, _Compare, _Alloc>::mapped_type mapped_type;
typedef typename std::map<_Key, _Tp, _Compare, _Alloc>::key_type key_type;
typedef typename std::map<_Key, _Tp, _Compare, _Alloc>::value_type value_type;
typedef typename std::map<_Key, _Tp, _Compare, _Alloc>::mapped_type mapped_type;
bool contains(const key_type& x) const { return this->find(x) != this->end(); }
};
@@ -54,9 +57,10 @@ public:
template <typename A, typename B> class DualMap
{
public:
typedef std::pair<A, B> pair_type;
typedef Map<A, B> forward_map_type;
typedef Map<B, A> reverse_map_type;
typedef std::pair<A, B> pair_type;
typedef std::pair<A, B> reverse_pair_type;
typedef Map<A, B> forward_map_type;
typedef Map<B, A> reverse_map_type;
public:
void set(const A& a, const B& b) { mapA[a] = b; mapB[b] = a; }
bool containsForward(const A& a) const { return mapA.contains(a); }
@@ -64,13 +68,13 @@ public:
B getForward(const A& a) const
{
typename forward_map_type::const_iterator it = mapA.find(a);
assert(it != mapA.end());
DEBUG_ASSERT(it != mapA.end());
return (it->second);
}
A getReverse(const B& b) const
{
typename reverse_map_type::const_iterator it = mapB.find(b);
assert(it != mapB.end());
DEBUG_ASSERT(it != mapB.end());
return (it->second);
}
int size() const { return mapA.size(); }
@@ -1,13 +1,6 @@
#ifndef FACTOR_GRAPH_H
#define FACTOR_GRAPH_H
#include <cassert>
#include <set>
#include <string>
#include <vector>
#include <map>
#include <sstream>
#include <iostream>
#include "gm.h"
@@ -95,10 +88,10 @@ template <typename _F> void FactorGraph<_F>::add(const factor_type& f)
vertexMap_[(void*) f_] = vf_;
const Domain& dom = f_->domain();
for (unsigned int i = 0; i < dom.size(); i++)
BOOST_FOREACH(const Variable* var, dom)
{
vertex_type v;
void* key = (void*) dom[i];
void* key = (void*) var;
if (vertexMap_.contains(key))
v = vertexMap_[key];
else
@@ -116,18 +109,16 @@ template <typename _F> void FactorGraph<_F>::add(const factor_type& f)
template <typename _F> void FactorGraph<_F>::print() const
{
for (unsigned int i = 0; i < vertices_.size(); i++)
BOOST_FOREACH(const vertex_type& u, vertices_)
{
const vertex_type& u = vertices_[i];
const vertex_vector_type& nb = neighbors(u);
cout << "Vertex: " << (u->isVariable() ? "Variable " : "Factor ");
if (u->isVariable()) { u->variable()->print(); cout << endl; }
else { cout << endl; factor(u).print(); }
cout << " Neighbors:";
for (unsigned int j = 0; j < nb.size(); j++)
BOOST_FOREACH(const vertex_type& v, nb)
{
const vertex_type& v = nb[j];
cout << " " << (v->type() == 0 ? v->variable()->name() + " (Variable)" : "(Factor)");
}
cout << endl;
@@ -1,6 +1,7 @@
#ifndef FACTOR_TEMPLATE_H
#define FACTOR_TEMPLATE_H
#include <algorithm>
#include "gm.h"
BEGIN_GRAPHICAL_MODEL_NAMESPACE;
@@ -34,7 +35,7 @@ public:
factor_value_type& operator[](const Assignment& a);
/** For get(a), the assignment could be a superset of the domain */
factor_value_type get(const Assignment& a);
factor_value_type get(const Assignment& a) const;
/** Return the domain of this factor */
const Domain& domain();
@@ -85,26 +86,22 @@ template <typename _V>
TableF<_V>::TableF(const Domain& dom, const Assignment& res) : dom_(dom)
{
Assignment temp;
for (unsigned int i = 0; i < dom.size(); i++)
DEBUG_ASSERT(dom[i]->type() == VARIABLE_FINITE);
BOOST_FOREACH(const Variable* v, dom)
DEBUG_ASSERT(v->type() == VARIABLE_FINITE);
genAssignments(dom, 0, res, temp);
}
// remove assignments that do not agree with variable in a assignment
template <typename _V>
void TableF<_V>::restricted(const Assignment& a)
{
Vector<typename TableF::iterator> its;
for (typename TableF::iterator it = this->begin(); it != this->end(); it++)
for (typename TableF::iterator it = this->begin(); it != this->end();)
{
const Assignment& b = it->first;
if (!b.agree(a)) {
// b.print("erase");
its << it;
}
if (!b.agree(a))
this->erase(it++);
else
it++;
}
for (typename Vector<typename TableF::iterator>::iterator it = its.begin(); it != its.end(); it++)
this->erase(*it);
}
template <typename _V>
@@ -124,24 +121,24 @@ template <typename _V>
void TableF<_V>::print(const std::string& name) const
{
cout << name; if (!name.empty()) cout << " = " << endl;
for (typename TableF::const_iterator it = this->begin(); it != this->end(); it++)
BOOST_FOREACH(const typename TableF::value_type& p, *this)
{
const gm::Assignment& a = it->first;
const factor_value_type& val = it->second;
const gm::Assignment& a = p.first;
const factor_value_type& val = p.second;
cout << val << " <-- "; a.print();
}
}
template <typename _V>
typename TableF<_V>::factor_value_type TableF<_V>::get(const Assignment& a)
typename TableF<_V>::factor_value_type TableF<_V>::get(const Assignment& a) const
{
// check if dom is a subset of variables in a
Assignment temp;
for (unsigned int i = 0; i < dom_.size(); i++)
BOOST_FOREACH(const Variable* v, dom_)
{
Assignment::const_iterator it = a.find(dom_[i]);
Assignment::const_iterator it = a.find(v);
if (it == a.end()) return 0.0;
else temp[dom_[i]] = (*it).second;
else temp[v] = (*it).second;
}
return this->operator [](temp);
}
@@ -15,6 +15,7 @@
// the following order of include statements is important
#include <fastlib/fastlib.h>
#include <boost/foreach.hpp>
#include "common_types.h"
#include "value.h"
#include "variable.h"
@@ -15,10 +15,10 @@ int main(int argc, char** argv)
template <typename Inference, typename Variable>
void printBelief(typename Inference::belief_type blf, Variable* var)
{
for (typename Inference::belief_type::iterator it = blf.begin(); it != blf.end(); it++)
BOOST_FOREACH(const typename Inference::belief_type::value_type& p, blf)
// for (typename Inference::belief_type::iterator it = blf.begin(); it != blf.end(); it++)
{
const gm::Value& val = (*it).first;
cout << (var->valueMap()->getForward(FINITE_VALUE(val))) << " = (" << (*it).second << ") ";
cout << (var->valueMap()->getForward(FINITE_VALUE(p.first))) << " = (" << p.second << ") ";
}
cout << endl;
// cout << "equal = " << (b.size() < 2 ? 0 : b[0] == b[1]) << endl;
@@ -78,6 +78,7 @@ void testNaiveInference()
e[sprinklet] = 1;
f1.restricted(e);
f2.restricted(e);
e.print("Evidence");
Graph fg;
fg.add(f1);
@@ -89,10 +90,10 @@ void testNaiveInference()
bp.run();
belief_map_type beliefs = bp.beliefs();
for (belief_map_type::iterator it = beliefs.begin(); it != beliefs.end(); it++)
BOOST_FOREACH (const belief_map_type::value_type& blf, beliefs)
{
cout << (*it).first->name() << " belief: ";
printBelief<Inference, Variable>((*it).second, (Variable*) (it->first));
cout << blf.first->name() << " belief: ";
printBelief<Inference, Variable>(blf.second, (Variable*) (blf.first));
}
}
@@ -1,183 +1,6 @@
#ifndef INFERENCE_H
#define INFERENCE_H
#include "gm.h"
BEGIN_GRAPHICAL_MODEL_NAMESPACE;
/**
* Naive inference implementation, use for testing correctness of other inference algorithm
* It calculates the belief of each variable node in the graph by summing up all possible
* products of factors in the graph
*/
template <typename _F>
class NaiveInference
{
public:
typedef _F factor_type;
typedef FactorGraph<_F> graph_type;
typedef typename _F::const_iterator assignment_const_iterator;
typedef typename _F::factor_value_type factor_value_type;
typedef typename FactorGraph<_F>::vertex_type vertex_type;
typedef typename FactorGraph<_F>::vertex_vector_type vertex_vector_type;
typedef Map<Value, factor_value_type, ValueCompare> belief_type;
typedef Map<const Variable*, belief_type> belief_map_type;
public:
/** Constructor, preparing to make inference on a factor graph */
NaiveInference(const graph_type& graph);
/** The inference algorithm */
void run();
/** Return the result as a belief map (from variables to their beliefs) */
belief_map_type beliefs() const { return beliefs_; }
/** Return belief of certain variable */
belief_type belief(const Variable* var) const;
protected:
const graph_type& graph_;
/** To mark visited vertex and cluster index (connected component) of each factor */
Map<vertex_type, bool> visited_;
Map<int, vertex_vector_type> factorClusters_;
/** The result */
belief_map_type beliefs_;
/** The main calculation, summing up all possible products of factor */
void visitFactors(const vertex_vector_type& factors, unsigned int index, factor_value_type currentVal, const Assignment& currentAsgn);
/** Prepare the order of calculation by depth first search the graph */
void DFSvisit(const vertex_type& u, int cluster);
void DFSorder();
/** Initialize and normalize the beliefs */
void initBeliefs();
void normalizeBeliefs();
};
template <typename _F> NaiveInference<_F>::NaiveInference(const graph_type& graph)
: graph_(graph)
{
}
template <typename _F> void NaiveInference<_F>::run()
{
// preparing the order of calculation
DFSorder();
initBeliefs();
// visit the factors according to theirs connected components
for (typename Map<int, vertex_vector_type>::const_iterator it = factorClusters_.begin();
it != factorClusters_.end(); it++)
{
const vertex_vector_type& factors = (*it).second;
visitFactors(factors, 0, factor_value_type(1.0), Assignment());
}
// normalize the results
normalizeBeliefs();
}
// Find the connected components of all factors by DFS
template <typename _F> void NaiveInference<_F>::DFSorder()
{
const vertex_vector_type& vertices = graph_.vertices();
for (unsigned int i = 0; i < vertices.size(); i++)
visited_[vertices[i]] = false;
factorClusters_.clear();
int cluster = 0;
for (unsigned int i = 0; i < vertices.size(); i++) if (!visited_[vertices[i]])
{
DFSvisit(vertices[i], cluster);
cluster++;
}
}
template <typename _F> void NaiveInference<_F>::DFSvisit(const vertex_type& u, int cluster)
{
if (u->isFactor()) factorClusters_[cluster] << u;
visited_[u] = true;
const vertex_vector_type& nb = graph_.neighbors(u);
for (unsigned int i = 0; i < nb.size(); i++)
{
const vertex_type& v = nb[i];
if (!visited_[v]) DFSvisit(v, cluster);
}
}
template <typename _F> void NaiveInference<_F>::visitFactors(const vertex_vector_type& factors, unsigned int index,
factor_value_type currentVal, const Assignment& currentAsgn)
{
if (index == factors.size()) // if we have the product of factors
{
for (Assignment::const_iterator it = currentAsgn.begin(); it != currentAsgn.end(); it++)
{
const Variable* var = (*it).first;
const Value& val = (*it).second;
beliefs_[var][val] += currentVal; // add it to the belief of each variable in the assignment
}
return;
}
const factor_type& f = graph_.factor(factors[index]);
for (assignment_const_iterator it = f.begin(); it != f.end(); it++) // iterate through all assignment that agrees with the current assignment
{
const Assignment& a = (*it).first;
factor_value_type val = (*it).second;
if (!currentAsgn.agree(a)) continue; // only proceed if current assignmet agrees with new assignment
Assignment newAsgn(currentAsgn);
newAsgn.insert(a.begin(), a.end());
visitFactors(factors, index+1, currentVal*val, newAsgn);
}
}
// set the belief of all variables to zeros
template <typename _F> void NaiveInference<_F>::initBeliefs()
{
const vertex_vector_type& vertices = graph_.vertices();
for (unsigned int i = 0; i < vertices.size(); i++)
{
const vertex_type& u = vertices[i];
if (u->isVariable())
{
const Variable* var = (const Variable*) u->variable();
for (int val = 0; val < var->cardinality(); val++)
beliefs_[var][val] = factor_value_type(0.0);
}
}
}
// normalize the beliefs
template <typename _F> void NaiveInference<_F>::normalizeBeliefs()
{
for (typename belief_map_type::iterator it = beliefs_.begin(); it != beliefs_.end(); it++)
{
factor_value_type sum = factor_value_type(0.0);
belief_type& blf = (*it).second;
for (typename belief_type::iterator bIt = blf.begin(); bIt != blf.end(); bIt++)
sum += (*bIt).second;
if (sum < factor_value_type(1e-15)) // sum is ZERO
{
for (typename belief_type::iterator bIt = blf.begin(); bIt != blf.end(); bIt++)
(*bIt).second = factor_value_type(1.0) / factor_value_type(blf.size());
}
else
{
for (typename belief_type::iterator bIt = blf.begin(); bIt != blf.end(); bIt++)
(*bIt).second /= sum;
}
}
}
template <typename _F>
typename NaiveInference<_F>::belief_type NaiveInference<_F>::belief(const Variable* var) const
{
typename belief_map_type::const_iterator it = beliefs_.find(var);
DEBUG_ASSERT(it != beliefs_.end());
return (*it).second;
}
END_GRAPHICAL_MODEL_NAMESPACE;
#include "naive_inference.h"
#endif // INFERENCE_H
@@ -0,0 +1,180 @@
#ifndef NAIVE_INFERENCE_H
#define NAIVE_INFERENCE_H
#include "gm.h"
BEGIN_GRAPHICAL_MODEL_NAMESPACE;
/**
* Naive inference implementation, use for testing correctness of other inference algorithm
* It calculates the belief of each variable node in the graph by summing up all possible
* products of factors in the graph
*/
template <typename _F>
class NaiveInference
{
public:
typedef _F factor_type;
typedef FactorGraph<_F> graph_type;
typedef typename _F::const_iterator assignment_const_iterator;
typedef typename _F::factor_value_type factor_value_type;
typedef typename FactorGraph<_F>::vertex_type vertex_type;
typedef typename FactorGraph<_F>::vertex_vector_type vertex_vector_type;
typedef Map<Value, factor_value_type, ValueCompare> belief_type;
typedef Map<const Variable*, belief_type> belief_map_type;
public:
/** Constructor, preparing to make inference on a factor graph */
NaiveInference(const graph_type& graph);
/** The inference algorithm */
void run();
/** Return the result as a belief map (from variables to their beliefs) */
belief_map_type beliefs() const { return beliefs_; }
/** Return belief of certain variable */
belief_type belief(const Variable* var) const;
protected:
const graph_type& graph_;
/** To mark visited vertex and cluster index (connected component) of each factor */
Map<vertex_type, bool> visited_;
Map<int, vertex_vector_type> factorClusters_;
/** The result */
belief_map_type beliefs_;
/** The main calculation, summing up all possible products of factor */
void visitFactors(const vertex_vector_type& factors, unsigned int index, factor_value_type currentVal, const Assignment& currentAsgn);
/** Prepare the order of calculation by depth first search the graph */
void DFSvisit(const vertex_type& u, int cluster);
void DFSorder();
/** Initialize and normalize the beliefs */
void initBeliefs();
void normalizeBeliefs();
};
template <typename _F> NaiveInference<_F>::NaiveInference(const graph_type& graph)
: graph_(graph)
{
}
template <typename _F> void NaiveInference<_F>::run()
{
// preparing the order of calculation
DFSorder();
initBeliefs();
// visit the factors according to theirs connected components
typedef Map<int, vertex_vector_type> map_t;
BOOST_FOREACH(const typename map_t::value_type& p, factorClusters_)
{
const vertex_vector_type& factors = p.second;
visitFactors(factors, 0, factor_value_type(1.0), Assignment());
}
// normalize the results
normalizeBeliefs();
}
// Find the connected components of all factors by DFS
template <typename _F> void NaiveInference<_F>::DFSorder()
{
const vertex_vector_type& vertices = graph_.vertices();
for (unsigned int i = 0; i < vertices.size(); i++)
visited_[vertices[i]] = false;
factorClusters_.clear();
int cluster = 0;
BOOST_FOREACH (const vertex_type& u, vertices)
if (!visited_[u])
{
DFSvisit(u, cluster);
cluster++;
}
}
template <typename _F> void NaiveInference<_F>::DFSvisit(const vertex_type& u, int cluster)
{
if (u->isFactor()) factorClusters_[cluster] << u;
visited_[u] = true;
const vertex_vector_type& nb = graph_.neighbors(u);
BOOST_FOREACH (const vertex_type& v, nb)
if (!visited_[v]) DFSvisit(v, cluster);
}
template <typename _F> void NaiveInference<_F>::visitFactors(const vertex_vector_type& factors, unsigned int index,
factor_value_type currentVal, const Assignment& currentAsgn)
{
if (index == factors.size()) // if we have the product of factors
{
BOOST_FOREACH(const Assignment::value_type& p, currentAsgn)
{
const Variable* var = p.first;
const Value& val = p.second;
beliefs_[var][val] += currentVal; // add it to the belief of each variable in the assignment
}
return;
}
const factor_type& f = graph_.factor(factors[index]);
BOOST_FOREACH (const typename factor_type::value_type& p, f) // iterate through all assignment that agrees with the current assignment
{
const Assignment& a = p.first;
const factor_value_type& val = p.second;
if (!currentAsgn.agree(a)) continue; // only proceed if current assignmet agrees with new assignment
Assignment newAsgn(currentAsgn);
newAsgn.insert(a.begin(), a.end());
visitFactors(factors, index+1, currentVal*val, newAsgn);
}
}
// set the belief of all variables to zeros
template <typename _F> void NaiveInference<_F>::initBeliefs()
{
const vertex_vector_type& vertices = graph_.vertices();
BOOST_FOREACH (const vertex_type& u, vertices)
{
if (u->isVariable())
{
const Variable* var = (const Variable*) u->variable();
for (int val = 0; val < var->cardinality(); val++)
beliefs_[var][val] = factor_value_type(0.0);
}
}
}
// normalize the beliefs
template <typename _F> void NaiveInference<_F>::normalizeBeliefs()
{
for (typename belief_map_type::iterator it = beliefs_.begin(); it != beliefs_.end(); it++)
{
factor_value_type sum = factor_value_type(0.0);
belief_type& blf = (*it).second;
for (typename belief_type::iterator bIt = blf.begin(); bIt != blf.end(); bIt++)
sum += (*bIt).second;
if (sum < factor_value_type(1e-15)) // sum is ZERO
{
for (typename belief_type::iterator bIt = blf.begin(); bIt != blf.end(); bIt++)
(*bIt).second = factor_value_type(1.0) / factor_value_type(blf.size());
}
else
{
for (typename belief_type::iterator bIt = blf.begin(); bIt != blf.end(); bIt++)
(*bIt).second /= sum;
}
}
}
template <typename _F>
typename NaiveInference<_F>::belief_type NaiveInference<_F>::belief(const Variable* var) const
{
typename belief_map_type::const_iterator it = beliefs_.find(var);
DEBUG_ASSERT(it != beliefs_.end());
return (*it).second;
}
END_GRAPHICAL_MODEL_NAMESPACE;
#endif // NAIVE_INFERENCE_H
@@ -73,9 +73,8 @@ public:
void print(const std::string& name = "") const
{
cout << name; if (!name.empty()) cout << " = " << endl;
for (gm::Universe::const_iterator i = this->begin(); i != this->end(); i++)
BOOST_FOREACH(const Variable* var, *this)
{
const gm::Variable* var = (*i);
var->print();
cout << endl;
// std::cout << "name = " << var->name();
@@ -77,9 +77,10 @@ public:
void print() const
{
cout << "name = " << name_ << " (discrete):";
const typename int_value_map_type::forward_map_type& map = valueMap_->forwardMap();
for (typename int_value_map_type::forward_map_type::const_iterator it = map.begin(); it != map.end(); it++)
cout << " " << (it->first) << " <--> " << (it->second);
typedef typename int_value_map_type::forward_map_type map_t;
const map_t& map = valueMap_->forwardMap();
BOOST_FOREACH (const typename map_t::value_type& p, map)
cout << " " << (p.first) << " <--> " << (p.second);
}
protected:
int cardinality_;