Added python bindings for ZigzagPersistence (as well as ImageZigzagPersistence) dev
authorDmitriy Morozov <dmitriy@mrzv.org>
Thu, 29 Jan 2009 10:16:56 -0800
branchdev
changeset 112 f209958b5c17
parent 111 958dec48d946
child 113 3e8bebb5d857
Added python bindings for ZigzagPersistence (as well as ImageZigzagPersistence)
bindings/python/CMakeLists.txt
bindings/python/dionysus.cpp
bindings/python/dionysus/__init__.py
bindings/python/python-optional.h
bindings/python/python-zigzag-persistence.h
bindings/python/simplex.cpp
bindings/python/zigzag-persistence.cpp
examples/triangle/triangle-zigzag.py
include/topology/zigzag-persistence.hpp
--- a/bindings/python/CMakeLists.txt	Wed Jan 28 14:57:17 2009 -0800
+++ b/bindings/python/CMakeLists.txt	Thu Jan 29 10:16:56 2009 -0800
@@ -1,10 +1,19 @@
+find_package                (PythonLibs)
+link_libraries              (${PYTHON_LIBRARIES})
+include_directories         (${PYTHON_INCLUDE_PATH})
 link_libraries              (${Boost_PYTHON_LIBRARY})
-include_directories         ("/usr/include/python2.6")
-link_directories            ("/usr/lib/python2.6")
+
+# currently can't build bindings with counters support, eventually FIXME
+remove_definitions          (-DCOUNTERS)
 add_library                 (_dionysus SHARED 
                                                 dionysus.cpp 
                                                 filtration.cpp
                                                 chain.cpp
                                                 static-persistence.cpp
-                                                simplex.cpp)
-target_link_libraries       (_dionysus ${libraries})                                            
+                                                simplex.cpp
+                                                zigzag-persistence.cpp
+
+                                                test-optional.cpp
+                            )
+
+target_link_libraries       (_dionysus ${libraries})
--- a/bindings/python/dionysus.cpp	Wed Jan 28 14:57:17 2009 -0800
+++ b/bindings/python/dionysus.cpp	Thu Jan 29 10:16:56 2009 -0800
@@ -7,6 +7,7 @@
 void export_filtration();
 void export_static_persistence();
 void export_chain();
+void export_zigzag_persistence();
 
 #ifdef LOGGING
 void            enable_log(std::string s)
@@ -22,6 +23,8 @@
     export_static_persistence();
     export_chain();
 
+    export_zigzag_persistence();
+
 #ifdef LOGGING
     bp::def("enable_log",           &enable_log);
 #endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bindings/python/dionysus/__init__.py	Thu Jan 29 10:16:56 2009 -0800
@@ -0,0 +1,18 @@
+from _dionysus import *
+
+
+#def init_with_data(s,v, d = None):
+#    s._cpp_init_(v)
+#    if d is not None:
+#        s.data = d
+#
+#Simplex._cpp_init_ = Simplex.__init__
+#Simplex.__init__ = init_with_data
+#
+#def data_cmp(s1, s2):
+#    return cmp(s1.data,s2.data)
+#
+#def data_dim_cmp(s1,s2):
+#    dim_cmp = cmp(s1.dimension(), s2.dimension())
+#    if dim_cmp: return dim_cmp
+#    else:       return data_cmp(s1,s2)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bindings/python/python-optional.h	Thu Jan 29 10:16:56 2009 -0800
@@ -0,0 +1,83 @@
+#ifndef __PYTHON_OPTIONAL_H__
+#define __PYTHON_OPTIONAL_H__
+
+#include <boost/python.hpp>
+
+// Taken from an email by John Wiegley; 
+// http://mail.python.org/pipermail/cplusplus-sig/2007-May/012003.html
+
+template <typename T, typename TfromPy>
+struct object_from_python
+{
+    object_from_python() 
+    {
+        boost::python::converter::registry::push_back(&TfromPy::convertible, 
+                                                      &TfromPy::construct, 
+                                                      boost::python::type_id<T>());
+    }
+};
+
+template <typename T, typename TtoPy, typename TfromPy>
+struct register_python_conversion
+{
+    register_python_conversion() 
+    {
+        boost::python::to_python_converter<T, TtoPy>();
+        object_from_python<T, TfromPy>();
+    }
+};
+
+template <typename T>
+struct python_optional : public boost::noncopyable
+{
+    struct optional_to_python
+    {
+        static PyObject * convert(const boost::optional<T>& value)
+        {
+            return (value ? boost::python::to_python_value<T>()(*value) :
+                            boost::python::detail::none());
+        }
+    };
+
+    struct optional_from_python
+    {
+        static void * convertible(PyObject * source)
+        {
+            using namespace boost::python::converter;
+
+            if (source == Py_None)
+                return source;
+
+            const registration& converters(registered<T>::converters);
+
+            if (implicit_rvalue_convertible_from_python(source, converters)) 
+            {
+                rvalue_from_python_stage1_data data = rvalue_from_python_stage1(source, converters);
+                return rvalue_from_python_stage2(source, data, converters);
+            }
+            return NULL;
+        }
+
+        static void construct(PyObject * source,
+                              boost::python::converter::rvalue_from_python_stage1_data * data)
+        {
+            using namespace boost::python::converter;
+
+            void * const storage = ((rvalue_from_python_storage<T> *) data)->storage.bytes;
+
+            if (data->convertible == source)     // == None
+                new (storage) boost::optional<T>(); // A Boost uninitialized value
+            else
+                new (storage) boost::optional<T>(*static_cast<T *>(data->convertible));
+
+            data->convertible = storage;
+        }
+    };
+
+    explicit python_optional() 
+    {
+        register_python_conversion<boost::optional<T>, optional_to_python, optional_from_python>();
+    }
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bindings/python/python-zigzag-persistence.h	Thu Jan 29 10:16:56 2009 -0800
@@ -0,0 +1,15 @@
+#ifndef __PYTHON_ZIGZAG_PERSISTENCE_H__
+#define __PYTHON_ZIGZAG_PERSISTENCE_H__
+
+#include <topology/zigzag-persistence.h>
+#include <topology/image-zigzag-persistence.h>
+#include <boost/python.hpp>
+
+//typedef         int                             BirthID;
+//typedef         boost::python::long_            BirthID;
+typedef         boost::python::object           BirthID;
+
+typedef         ZigzagPersistence<BirthID>      ZZPersistence;
+typedef         ImageZigzagPersistence<BirthID> IZZPersistence;
+
+#endif
--- a/bindings/python/simplex.cpp	Wed Jan 28 14:57:17 2009 -0800
+++ b/bindings/python/simplex.cpp	Thu Jan 29 10:16:56 2009 -0800
@@ -5,6 +5,7 @@
 #include <boost/python.hpp>
 #include <boost/python/stl_iterator.hpp>
 #include <boost/shared_ptr.hpp>
+#include <boost/functional/hash.hpp>
 using namespace boost::python;
 
 /* Various wrappers for exposing Simplex to Python */
@@ -38,6 +39,20 @@
     return p;
 }
 
+// Simplex hash
+template<class V, class T>
+size_t                              hash_simplex(const Simplex<V,T>& s)
+{
+    return boost::hash_range(s.vertices().begin(), s.vertices().end());
+}
+
+template<class V, class T>
+size_t                              eq_simplex(const Simplex<V,T>& a, const Simplex<V,T>& b)
+{
+    return vertex_comparison(a,b) == 0;
+}
+
+
 /* Comparisons */
 // VertexComparison
 template<class V, class T>
@@ -77,6 +92,9 @@
         
         .add_property("vertices",   range(&vertices_begin<Vertex,Data>, &vertices_end<Vertex,Data>))
         .def(repr(self))
+
+        .def("__hash__",            &hash_simplex<Vertex, Data>)
+        .def("__eq__",              &eq_simplex<Vertex, Data>)
     ;
 
     def("vertex_cmp",               &vertex_comparison<Vertex, Data>);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bindings/python/zigzag-persistence.cpp	Thu Jan 29 10:16:56 2009 -0800
@@ -0,0 +1,86 @@
+#include <topology/zigzag-persistence.h>
+#include <topology/image-zigzag-persistence.h>
+
+#include <boost/python.hpp>
+#include <boost/python/stl_iterator.hpp>
+#include <boost/shared_ptr.hpp>
+namespace bp = boost::python;
+
+#include "python-zigzag-persistence.h"      // defines ZZPersistence, IZZPersistence
+#include "python-optional.h"
+
+
+// ZigzagPersistence
+bp::tuple                           zzp_add(ZZPersistence& zzp, bp::object bdry, BirthID birth)
+{
+    // Make ZColumn    
+    // NB: it's extremely weird that I have to do it this way, 
+    //     but for some reason I cannot just create boundary on the stack
+    boost::shared_ptr<ZZPersistence::ZColumn> 
+                                            boundary(new ZZPersistence::ZColumn(bp::stl_input_iterator<ZZPersistence::SimplexIndex>(bdry), 
+                                                                                bp::stl_input_iterator<ZZPersistence::SimplexIndex>()));
+    boundary->sort(zzp.cmp);
+
+    ZZPersistence::SimplexIndex             i;
+    ZZPersistence::Death                    d;
+    boost::tie(i,d)                                 = zzp.add(*boundary, birth); 
+    return bp::make_tuple(i,d);
+}
+
+ZZPersistence::Death                zzp_remove(ZZPersistence& zzp, ZZPersistence::SimplexIndex s, ZZPersistence::BirthID birth)
+{
+    return zzp.remove(s, birth); 
+}
+
+
+// ImageZigzagPersistence
+bp::tuple                           izzp_add(IZZPersistence& izzp, bp::object bdry, bool subcomplex, BirthID birth)
+{
+    // Make ZColumn    
+    // NB: it's extremely weird that I have to do it this way, 
+    //     but for some reason I cannot just create boundary on the stack
+    boost::shared_ptr<IZZPersistence::ZColumn> 
+                                            boundary(new IZZPersistence::ZColumn(bp::stl_input_iterator<IZZPersistence::SimplexIndex>(bdry), 
+                                                                                 bp::stl_input_iterator<IZZPersistence::SimplexIndex>()));
+    boundary->sort(izzp.cmp);
+
+    IZZPersistence::SimplexIndex            i;
+    IZZPersistence::Death                   d;
+    boost::tie(i,d)                                 = izzp.add(*boundary, subcomplex, birth); 
+    return bp::make_tuple(i,d);
+}
+
+IZZPersistence::Death               izzp_remove(IZZPersistence& izzp, IZZPersistence::SimplexIndex s, IZZPersistence::BirthID birth)
+{
+    return izzp.remove(s, birth); 
+}
+
+
+// SimplexIndex
+template<class T>
+unsigned                            si_order(T& si)
+{
+    return si->order;
+}
+
+
+void export_zigzag_persistence()
+{
+    python_optional<BirthID>();   
+
+    bp::class_<ZZPersistence::SimplexIndex>("SimplexIndex")
+        .def("order",           &si_order<ZZPersistence::SimplexIndex>);
+    
+    bp::class_<IZZPersistence::SimplexIndex>("ISimplexIndex")
+        .def("order",           &si_order<IZZPersistence::SimplexIndex>);
+
+    bp::class_<ZZPersistence>("ZigzagPersistence")
+        .def("add",             &zzp_add)
+        .def("remove",          &zzp_remove)
+    ;
+    
+    bp::class_<IZZPersistence>("ImageZigzagPersistence")
+        .def("add",             &izzp_add)
+        .def("remove",          &izzp_remove)
+    ;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/triangle/triangle-zigzag.py	Thu Jan 29 10:16:56 2009 -0800
@@ -0,0 +1,35 @@
+from dionysus import Simplex, ZigzagPersistence, \
+                     vertex_cmp, data_cmp \
+#                    ,enable_log
+
+complex = {Simplex((0,),        0):     None,                   # A
+           Simplex((1,),        1):     None,                   # B
+           Simplex((2,),        2):     None,                   # C
+           Simplex((0,1),       2.5):   None,                   # AB
+           Simplex((1,2),       2.9):   None,                   # BC
+           Simplex((0,2),       3.5):   None,                   # CA
+           Simplex((0,1,2),     5):     None}                   # ABC
+
+print "Complex:"
+for s in sorted(complex.keys()): print s
+print
+
+#enable_log("topology/persistence")
+zz = ZigzagPersistence()
+
+# Add all the simplices
+b = 1
+for s in sorted(complex.keys(), data_cmp):
+    print "%d: Adding %s" % (b, s)
+    i,d = zz.add([complex[ss] for ss in s.boundary], b)
+    complex[s] = i
+    if d:   print "Interval (%d, %d)" % (d, b-1)
+    b += 1
+
+# Remove all the simplices
+for s in sorted(complex.keys(), reverse = True):
+    print "%d: Removing %s" % (b, s)
+    d = zz.remove(complex[s], b)
+    complex[s] = None
+    if d:   print "Interval (%d, %d)" % (d, b-1)
+    b += 1
--- a/include/topology/zigzag-persistence.hpp	Wed Jan 28 14:57:17 2009 -0800
+++ b/include/topology/zigzag-persistence.hpp	Thu Jan 29 10:16:56 2009 -0800
@@ -1,5 +1,6 @@
 #include <utilities/log.h>
 #include <boost/utility.hpp>
+#include <boost/iterator/filter_iterator.hpp>
 #include <algorithm>
 #include <utilities/indirect.h>
 #include <functional>