Skip to content

Commit

Permalink
#381: CLIQUE integration between C++ and Python.
Browse files Browse the repository at this point in the history
  • Loading branch information
annoviko committed Feb 5, 2019
1 parent 1bb32c0 commit 16aeec6
Show file tree
Hide file tree
Showing 10 changed files with 364 additions and 11 deletions.
2 changes: 2 additions & 0 deletions ccore/src/ccore.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
<ClCompile Include="differential\differ_factor.cpp" />
<ClCompile Include="interface\agglomerative_interface.cpp" />
<ClCompile Include="interface\bsas_interface.cpp" />
<ClCompile Include="interface\clique_interface.cpp" />
<ClCompile Include="interface\cure_interface.cpp" />
<ClCompile Include="interface\dbscan_interface.cpp" />
<ClCompile Include="interface\elbow_interface.cpp" />
Expand Down Expand Up @@ -151,6 +152,7 @@
<ClInclude Include="differential\solve_type.hpp" />
<ClInclude Include="interface\agglomerative_interface.h" />
<ClInclude Include="interface\bsas_interface.h" />
<ClInclude Include="interface\clique_interface.h" />
<ClInclude Include="interface\cure_interface.h" />
<ClInclude Include="interface\dbscan_interface.h" />
<ClInclude Include="interface\elbow_interface.h" />
Expand Down
6 changes: 6 additions & 0 deletions ccore/src/ccore.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,9 @@
<ClCompile Include="cluster\clique_block.cpp">
<Filter>Source Files\cluster</Filter>
</ClCompile>
<ClCompile Include="interface\clique_interface.cpp">
<Filter>Source Files\interface</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="cluster\agglomerative.hpp">
Expand Down Expand Up @@ -537,5 +540,8 @@
<ClInclude Include="cluster\clique_data.hpp">
<Filter>Source Files\cluster</Filter>
</ClInclude>
<ClInclude Include="interface\clique_interface.h">
<Filter>Source Files\interface</Filter>
</ClInclude>
</ItemGroup>
</Project>
62 changes: 62 additions & 0 deletions ccore/src/interface/clique_interface.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/**
*
* @authors Andrei Novikov ([email protected])
* @date 2014-2019
* @copyright GNU Public License
*
* GNU_PUBLIC_LICENSE
* pyclustering is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* pyclustering is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

#include "interface/clique_interface.h"

#include "cluster/clique.hpp"


pyclustering_package * clique_algorithm(const pyclustering_package * const p_sample, const std::size_t p_intervals, const std::size_t p_threshold) {
dataset input_dataset;
p_sample->extract(input_dataset);

ccore::clst::clique solver(p_intervals, p_threshold);

ccore::clst::clique_data output_result;

solver.process(input_dataset, output_result);

pyclustering_package * package = create_package_container(CLIQUE_PACKAGE_SIZE);

((pyclustering_package **) package->data)[CLIQUE_PACKAGE_INDEX_CLUSTERS] = create_package(&output_result.clusters());
((pyclustering_package **) package->data)[CLIQUE_PACKAGE_INDEX_NOISE] = create_package(&output_result.noise());

const auto & blocks = output_result.blocks();
((pyclustering_package **) package->data)[CLIQUE_PACKAGE_INDEX_LOGICAL_LOCATION] = create_package_container(blocks.size());
((pyclustering_package **) package->data)[CLIQUE_PACKAGE_INDEX_MAX_CORNER] = create_package_container(blocks.size());
((pyclustering_package **) package->data)[CLIQUE_PACKAGE_INDEX_MIN_CORNER] = create_package_container(blocks.size());
((pyclustering_package **) package->data)[CLIQUE_PACKAGE_INDEX_BLOCK_POINTS] = create_package_container(blocks.size());

pyclustering_package * logical_location = ((pyclustering_package **) package->data)[CLIQUE_PACKAGE_INDEX_LOGICAL_LOCATION];
pyclustering_package * max_corner = ((pyclustering_package **) package->data)[CLIQUE_PACKAGE_INDEX_MAX_CORNER];
pyclustering_package * min_corner = ((pyclustering_package **) package->data)[CLIQUE_PACKAGE_INDEX_MIN_CORNER];
pyclustering_package * block_points = ((pyclustering_package **) package->data)[CLIQUE_PACKAGE_INDEX_BLOCK_POINTS];

for (std::size_t i = 0; i < blocks.size(); i++) {
((pyclustering_package **) logical_location->data)[i] = create_package(&(blocks[i].get_logical_location()));
((pyclustering_package **) max_corner->data)[i] = create_package(&(blocks[i].get_spatial_block().get_max_corner()));
((pyclustering_package **) min_corner->data)[i] = create_package(&(blocks[i].get_spatial_block().get_min_corner()));
((pyclustering_package **) block_points->data)[i] = create_package(&(blocks[i].get_points()));
}

return package;
}
62 changes: 62 additions & 0 deletions ccore/src/interface/clique_interface.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/**
*
* @authors Andrei Novikov ([email protected])
* @date 2014-2019
* @copyright GNU Public License
*
* GNU_PUBLIC_LICENSE
* pyclustering is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* pyclustering is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

#pragma once


#include "interface/pyclustering_package.hpp"

#include "definitions.hpp"


/**
*
* @brief CLIQUE result is returned by pyclustering_package that consist sub-packages and this enumerator provides
* named indexes for sub-packages.
*
*/
enum clique_package_indexer {
CLIQUE_PACKAGE_INDEX_CLUSTERS = 0,
CLIQUE_PACKAGE_INDEX_NOISE,
CLIQUE_PACKAGE_INDEX_LOGICAL_LOCATION,
CLIQUE_PACKAGE_INDEX_MAX_CORNER,
CLIQUE_PACKAGE_INDEX_MIN_CORNER,
CLIQUE_PACKAGE_INDEX_BLOCK_POINTS,
CLIQUE_PACKAGE_SIZE
};


/**
*
* @brief Clustering algorithm CLIQUE returns allocated clusters.
* @details Caller should destroy returned clustering data using 'cure_data_destroy' when
* it is not required anymore.
*
* @param[in] p_sample: input data for clustering.
* @param[in] p_intervals: amount of intervals in each dimension.
* @param[in] p_threshold: minimum number of objects that should be contained by non-noise block.
*
* @return Returns pointer to cure data - clustering result that can be used for obtaining
* allocated clusters, representative points and means of each cluster.
*
*/
extern "C" DECLARATION pyclustering_package * clique_algorithm(const pyclustering_package * const p_sample, const std::size_t p_intervals, const std::size_t p_threshold);
3 changes: 3 additions & 0 deletions ccore/tst/utcore.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@
<ClCompile Include="..\src\differential\differ_factor.cpp" />
<ClCompile Include="..\src\interface\agglomerative_interface.cpp" />
<ClCompile Include="..\src\interface\bsas_interface.cpp" />
<ClCompile Include="..\src\interface\clique_interface.cpp" />
<ClCompile Include="..\src\interface\cure_interface.cpp" />
<ClCompile Include="..\src\interface\dbscan_interface.cpp" />
<ClCompile Include="..\src\interface\elbow_interface.cpp" />
Expand Down Expand Up @@ -240,6 +241,7 @@
<ClCompile Include="utest-clique.cpp" />
<ClCompile Include="utest-elbow.cpp" />
<ClCompile Include="utest-interface-bsas.cpp" />
<ClCompile Include="utest-interface-clique.cpp" />
<ClCompile Include="utest-interface-elbow.cpp" />
<ClCompile Include="utest-interface-mbsas.cpp" />
<ClCompile Include="utest-interface-ttsas.cpp" />
Expand Down Expand Up @@ -360,6 +362,7 @@
<ClInclude Include="..\src\differential\solve_type.hpp" />
<ClInclude Include="..\src\interface\agglomerative_interface.h" />
<ClInclude Include="..\src\interface\bsas_interface.h" />
<ClInclude Include="..\src\interface\clique_interface.h" />
<ClInclude Include="..\src\interface\cure_interface.h" />
<ClInclude Include="..\src\interface\dbscan_interface.h" />
<ClInclude Include="..\src\interface\elbow_interface.h" />
Expand Down
9 changes: 9 additions & 0 deletions ccore/tst/utcore.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,12 @@
<ClCompile Include="utest-clique.cpp">
<Filter>Unit Tests</Filter>
</ClCompile>
<ClCompile Include="..\src\interface\clique_interface.cpp">
<Filter>Tested Code\interface</Filter>
</ClCompile>
<ClCompile Include="utest-interface-clique.cpp">
<Filter>Unit Tests</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\src\container\adjacency.hpp">
Expand Down Expand Up @@ -745,5 +751,8 @@
<ClInclude Include="..\src\cluster\clique_block.hpp">
<Filter>Tested Code\cluster</Filter>
</ClInclude>
<ClInclude Include="..\src\interface\clique_interface.h">
<Filter>Tested Code\interface</Filter>
</ClInclude>
</ItemGroup>
</Project>
40 changes: 40 additions & 0 deletions ccore/tst/utest-interface-clique.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**
*
* @authors Andrei Novikov ([email protected])
* @date 2014-2019
* @copyright GNU Public License
*
* GNU_PUBLIC_LICENSE
* pyclustering is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* pyclustering is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

#include "gtest/gtest.h"

#include "interface/clique_interface.h"
#include "interface/pyclustering_package.hpp"

#include "utenv_utils.hpp"

#include <memory>


TEST(utest_interface_clique, clique_algorithm) {
std::shared_ptr<pyclustering_package> sample = pack(dataset({ { 1.0, 1.0 }, { 1.1, 1.0 }, { 1.2, 1.4 }, { 10.0, 10.3 }, { 10.1, 10.2 }, { 10.2, 10.4 } }));

pyclustering_package * result = clique_algorithm(sample.get(), 2, 0);
ASSERT_EQ((std::size_t) CLIQUE_PACKAGE_SIZE, result->size);

delete result;
}
40 changes: 33 additions & 7 deletions pyclustering/cluster/clique.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@

import itertools

from collections import deque
from pyclustering.core.wrapper import ccore_library

import pyclustering.core.clique_wrapper as wrapper


try:
Expand Down Expand Up @@ -189,11 +191,11 @@ def get_corners(self):


class clique_block:
def __init__(self):
self.__logical_location = []
self.__spatial_location = None
self.__points = []
self.__visited = False
def __init__(self, logical_location=None, spatial_location=None, points=None, visited=False):
self.__logical_location = logical_location or []
self.__spatial_location = spatial_location
self.__points = points or []
self.__visited = visited

def __str__(self):
return str(self.__logical_location)
Expand Down Expand Up @@ -288,6 +290,10 @@ def __init__(self, data, amount_intervals, density_threshold, **kwargs):
self.__amount_intervals = amount_intervals
self.__density_threshold = density_threshold

self.__ccore = kwargs.get('ccore', True)
if self.__ccore:
self.__ccore = ccore_library.workable()

self.__clusters = []
self.__noise = []

Expand All @@ -298,11 +304,31 @@ def __init__(self, data, amount_intervals, density_threshold, **kwargs):


def process(self):
if self.__ccore:
self.__process_by_ccore()
else:
self.__process_by_python()

return self


def __process_by_ccore(self):
(self.__clusters, self.__noise, block_logical_locations, block_max_corners, block_min_corners, block_points) = \
wrapper.clique(self.__data, self.__amount_intervals, self.__density_threshold)

amount_cells = len(block_logical_locations)
for i in range(amount_cells):
self.__cells.append(clique_block(block_logical_locations[i],
spatial_block(block_max_corners[i], block_min_corners[i]),
block_points[i],
True))


def __process_by_python(self):
self.__create_grid()
self.__allocate_clusters()

self.__cells_map.clear()
return self


def get_clusters(self):
Expand Down
10 changes: 6 additions & 4 deletions pyclustering/cluster/tests/integration/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,17 @@
"""

import unittest;
from pyclustering.tests.suite_holder import suite_holder;
import unittest
from pyclustering.tests.suite_holder import suite_holder

# Generate images without having a window appear.
import matplotlib;
matplotlib.use('Agg');
import matplotlib
matplotlib.use('Agg')


from pyclustering.cluster.tests.integration import it_agglomerative as cluster_agglomerative_integration_tests
from pyclustering.cluster.tests.integration import it_bsas as cluster_bsas_integration_tests
from pyclustering.cluster.tests.integration import it_clique as cluster_clique_integration_tests
from pyclustering.cluster.tests.integration import it_cure as cluster_cure_integration_tests
from pyclustering.cluster.tests.integration import it_dbscan as cluster_dbscan_integration_tests
from pyclustering.cluster.tests.integration import it_elbow as cluster_elbow_integration_tests
Expand All @@ -58,6 +59,7 @@ def __init__(self):
def fill_suite(integration_cluster_suite):
integration_cluster_suite.addTests(unittest.TestLoader().loadTestsFromModule(cluster_agglomerative_integration_tests))
integration_cluster_suite.addTests(unittest.TestLoader().loadTestsFromModule(cluster_bsas_integration_tests))
integration_cluster_suite.addTests(unittest.TestLoader().loadTestsFromModule(cluster_clique_integration_tests))
integration_cluster_suite.addTests(unittest.TestLoader().loadTestsFromModule(cluster_cure_integration_tests))
integration_cluster_suite.addTests(unittest.TestLoader().loadTestsFromModule(cluster_dbscan_integration_tests))
integration_cluster_suite.addTests(unittest.TestLoader().loadTestsFromModule(cluster_elbow_integration_tests))
Expand Down
Loading

0 comments on commit 16aeec6

Please sign in to comment.