Skip to content

Commit

Permalink
Merge branch 'release/0.1.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
wmayner committed May 12, 2015
2 parents e789429 + dae74d2 commit ed19899
Show file tree
Hide file tree
Showing 8 changed files with 1,364 additions and 1,724 deletions.
1 change: 0 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
buildcython:
cython -v -t --cplus pyemd/emd.pyx
python setup.py build_ext --inplace
mv emd*.so pyemd/emd.so

clean:
rm -rf pyemd/emd.so
Expand Down
2 changes: 1 addition & 1 deletion pyemd/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
"""

__title__ = 'pyemd'
__version__ = '0.0.10'
__version__ = '0.1.0'
__author__ = 'Will Mayner'
__author_email__ = 'wmayner@gmail.com'
__author_website__ = 'http://willmayner.com'
Expand Down
2,949 changes: 1,328 additions & 1,621 deletions pyemd/emd.cpp

Large diffs are not rendered by default.

51 changes: 9 additions & 42 deletions pyemd/emd.pyx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# distutils: language = c++

from libcpp.vector cimport vector
Expand All @@ -20,46 +22,31 @@ cdef extern from "lib/emd_hat.hpp":
# NumPy array to C++ vector conversion
# ====================================

# See http://stackoverflow.com/a/2434208/1085344
cdef vector[double] _c_array_to_vector(double* array, int length):
cdef vector[double] output_vector
output_vector.reserve(length)
output_vector.assign(array, array + length)
return output_vector


# See https://github.com/cython/cython/wiki/tutorials-NumpyPointerToC
@cython.boundscheck(False)
@cython.wraparound(False)
def _np_array_to_vector(np.ndarray[double, ndim=1, mode="c"] array):
cdef vector[double] _np_array_to_vector(
np.ndarray[double, ndim=1, mode="c"] array):
"""Convert a 1D numpy array to a C++ vector.

:param array: A 1D numpy array of ``np.double``.
"""
cdef vector[double] output_vector
cdef int length
length = array.size

# Pass pointer to beginning of numpy array data
output_vector = _c_array_to_vector(&array[0], length)

output_vector.reserve(array.size)
output_vector.assign(&array[0], &array[0] + array.size)
return output_vector


def _2d_np_array_to_2d_vector(np.ndarray[double, ndim=2, mode="c"] matrix):
"""Convert a 2D numpy array to a C++ vector of vectors.
:param array: A 2D numpy array of ``np.double``.
"""
cdef vector[vector[double]] output_matrix
output_matrix.resize(array.shape[0])
cdef vector[double] c_row

for row in matrix:
c_row = _np_array_to_vector(row)
output_matrix.push_back(c_row)

for i in range(matrix.shape[0]):
output_matrix[i] = _np_array_to_vector(row)
return output_matrix


Expand All @@ -78,33 +65,13 @@ def emd(np.ndarray[double, ndim=1, mode="c"] first_signature,
:math:`N`..
:param distance_matrix: A 2D numpy array of ``np.double``, of size
:math:`N \cross N`.
"""
# Validation
if ([arg.dtype for arg in [first_signature, second_signature,
distance_matrix]] != [np.dtype('float64')] * 3):
raise ValueError("Signatures and ditance matrix must be of dtype " +
"'float64'.")
N = first_signature.shape[0]
if (N != second_signature.shape[0]):
raise ValueError("Signatures must be the same size.")
if ((N != distance_matrix.shape[0]) or (N != distance_matrix.shape[1])):
raise ValueError("Distance matrix must be NxN, where N is the length of" +
"the signatures.")
if (first_signature.ndim != 1) or (first_signature.ndim !=
second_signature.ndim):
raise ValueError("Signatures must be 1-dimensional.")
if (distance_matrix.ndim != 2):
raise ValueError("Distance matrix must be 2-dimensional.")

# Convert numpy input to C++ vectors
cdef vector[double] c_first_signature
cdef vector[double] c_second_signature
cdef vector[vector[double]] c_distance_matrix
c_first_signature = _np_array_to_vector(first_signature)
c_second_signature = _np_array_to_vector(second_signature)
c_distance_matrix = _2d_np_array_to_2d_vector(distance_matrix)

return emd_hat_gd_metric_double(c_first_signature,
c_second_signature,
c_distance_matrix)
12 changes: 6 additions & 6 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Cython==0.20.1
numpy==1.8.0
py==1.4.20
pytest==2.5.2
wheel==0.23.0
twine==1.3.0
Cython>=0.20.2
numpy>=1.8.0, <2.0.0
py>=1.4.20
pytest>=2.5.2
wheel>=0.23.0
twine>=1.3.0
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ def finalize_options(self):
with open('README.rst') as f:
readme = f.read()

requires = ['numpy >=1.8.0, < 2.0.0']
requires = ['numpy >=1.8.0, <2.0.0']

setup(
name='pyemd',
version='0.0.10',
version='0.1.0',
description=("A Python wrapper for Ofir Pele and Michael Werman's " +
"implementation of the Earth Mover's Distance."),
long_description=readme,
Expand Down
Empty file removed test/__init__.py
Empty file.
69 changes: 18 additions & 51 deletions test/test_pyemd.py
Original file line number Diff line number Diff line change
@@ -1,62 +1,29 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import unittest
import numpy as np
from pyemd import emd


class TestEmd(unittest.TestCase):
def test_case_1():
first_signature = np.array([0.0, 1.0])
second_signature = np.array([5.0, 3.0])
distance_matrix = np.array([[0.0, 0.5],
[0.5, 0.0]])
assert 3.5 == emd(first_signature, second_signature, distance_matrix)

def test_case_1(self):
first_signature = np.array([0.0, 1.0])
second_signature = np.array([5.0, 3.0])
distance_matrix = np.array([[0.0, 0.5],
[0.5, 0.0]])
assert 3.5 == emd(first_signature, second_signature, distance_matrix)

def test_case_2(self):
first_signature = np.array([1.0, 1.0])
second_signature = np.array([1.0, 1.0])
distance_matrix = np.array([[0.0, 1.0],
[1.0, 0.0]])
assert 0.0 == emd(first_signature, second_signature, distance_matrix)
def test_case_2():
first_signature = np.array([1.0, 1.0])
second_signature = np.array([1.0, 1.0])
distance_matrix = np.array([[0.0, 1.0],
[1.0, 0.0]])
assert 0.0 == emd(first_signature, second_signature, distance_matrix)

def test_case_3(self):
first_signature = np.array([6.0, 1.0])
second_signature = np.array([1.0, 7.0])
distance_matrix = np.array([[0.0, 0.0],
[0.0, 0.0]])
assert 0.0 == emd(first_signature, second_signature, distance_matrix)

def test_error_different_signature_lengths(self):
first_signature = np.array([6.0, 1.0, 9.0])
second_signature = np.array([1.0, 7.0])
distance_matrix = np.array([[0.0, 1.0],
[1.0, 0.0]])
with self.assertRaises(ValueError):
emd(first_signature, second_signature, distance_matrix)

def test_error_wrong_distance_matrix_shape(self):
first_signature = np.array([6.0, 1.0])
second_signature = np.array([1.0, 7.0])
distance_matrix = np.array([[0.0, 1.0, 0.4],
[1.0, 0.0]])
with self.assertRaises(ValueError):
emd(first_signature, second_signature, distance_matrix)

def test_error_wrong_signature_ndim(self):
first_signature = np.array([[6.0, 1.0]])
second_signature = np.array([1.0, 7.0])
distance_matrix = np.array([[0.0, 1.0],
[1.0, 0.0]])
with self.assertRaises(ValueError):
emd(first_signature, second_signature, distance_matrix)

def test_error_wrong_distance_matrix_ndim(self):
first_signature = np.array([6.0, 1.0])
second_signature = np.array([1.0, 7.0])
distance_matrix = np.array([[[0.0, 1.0],
[1.0, 0.0]]])
with self.assertRaises(ValueError):
emd(first_signature, second_signature, distance_matrix)
def test_case_3():
first_signature = np.array([6.0, 1.0])
second_signature = np.array([1.0, 7.0])
distance_matrix = np.array([[0.0, 0.0],
[0.0, 0.0]])
assert 0.0 == emd(first_signature, second_signature, distance_matrix)

0 comments on commit ed19899

Please sign in to comment.