# HG changeset patch # User Dmitriy Morozov <dmitriy@mrzv.org> # Date 1248975349 25200 # Node ID d39a20acb253c420ac9276030ed92441d8fa948e # Parent 4e27f1f7c169c19b6f96b3c189a09a269c9974e5 Switched PairwiseDistances in Python bindings to C++ diff -r 4e27f1f7c169 -r d39a20acb253 bindings/python/CMakeLists.txt --- a/bindings/python/CMakeLists.txt Thu Jul 30 10:23:31 2009 -0700 +++ b/bindings/python/CMakeLists.txt Thu Jul 30 10:35:49 2009 -0700 @@ -15,6 +15,7 @@ zigzag-persistence.cpp cohomology-persistence.cpp rips.cpp + distances.cpp ) set (bindings_libraries ${libraries}) diff -r 4e27f1f7c169 -r d39a20acb253 bindings/python/dionysus.cpp --- a/bindings/python/dionysus.cpp Thu Jul 30 10:23:31 2009 -0700 +++ b/bindings/python/dionysus.cpp Thu Jul 30 10:35:49 2009 -0700 @@ -12,6 +12,8 @@ void export_cohomology_persistence(); void export_rips(); +void export_pairwise_distances(); + #ifndef NO_CGAL void export_alphashapes2d(); void export_alphashapes3d(); @@ -36,6 +38,8 @@ export_cohomology_persistence(); export_rips(); + export_pairwise_distances(); + #ifndef NO_CGAL export_alphashapes2d(); export_alphashapes3d(); diff -r 4e27f1f7c169 -r d39a20acb253 bindings/python/dionysus/__init__.py --- a/bindings/python/dionysus/__init__.py Thu Jul 30 10:23:31 2009 -0700 +++ b/bindings/python/dionysus/__init__.py Thu Jul 30 10:35:49 2009 -0700 @@ -1,5 +1,5 @@ from _dionysus import * -from distances import * +from distances import l2, ExplicitDistances, points_file from zigzag import * diff -r 4e27f1f7c169 -r d39a20acb253 bindings/python/distances.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bindings/python/distances.cpp Thu Jul 30 10:35:49 2009 -0700 @@ -0,0 +1,21 @@ +#include <boost/python.hpp> +namespace bp = boost::python; + +#include "distances.h" +namespace dp = dionysus::python; + +boost::shared_ptr<dp::ListPointPairwiseDistances> init_from_list(bp::list lst) +{ + boost::shared_ptr<dp::ListPointPairwiseDistances> p(new dp::ListPointPairwiseDistances(lst)); + return p; +} + +void export_pairwise_distances() +{ + bp::class_<dp::ListPointPairwiseDistances>("PairwiseDistances", bp::no_init) + .def("__init__", bp::make_constructor(&init_from_list)) + .def("__len__", &dp::ListPointPairwiseDistances::size) + .def("__call__", &dp::ListPointPairwiseDistances::operator()) + ; +} + diff -r 4e27f1f7c169 -r d39a20acb253 bindings/python/distances.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bindings/python/distances.h Thu Jul 30 10:35:49 2009 -0700 @@ -0,0 +1,54 @@ +#include <utilities/log.h> + +#include <boost/python.hpp> +namespace bp = boost::python; + +namespace dionysus { +namespace python { + +typedef bp::list ListPoint; + +struct ListPointL2Distance: + public std::binary_function<bp::object, bp::object, double> +{ + result_type operator()(bp::object p1, bp::object p2) const + { + ListPoint lp1 = bp::extract<ListPoint>(p1), lp2 = bp::extract<ListPoint>(p2); + + AssertMsg(bp::len(lp1) == bp::len(lp2), "Points must be in the same dimension (in L2Distance): dim1=%d, dim2=%d", bp::len(lp1), bp::len(lp2)); + result_type sum = 0; + for (size_t i = 0; i < bp::len(lp1); ++i) + { + double diff = bp::extract<double>(lp1[i]) - bp::extract<double>(lp2[i]); + sum += diff*diff; + } + + return sqrt(sum); + } +}; + +class ListPointPairwiseDistances +{ + public: + typedef bp::list Container; + typedef ListPointL2Distance Distance; + typedef unsigned IndexType; + typedef Distance::result_type DistanceType; + + + ListPointPairwiseDistances(Container container): + container_(container) {} + + DistanceType operator()(IndexType a, IndexType b) const { return distance_(container_[a], container_[b]); } + + size_t size() const { return bp::len(container_); } + IndexType begin() const { return 0; } + IndexType end() const { return size(); } + + private: + Container container_; + Distance distance_; +}; + +} } // namespace dionysus::python + diff -r 4e27f1f7c169 -r d39a20acb253 doc/python/rips.rst --- a/doc/python/rips.rst Thu Jul 30 10:23:31 2009 -0700 +++ b/doc/python/rips.rst Thu Jul 30 10:35:49 2009 -0700 @@ -60,9 +60,11 @@ def __call__(self, x, y): return math.fabs(y-x) -The bindings provide a pure Python class :class:`PairwiseDistances` to deal with -explicit points in a Euclidean space. It is defined in -:sfile:`bindings/python/dionysus/distances.py`:: +The bindings expose a C++ class as a Python class :class:`PairwiseDistances` to deal with +explicit points in a Euclidean space. In pure Python it could be defined as +follows (in fact it used to be a pure Python class, and one may still find it in +:sfile:`bindings/python/dionysus/distances.py`; its performance is much slower +than its pure C++ analog):: class PairwiseDistances: def __init__(self, points, norm = l2): @@ -84,6 +86,9 @@ distances = PairwiseDistances(points) distances = ExplicitDistances(distances) +With :class:`PairwiseDistances` being a C++ class, and +:class:`ExplicitDistances` being pure Python, the speead-up seems minor. + Example ------- diff -r 4e27f1f7c169 -r d39a20acb253 doc/tutorial.rst --- a/doc/tutorial.rst Thu Jul 30 10:23:31 2009 -0700 +++ b/doc/tutorial.rst Thu Jul 30 10:35:49 2009 -0700 @@ -172,7 +172,9 @@ Currently, when the choice comes between efficiency and flexibility, the Python bindings err on the side of flexibility. There is hope that in the future the choice won't really be necessary. Meanwhile, one can use a few techniques that -speed up computation at the expense of memory: +speed up computation at the expense of memory. Note, however, that since the +recent switch of :class:`PairwiseDistances` to C++ rather than pure Python, it +is not clear whether these deliver a substantial speed-up: * To avoid (possibly expensive) computation of distances during Rips complex generation, store :class:`ExplicitDistances` (see :ref:`distances`):: diff -r 4e27f1f7c169 -r d39a20acb253 examples/rips/rips-pairwise.py --- a/examples/rips/rips-pairwise.py Thu Jul 30 10:23:31 2009 -0700 +++ b/examples/rips/rips-pairwise.py Thu Jul 30 10:35:49 2009 -0700 @@ -9,7 +9,7 @@ def main(filename, skeleton, max): points = [p for p in points_file(filename)] distances = PairwiseDistances(points) - distances = ExplicitDistances(distances) # speeds up generation of the Rips complex at the expense of memory usage + # distances = ExplicitDistances(distances) # speeds up generation of the Rips complex at the expense of memory usage rips = Rips(distances) print time.asctime(), "Rips initialized" diff -r 4e27f1f7c169 -r d39a20acb253 include/geometry/distances.h --- a/include/geometry/distances.h Thu Jul 30 10:23:31 2009 -0700 +++ b/include/geometry/distances.h Thu Jul 30 10:35:49 2009 -0700 @@ -1,6 +1,8 @@ #ifndef __DISTANCES_H__ #define __DISTANCES_H__ +#include <vector> + /** * Class: ExplicitDistances * Stores the pairwise distances of Distances_ instance passed at construction.