--- 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})
--- 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();
--- 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 *
--- /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())
+ ;
+}
+
--- /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
+
--- 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
-------
--- 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`)::
--- 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"
--- 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.