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