allow adding and viewing sequences to an analysis from python
authorDiane Trout <diane@caltech.edu>
Thu, 30 Nov 2006 02:23:08 +0000 (02:23 +0000)
committerDiane Trout <diane@caltech.edu>
Thu, 30 Nov 2006 02:23:08 +0000 (02:23 +0000)
this required a way to deal with the collection of sequences
returned by Mussa::sequences()

the python wiki had some suggestions (though their code didn't compile)
http://wiki.python.org/moin/boost.python/StlContainers

py/CMakeLists.txt
py/mussa.cpp
py/stl_container_adapter.hpp [new file with mode: 0644]
py/test/TestMussa.py [new file with mode: 0644]

index c16083f33b645aec7b2c2f185d7f84ee6751058c..00a18b5969a069f2699c6c70874cc8cfcea81eaf 100644 (file)
@@ -35,7 +35,6 @@ IF(BOOST_PYTHON_LIBRARY)
                       COMMAND cp libmussa.so mussa.so
                       DEPENDS mussa)
   ENDIF(WIN32)
-  LINK_DIRECTORIES(${CMAKE_BINARY_DIR}/alg)
   TARGET_LINK_LIBRARIES(mussa 
                           mussa_core
                           ${BOOST_PYTHON_LIBRARY}
@@ -46,7 +45,6 @@ IF(BOOST_PYTHON_LIBRARY)
                           optimized ${QT_QTCORE_LIBRARY} 
                           debug ${QT_QTCORE_LIBRARY_DEBUG} 
                         )
-
   GET_MUSSA_COMPILE_FLAGS(PY_CFLAGS)
   GET_MUSSA_LINK_FLAGS(PY_LDFLAGS)
   SET_SOURCE_FILES_PROPERTIES(
@@ -64,6 +62,7 @@ IF(BOOST_PYTHON_LIBRARY)
     SET(PYTEST_DIR ${CMAKE_SOURCE_DIR}/py/test/)
     ADD_TEST(TestSequence ${PYTHON_EXECUTABLE} ${PYTEST_DIR}/TestSequence.py)
     ADD_TEST(TestFlp ${PYTHON_EXECUTABLE} ${PYTEST_DIR}/TestFlp.py)
+    ADD_TEST(TestMussa ${PYTHON_EXECUTABLE} ${PYTEST_DIR}/TestMussa.py)
   ENDIF(PYTHON_EXECUTABLE)
 ELSE(BOOST_PYTHON_LIBRARY)
 ENDIF(BOOST_PYTHON_LIBRARY)
index f38063e2c3ed7a9f5e49f38e109b25ae60b19cb1..a203c7a709706c2421797f76db4f3988109f440e 100644 (file)
@@ -2,11 +2,29 @@
 namespace py = boost::python;
 
 #include "alg/mussa.hpp"
+#include "alg/sequence.hpp"
+#include "stl_container_adapter.hpp"
 
 void export_mussa()
 {
   void (Mussa::*load_mupa_string)(std::string) = &Mussa::load_mupa_file;
+  void (Mussa::*append_sequence_ref)(const Sequence&) = &Mussa::append_sequence;
+  void (Mussa::*append_sequence_ptr)(const boost::shared_ptr<Sequence>) = &Mussa::append_sequence;
 
+  py::class_<Mussa::vector_sequence_type>("Sequences")
+    .def("__len__", &Mussa::vector_sequence_type::size, "return length of sequences")
+    .def("__contains__", &std_item<Mussa::vector_sequence_type>::in)
+    .def("__iter__", py::iterator<Mussa::vector_sequence_type>())
+    .def("clear", &Mussa::vector_sequence_type::clear, "remove all the sequences")
+    .def("append", &std_item<Mussa::vector_sequence_type>::add,
+        py::with_custodian_and_ward<1,2>()) // to let container keep value
+    .def("__getitem__", &std_item<Mussa::vector_sequence_type>::get,
+        py::return_value_policy<py::copy_non_const_reference>())
+    .def("__setitem__", &std_item<Mussa::vector_sequence_type>::set,
+        py::with_custodian_and_ward<1,2>()) // to let container keep value
+    .def("__delitem__", &std_item<Mussa::vector_sequence_type>::del)
+  ;
   py::class_<Mussa>("Mussa")
     .def("save", &Mussa::save)
     .def("load", &Mussa::load, "Load previous run analysis")
@@ -25,8 +43,8 @@ void export_mussa()
     .add_property("analysisModeName", &Mussa::get_analysis_mode_name)
     .def("analyze", &Mussa::analyze, "Run the analysis")
     .def("paths", &Mussa::paths, py::return_internal_reference<>())
-    //.def("sequences", &Mussa::sequences)
-    //.def("addSequence", &Mussa::append_sequence)  
+    .def("sequences", &Mussa::sequences, py::return_internal_reference<>())
+    .def("add_sequence", append_sequence_ref)  
   ;
 
   py::enum_<Mussa::analysis_modes>("analysis_modes")
diff --git a/py/stl_container_adapter.hpp b/py/stl_container_adapter.hpp
new file mode 100644 (file)
index 0000000..8758247
--- /dev/null
@@ -0,0 +1,108 @@
+#ifndef STL_CONTAINER_ADAPTER_HPP_
+#define STL_CONTAINER_ADAPTER_HPP_
+
+#include <algorithm>
+#include <boost/python.hpp>
+
+// This template started off life at
+// http://wiki.python.org/moin/boost.python/StlContainers
+
+void IndexError() { PyErr_SetString(PyExc_IndexError, "Index out of range"); }
+template<typename T>
+struct std_item
+{
+    typedef typename T::value_type V;
+    static V& get(T& x, int i)
+    {
+        if( i<0 ) i+=x.size();
+        if( i>=0 && i<x.size() ) return x[i];
+        IndexError();
+    }
+    static void set(T& x, int i, V const& v)
+    {
+        if( i<0 ) i+=x.size();
+        if( i>=0 && i<x.size() ) x[i]=v;
+        else IndexError();
+    }
+    static void del(T& x, int i)
+    {
+        if( i<0 ) i+=x.size();
+        if( i>=0 && i<x.size() ) {
+          typename T::iterator itor = x.begin();
+          itor += i; 
+          x.erase(itor);
+        } else IndexError();
+    }
+    static void add(T& x, V& v)
+    {
+        x.push_back(v);
+    }
+    static bool in(T const& x, V const& v)
+    {
+        return std::find(x.begin(), x.end(), v) != x.end();
+    }    
+    static int index(T const& x, V const& v)
+    {
+        int i=0;
+        for(typename T::const_iterator it=x.begin; it!=x.end(); ++it,++i)
+          if( *it == v ) return i;
+        return -1;
+    }
+};
+
+void KeyError() { PyErr_SetString(PyExc_KeyError, "Key not found"); }
+template<typename T>
+struct map_item
+{
+    typedef typename T::key_type K;
+    typedef typename T::mapped_type V;
+    static V& get(T const& x, K const& i)
+    {
+        if( x.find(i) != x.end() ) return x[i];
+        KeyError();
+    }
+    static void set(T const& x, K const& i, V const& v)
+    {
+        x[i]=v; // use map autocreation feature
+    }
+    static void del(T const& x, K const& i)
+    {
+        if( x.find(i) != x.end() ) x.erase(i);
+        else KeyError();
+    }
+    static bool in(T const& x, K const& i)
+    {
+        return x.find(i) != x.end();
+    }
+    static boost::python::list keys(T const& x)
+    {
+        boost::python::list t;
+        for(typename T::const_iterator it=x.begin; it!=x.end(); ++it)
+          t.append(it->first);
+        return t;
+    }
+    static boost::python::list values(T const& x)
+    {
+        boost::python::list t;
+        for(typename T::const_iterator it=x.begin; it!=x.end(); ++it)
+          t.append(it->second);
+        return t;
+    }
+    static boost::python::list items(T const& x)
+    {
+        boost::python::list t;
+        for(typename T::const_iterator it=x.begin; it!=x.end(); ++it)
+          t.append(boost::python::make_tuple(it->first,it->second));
+        return t;
+    }
+    static int index(T const& x,  K const& k)
+    {
+        int i=0;
+        for(typename T::const_iterator it=x.begin; it!=x.end(); ++it,++i)
+          if( it->first == k ) return i;
+        return -1;
+    }
+};
+
+
+#endif /*STL_CONTAINER_ADAPTER_HPP_*/
diff --git a/py/test/TestMussa.py b/py/test/TestMussa.py
new file mode 100644 (file)
index 0000000..d5d91cb
--- /dev/null
@@ -0,0 +1,28 @@
+import os
+import sys
+import unittest
+
+# kinda hackish but it makes it possible to runi under ctest 
+sys.path.append(os.getcwd())
+
+import mussa
+
+class TestMussa(unittest.TestCase):
+  def testSimple(self):
+    s1 = mussa.Sequence("A"*10)
+    s2 = mussa.Sequence("GG"+"A"*8+"GG")
+    s3 = mussa.Sequence("T"*10)
+    m = mussa.Mussa()
+    m.window = 10
+    m.threshold = 8
+    m.add_sequence(s1)
+    m.add_sequence(s2)
+    m.add_sequence(s3)
+    m.analyze()
+    print m.paths()
+
+def suite():
+  return unittest.makeSuite(TestMussa, 'test')
+
+if __name__ == "__main__":
+  sys.exit(unittest.main(defaultTest='suite'))
\ No newline at end of file