Cleaned up variable elimination code in ClusterGraph
[libdai.git] / include / dai / properties.h
1 /* This file is part of libDAI - http://www.libdai.org/
2 *
3 * libDAI is licensed under the terms of the GNU General Public License version
4 * 2, or (at your option) any later version. libDAI is distributed without any
5 * warranty. See the file COPYING for more details.
6 *
7 * Copyright (C) 2006-2009 Joris Mooij [joris dot mooij at libdai dot org]
8 * Copyright (C) 2006-2007 Radboud University Nijmegen, The Netherlands
9 */
10
11
12 /// \file
13 /// \brief Defines the Property and PropertySet classes, which are mainly used for managing parameters of inference algorithms
14
15
16 #ifndef __defined_libdai_properties_h
17 #define __defined_libdai_properties_h
18
19
20 #include <iostream>
21 #include <sstream>
22 #include <boost/any.hpp>
23 #include <map>
24 #include <vector>
25 #include <typeinfo>
26 #include <dai/exceptions.h>
27 #include <dai/util.h>
28 #include <boost/lexical_cast.hpp>
29
30
31 namespace dai {
32
33
34 /// Type of the key of a Property
35 typedef std::string PropertyKey;
36
37 /// Type of the value of a Property
38 typedef boost::any PropertyValue;
39
40 /// A Property is a pair of a key and a corresponding value
41 typedef std::pair<PropertyKey, PropertyValue> Property;
42
43
44 /// Writes a Property object (key-value pair) to an output stream
45 std::ostream& operator<< ( std::ostream & os, const Property &p );
46
47
48 /// Represents a set of properties, mapping keys (of type PropertyKey) to values (of type PropertyValue)
49 /** Properties are used for specifying parameters of algorithms in a convenient way, where the values of
50 * the parameters can be of different types (e.g., strings, doubles, integers, enums). A PropertySet is
51 * an attempt to mimic the functionality of a Python dictionary object in C++, using the boost::any class.
52 *
53 * A PropertySet can be converted to and from a string, using the following format:
54 *
55 * <tt>[key1=val1,key2=val2,...,keyn=valn]</tt>
56 *
57 * That is,
58 * - the whole PropertySet is wrapped in square brackets ("[", "]")
59 * - all properties in the PropertySet are seperated by a comma (",")
60 * - each Property consists of:
61 * - the name of the key
62 * - an equality sign ("=")
63 * - its value (represented as a string)
64 *
65 * Also, a PropertySet provides functionality for converting the representation of
66 * individual values from some arbitrary type to and from std::string.
67 *
68 * \note Not all types are automatically supported; if a type is unknown, an UNKNOWN_PROPERTY_TYPE
69 * exception is thrown. Adding support for a new type can be done in the body of the
70 * operator<<(std::ostream &, const Property &).
71 */
72 class PropertySet : private std::map<PropertyKey, PropertyValue> {
73 public:
74 /// \name Constructors and destructors
75 //@{
76 /// Default constructor
77 PropertySet() {}
78
79 /// Construct from a string
80 /** \param s string in the format <tt>"[key1=val1,key2=val2,...,keyn=valn]"</tt>
81 */
82 PropertySet( const std::string& s ) {
83 std::stringstream ss;
84 ss << s;
85 ss >> *this;
86 }
87 //@}
88
89 /// \name Setting property keys/values
90 //@{
91 /// Sets a property (a key \a key with a corresponding value \a val)
92 PropertySet& Set( const PropertyKey& key, const PropertyValue& val ) {
93 this->operator[](key) = val;
94 return *this;
95 }
96
97 /// Set properties according to \a newProps, overriding properties that already exist with new values
98 PropertySet& Set( const PropertySet& newProps ) {
99 const std::map<PropertyKey, PropertyValue> *m = &newProps;
100 foreach(value_type i, *m)
101 Set( i.first, i.second );
102 return *this;
103 }
104
105 /// Shorthand for (temporarily) adding properties
106 /** \par Example:
107 \code
108 PropertySet p()("method","BP")("verbose",1)("tol",1e-9)
109 \endcode
110 */
111 PropertySet operator()( const PropertyKey& key, const PropertyValue& val ) const {
112 PropertySet copy = *this;
113 return copy.Set(key,val);
114 }
115
116 /// Sets a property (a key \a key with a corresponding value \a val, which is first converted from \a ValueType to string)
117 /** The implementation makes use of boost::lexical_cast.
118 * \tparam ValueType Type from which the value should be cast
119 * \throw IMPOSSIBLE_TYPECAST if the type cast cannot be done
120 */
121 template<typename ValueType>
122 PropertySet& setAsString( const PropertyKey& key, ValueType& val ) {
123 try {
124 return Set( key, boost::lexical_cast<std::string>(val) );
125 } catch( boost::bad_lexical_cast & ) {
126 DAI_THROWE(IMPOSSIBLE_TYPECAST,"Cannot cast value of property '" + key + "' to string.");
127 }
128 }
129
130 /// Converts the type of the property value corresponding with \a key from string to \a ValueType (if necessary)
131 /** The implementation makes use of boost::lexical_cast
132 * \tparam ValueType Type to which the value should be cast
133 * \throw IMPOSSIBLE_TYPECAST if the type cast cannot be done
134 */
135 template<typename ValueType>
136 void ConvertTo( const PropertyKey& key ) {
137 PropertyValue val = Get(key);
138 if( val.type() != typeid(ValueType) ) {
139 DAI_ASSERT( val.type() == typeid(std::string) );
140 try {
141 Set(key, boost::lexical_cast<ValueType>(GetAs<std::string>(key)));
142 } catch(boost::bad_lexical_cast &) {
143 DAI_THROWE(IMPOSSIBLE_TYPECAST,"Cannot cast value of property '" + key + "' from string to desired type.");
144 }
145 }
146 }
147 //@}
148
149 //@}
150
151 /// \name Queries
152 //@{
153 /// Check if a property with the given \a key is defined
154 bool hasKey( const PropertyKey& key ) const {
155 PropertySet::const_iterator x = find(key);
156 return (x != this->end());
157 }
158
159 /// Returns a set containing all keys
160 std::set<PropertyKey> keys() const {
161 std::set<PropertyKey> res;
162 const_iterator i;
163 for( i = begin(); i != end(); i++ )
164 res.insert( i->first );
165 return res;
166 }
167
168 /// Gets the value corresponding to \a key
169 /** \throw OBJECT_NOT_FOUND if the key cannot be found in \c *this
170 */
171 const PropertyValue& Get( const PropertyKey& key ) const {
172 PropertySet::const_iterator x = find(key);
173 if( x == this->end() )
174 DAI_THROWE(OBJECT_NOT_FOUND,"PropertySet::Get cannot find property '" + key + "'");
175 return x->second;
176 }
177
178 /// Gets the value corresponding to \a key, cast to \a ValueType
179 /** \tparam ValueType Type to which the value should be cast
180 * \throw OBJECT_NOT_FOUND if the key cannot be found in \c *this
181 * \throw IMPOSSIBLE_TYPECAST if the type cast cannot be done
182 */
183 template<typename ValueType>
184 ValueType GetAs( const PropertyKey& key ) const {
185 try {
186 return boost::any_cast<ValueType>(Get(key));
187 } catch( const boost::bad_any_cast & ) {
188 DAI_THROWE(IMPOSSIBLE_TYPECAST,"Cannot cast value of property '" + key + "' to desired type.");
189 return ValueType();
190 }
191 }
192
193 /// Gets the value corresponding to \a key, cast to \a ValueType, converting from a string if necessary
194 /** If the type of the value is already equal to \a ValueType, no conversion is done.
195 * Otherwise, the type of the value should be a std::string, in which case boost::lexical_cast is
196 * used to convert this to \a ValueType.
197 * \tparam ValueType Type to which the value should be cast/converted
198 * \throw OBJECT_NOT_FOUND if the key cannot be found in \c *this
199 * \throw IMPOSSIBLE_TYPECAST if the type cast cannot be done
200 */
201 template<typename ValueType>
202 ValueType getStringAs( const PropertyKey& key ) const {
203 PropertyValue val = Get(key);
204 if( val.type() == typeid(ValueType) ) {
205 return boost::any_cast<ValueType>(val);
206 } else if( val.type() == typeid(std::string) ) {
207 try {
208 return boost::lexical_cast<ValueType>(GetAs<std::string>(key));
209 } catch(boost::bad_lexical_cast &) {
210 DAI_THROWE(IMPOSSIBLE_TYPECAST,"Cannot cast value of property '" + key + "' from string to desired type.");
211 }
212 } else
213 DAI_THROWE(IMPOSSIBLE_TYPECAST,"Cannot cast value of property '" + key + "' from string to desired type.");
214 return ValueType();
215 }
216 //@}
217
218 /// \name Input/output
219 //@{
220 /// Writes a PropertySet object to an output stream.
221 /** It uses the format <tt>"[key1=val1,key2=val2,...,keyn=valn]"</tt>.
222 * \note Only a subset of all possible types is supported (see the implementation of this function).
223 * Adding support for more types has to be done by hand.
224 * \throw UNKNOWN_PROPERTY_TYPE if the type of a property value is not supported.
225 */
226 friend std::ostream& operator<< ( std::ostream& os, const PropertySet& ps );
227
228 /// Reads a PropertySet object from an input stream.
229 /** It expects a string in the format <tt>"[key1=val1,key2=val2,...,keyn=valn]"</tt>.
230 * Values are stored as strings.
231 * \throw MALFORMED_PROPERTY if the string is not in the expected format
232 */
233 friend std::istream& operator>> ( std::istream& is, PropertySet& ps );
234 //@}
235 };
236
237
238 } // end of namespace dai
239
240
241 #endif