Moved alias code from tests/testdai.cpp to src/alldai.cpp
authorJoris Mooij <joris.mooij@tuebingen.mpg.de>
Fri, 15 Jan 2010 10:39:20 +0000 (11:39 +0100)
committerJoris Mooij <joris.mooij@tuebingen.mpg.de>
Fri, 15 Jan 2010 10:39:20 +0000 (11:39 +0100)
include/dai/alldai.h
include/dai/exceptions.h
include/dai/mr.h
include/dai/treeep.h
src/alldai.cpp
src/exceptions.cpp
tests/testdai.cpp

index 19f00f0..ed04ce2 100644 (file)
@@ -11,6 +11,7 @@
 
 /// \file
 /// \brief Main libDAI header file. It \#includes all other libDAI headers.
+/// \todo Update documentation about aliases (add a section to the fileformats)
 
 
 #ifndef __defined_libdai_alldai_h
@@ -74,11 +75,27 @@ InfAlg *newInfAlg( const std::string &name, const FactorGraph &fg, const Propert
  *  \param fg The FactorGraph that the algorithm should be applied to.
  *  \return Returns a pointer to the new InfAlg object; it is the responsibility of the caller to delete it later.
  *  \throw UNKNOWN_DAI_ALGORITHM if the requested name is not known/compiled in.
- *  \todo Support aliases like in testdai.cpp
+ *  \todo Allow alias substitution
  */
 InfAlg *newInfAlgFromString( const std::string &nameOpts, const FactorGraph &fg );
 
 
+/// Extracts the name and property set from a string \a s in the format "name[key1=val1,key2=val2,...]" or "name"
+std::pair<std::string, PropertySet> parseNameProperties( const std::string &s );
+
+
+/// Extracts the name and property set from a string \a s in the format "name[key1=val1,key2=val2,...]" or "name", performing alias substitution
+/** Alias substitution is performed as follows: as long as name appears as a key in \a aliases,
+ *  it is substituted by its value. Properties in \a s override those of the alias (in case of
+ *  recursion, the "outer" properties override those of the "inner" aliases).
+ */
+std::pair<std::string, PropertySet> parseNameProperties( const std::string &s, const std::map<std::string,std::string> &aliases );
+
+
+/// Reads aliases from file named \a filename
+std::map<std::string,std::string> readAliasesFile( const std::string &filename );
+
+
 /// Contains the names of all inference algorithms compiled into libDAI.
 static const char* DAINames[] = {
     ExactInf::Name,
index df75624..d3e7034 100644 (file)
@@ -87,6 +87,7 @@ class Exception : public std::runtime_error {
                    UNKNOWN_PROPERTY,
                    MALFORMED_PROPERTY,
                    NOT_ALL_PROPERTIES_SPECIFIED,
+                   INVALID_ALIAS,
                    CANNOT_READ_FILE,
                    CANNOT_WRITE_FILE,
                    INVALID_FACTORGRAPH_FILE,
index 38b9377..d5a109f 100644 (file)
@@ -33,7 +33,7 @@ namespace dai {
 
 /// Approximate inference algorithm by Montanari and Rizzo [\ref MoR05]
 /** \author Bastian Wemmenhove wrote the original implementation before it was merged into libDAI
- *  \todo Clean up code (use a BipartiteGraph-like implementation for the graph structure)
+ *  \todo Clean up code (use a BipartiteGraph-like implementation for the graph structure, and BBP for response propagation).
  */
 class MR : public DAIAlgFG {
     private:
index 14e69f3..2851da9 100644 (file)
@@ -4,15 +4,15 @@
  *  2, or (at your option) any later version. libDAI is distributed without any
  *  warranty. See the file COPYING for more details.
  *
- *  Copyright (C) 2006-2009  Joris Mooij  [joris dot mooij at libdai dot org]
+ *  Copyright (C) 2006-2010  Joris Mooij  [joris dot mooij at libdai dot org]
  *  Copyright (C) 2006-2007  Radboud University Nijmegen, The Netherlands
  */
 
 
 /// \file
 /// \brief Defines class TreeEP, which implements Tree Expectation Propagation
-/// \todo Clean up the TreeEP code (by making JTree more powerful, e.g., by
-/// adding Pearl's cutset algorithm and propagation with an arbitrary root)
+/// \todo Clean up the TreeEP code (exploiting that a large part of the code
+/// is just a special case of JTree).
 
 
 #ifndef __defined_libdai_treeep_h
@@ -72,7 +72,11 @@ class TreeEP : public JTree {
         static const char *Name;
 
     private:
-        /// Stores the data structures needed to efficiently update the approximation of an off-tree factor
+        /// Stores the data structures needed to efficiently update the approximation of an off-tree factor.
+        /** The TreeEP object stores a TreeEPSubTree object for each off-tree factor.
+         *  It stores the approximation of that off-tree factor, which is represented 
+         *  as a distribution on a subtree of the main tree.
+         */
         class TreeEPSubTree {
             private:
                 /// Outer region pseudomarginals (corresponding with the \f$\tilde f_i(x_j,x_k)\f$ in [\ref MiQ04])
@@ -119,7 +123,7 @@ class TreeEP : public JTree {
                     return *this;
                 }
 
-                /// Construct from super tree
+                /// Construct from \a subRTree, which is a subtree of the main tree \a jt_RTree, with distribution represented by \a jt_Qa and \a jt_Qb, for off-tree factor \a I
                 TreeEPSubTree( const RootedTree &subRTree, const RootedTree &jt_RTree, const std::vector<Factor> &jt_Qa, const std::vector<Factor> &jt_Qb, const Factor *I );
             //@}
 
@@ -191,7 +195,6 @@ class TreeEP : public JTree {
         virtual std::string printProperties() const;
     //@}
 
-
     private:
         /// Helper function for constructors
         void construct( const RootedTree &tree );
index d6688d6..1468bb6 100644 (file)
@@ -85,4 +85,75 @@ InfAlg *newInfAlgFromString( const std::string &nameOpts, const FactorGraph &fg
 }
 
 
+std::pair<std::string, PropertySet> parseNameProperties( const std::string &s ) {
+    string::size_type pos = s.find_first_of('[');
+    string name;
+    PropertySet opts;
+    if( pos == string::npos ) {
+        name = s;
+    } else {
+        name = s.substr(0,pos);
+
+        stringstream ss;
+        ss << s.substr(pos,s.length());
+        ss >> opts;
+    }
+    return make_pair(name,opts);
+}
+
+
+std::pair<std::string, PropertySet> parseNameProperties( const std::string &s, const std::map<std::string,std::string> &aliases ) {
+    // break string into method[properties]
+    pair<string,PropertySet> ps = parseNameProperties(s);
+    bool looped = false;
+
+    // as long as 'method' is an alias, update:
+    while( aliases.find(ps.first) != aliases.end() && !looped ) {
+        string astr = aliases.find(ps.first)->second;
+        pair<string,PropertySet> aps = parseNameProperties(astr);
+        if( aps.first == ps.first )
+            looped = true;
+        // override aps properties by ps properties
+        aps.second.Set( ps.second );
+        // replace ps by aps
+        ps = aps;
+        // repeat until method name == alias name ('looped'), or
+        // there is no longer an alias 'method'
+    }
+
+    return ps;
+}
+
+
+std::map<std::string,std::string> readAliasesFile( const std::string &filename ) {
+    // Read aliases
+    map<string,string> result;
+    ifstream infile;
+    infile.open( filename.c_str() );
+    if( infile.is_open() ) {
+        while( true ) {
+            string line;
+            getline( infile,line );
+            if( infile.fail() )
+                break;
+            if( (!line.empty()) && (line[0] != '#') ) {
+                string::size_type pos = line.find(':',0);
+                if( pos == string::npos )
+                    DAI_THROWE(INVALID_ALIAS,"Invalid alias '" + line + "'");
+                else {
+                    string::size_type posl = line.substr(0, pos).find_last_not_of(" \t");
+                    string key = line.substr(0, posl + 1);
+                    string::size_type posr = line.substr(pos + 1, line.length()).find_first_not_of(" \t");
+                    string val = line.substr(pos + 1 + posr, line.length());
+                    result[key] = val;
+                }
+            }
+        }
+        infile.close();
+    } else
+        DAI_THROWE(CANNOT_READ_FILE,"Error opening aliases file " + filename);
+    return result;
+}
+
+
 } // end of namespace dai
index 6a24138..0ddd130 100644 (file)
@@ -28,6 +28,7 @@ namespace dai {
         "Unknown Property",
         "Malformed Property",
         "Not all mandatory Properties specified",
+        "Invalid alias",
         "Cannot read file",
         "Cannot write file",
         "Invalid FactorGraph file",
index b43adda..ebdd890 100644 (file)
@@ -143,54 +143,6 @@ class TestDAI {
 };
 
 
-pair<string, PropertySet> parseMethodRaw( const string &s ) {
-    string::size_type pos = s.find_first_of('[');
-    string name;
-    PropertySet opts;
-    if( pos == string::npos ) {
-        name = s;
-    } else {
-        name = s.substr(0,pos);
-
-        stringstream ss;
-        ss << s.substr(pos,s.length());
-        ss >> opts;
-    }
-    return make_pair(name,opts);
-}
-
-
-pair<string, PropertySet> parseMethod( const string &_s, const map<string,string> & aliases ) {
-    // break string into method[properties]
-    pair<string,PropertySet> ps = parseMethodRaw(_s);
-    bool looped = false;
-
-    // as long as 'method' is an alias, update:
-    while( aliases.find(ps.first) != aliases.end() && !looped ) {
-        string astr = aliases.find(ps.first)->second;
-        pair<string,PropertySet> aps = parseMethodRaw(astr);
-        if( aps.first == ps.first )
-            looped = true;
-        // override aps properties by ps properties
-        aps.second.Set( ps.second );
-        // replace ps by aps
-        ps = aps;
-        // repeat until method name == alias name ('looped'), or
-        // there is no longer an alias 'method'
-    }
-
-    // check whether name is valid
-    size_t n = 0;
-    for( ; strlen( DAINames[n] ) != 0; n++ )
-        if( ps.first == DAINames[n] )
-            break;
-    if( strlen( DAINames[n] ) == 0 && (ps.first != "LDPC") )
-        DAI_THROWE(UNKNOWN_DAI_ALGORITHM,string("Unknown DAI algorithm \"") + ps.first + string("\" in \"") + _s + string("\""));
-
-    return ps;
-}
-
-
 Real clipReal( Real x, Real minabs ) {
     if( abs(x) < minabs )
         return minabs;
@@ -253,32 +205,8 @@ int main( int argc, char *argv[] ) {
     try {
         // Read aliases
         map<string,string> Aliases;
-        if( !aliases.empty() ) {
-            ifstream infile;
-            infile.open (aliases.c_str());
-            if (infile.is_open()) {
-                while( true ) {
-                    string line;
-                    getline(infile,line);
-                    if( infile.fail() )
-                        break;
-                    if( (!line.empty()) && (line[0] != '#') ) {
-                        string::size_type pos = line.find(':',0);
-                        if( pos == string::npos )
-                            DAI_THROWE(RUNTIME_ERROR,"Invalid alias");
-                        else {
-                            string::size_type posl = line.substr(0, pos).find_last_not_of(" \t");
-                            string key = line.substr(0, posl + 1);
-                            string::size_type posr = line.substr(pos + 1, line.length()).find_first_not_of(" \t");
-                            string val = line.substr(pos + 1 + posr, line.length());
-                            Aliases[key] = val;
-                        }
-                    }
-                }
-                infile.close();
-            } else
-                DAI_THROWE(RUNTIME_ERROR,"Error opening aliases file");
-        }
+        if( !aliases.empty() )
+            Aliases = readAliasesFile( aliases );
 
         FactorGraph fg;
         fg.ReadFromFile( filename.c_str() );
@@ -303,7 +231,16 @@ int main( int argc, char *argv[] ) {
         cout << endl;
 
         for( size_t m = 0; m < methods.size(); m++ ) {
-            pair<string, PropertySet> meth = parseMethod( methods[m], Aliases );
+            // parse method
+            pair<string, PropertySet> meth = parseNameProperties( methods[m], Aliases );
+
+            // check whether name is valid
+            size_t n = 0;
+            for( ; strlen( DAINames[n] ) != 0; n++ )
+                if( meth.first == DAINames[n] )
+                    break;
+            if( strlen( DAINames[n] ) == 0 )
+                DAI_THROWE(UNKNOWN_DAI_ALGORITHM,string("Unknown DAI algorithm \"") + meth.first + string("\" in \"") + methods[m] + string("\""));
 
             if( vm.count("tol") )
                 meth.second.Set("tol",tol);