flspp 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
flspp-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,115 @@
1
+ Metadata-Version: 2.1
2
+ Name: flspp
3
+ Version: 0.1.0
4
+ Summary:
5
+ Author: Lukas Drexler
6
+ Author-email: lukas.drexler@hhu.de
7
+ Requires-Python: >=3.9,<4.0
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3.9
10
+ Classifier: Programming Language :: Python :: 3.10
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Requires-Dist: numpy (>=1.26.4,<2.0.0)
14
+ Requires-Dist: scikit-learn (>=1.4.2,<2.0.0)
15
+ Description-Content-Type: text/markdown
16
+
17
+ [![Build Status](https://github.com/algo-hhu/FLSpp/actions/workflows/mypy-flake-test.yml/badge.svg)](https://github.com/algo-hhu/FLSpp/actions)
18
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
19
+ [![Supported Python version](https://img.shields.io/badge/python-3.9+-blue.svg)](https://www.python.org/downloads/release/python-390/)
20
+ [![Stable Version](https://img.shields.io/pypi/v/flspp?label=stable)](https://pypi.org/project/flspp/)
21
+
22
+ # FLS++
23
+
24
+ An implementation of the FLS++ algorithm for k-means Clustering, as presented in [1]. This is an improvement of the LS++ algorithm presented by Lattanzi and Sohler in [2].
25
+
26
+ One can think of the algorithm as working in 3 phases:
27
+
28
+ 1. Initializing centers with k-means++ (as presented in [3]).
29
+ 2. Improving the center set by performing local search swaps (for details, see [1]).
30
+ 3. Converging the solution with LLoyd's algorithm (also known as "the" k-means algorithm).
31
+
32
+ In [2] it is shown that `O(k log log k)` local search iterations yield a constant factor approximation. However, in both [1] and [2] it is shown that, in practice, a very small number of iterations (e.g. 20) already yields very good results, at very reasonable runtime.
33
+
34
+ The interface is built in the same way as scikit-learn's KMeans for better compatibility.
35
+
36
+ ## References
37
+
38
+ [1] Theo Conrads, Lukas Drexler, Joshua Könen, Daniel R. Schmidt and Melanie Schmidt. "Local Search k-means++ with Foresight" (2024)
39
+
40
+ [2] Silvio Lattanzi and Christian Sohler. A better k-means++ algorithm via local search. In Proc.444
41
+ of the 36th ICML, volume 97 of Proceedings of Machine Learning Research, pages 3662–3671.445
42
+ PMLR, 09–15 Jun 2019
43
+
44
+ [3] David Arthur and Sergei Vassilvitskii. K-means++: The advantages of careful seeding. In409
45
+ Proceedings of the 18th SODA, page 1027–1035, USA, 2007
46
+
47
+ ## Installation
48
+
49
+ ```bash
50
+ pip install flspp
51
+ ```
52
+
53
+ ## Example
54
+
55
+ ```python
56
+ from flspp import FLSpp
57
+
58
+ example_data = [
59
+ [1.0, 1.0, 1.0],
60
+ [1.1, 1.1, 1.1],
61
+ [1.2, 1.2, 1.2],
62
+ [2.0, 2.0, 2.0],
63
+ [2.1, 2.1, 2.1],
64
+ [2.2, 2.2, 2.2],
65
+ ]
66
+
67
+ flspp = FLSpp(n_clusters=2)
68
+ labels = flspp.fit_predict(example_data)
69
+ centers = flspp.cluster_centers_
70
+
71
+ print(labels) # [1, 1, 1, 0, 0, 0]
72
+ print(centers) # [[2.1, 2.1, 2.1], [1.1, 1.1, 1.1]]
73
+ ```
74
+
75
+ ## Development
76
+
77
+ Install [poetry](https://python-poetry.org/docs/#installation)
78
+ ```bash
79
+ curl -sSL https://install.python-poetry.org | python3 -
80
+ ```
81
+
82
+ Install clang
83
+ ```bash
84
+ sudo apt-get install clang
85
+ ```
86
+
87
+ Set clang variables
88
+ ```bash
89
+ export CXX=/usr/bin/clang++
90
+ export CC=/usr/bin/clang
91
+ ```
92
+
93
+ Install the package
94
+ ```bash
95
+ poetry install
96
+ ```
97
+
98
+ If the installation does not work and you do not see the C++ output, you can build the package to see the stack trace
99
+ ```bash
100
+ poetry build
101
+ ```
102
+
103
+ Run the tests
104
+ ```bash
105
+ poetry run python -m unittest discover tests -v
106
+ ```
107
+
108
+ ## Citation
109
+
110
+ If you use this code, please cite the following paper:
111
+
112
+ ```
113
+ Theo Conrads, Lukas Drexler, Joshua Könen, Daniel R. Schmidt and Melanie Schmidt. "Local Search k-means++ with Foresight" (2024). Accepted at SEA 2024.
114
+ ```
115
+
flspp-0.1.0/README.md ADDED
@@ -0,0 +1,98 @@
1
+ [![Build Status](https://github.com/algo-hhu/FLSpp/actions/workflows/mypy-flake-test.yml/badge.svg)](https://github.com/algo-hhu/FLSpp/actions)
2
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
3
+ [![Supported Python version](https://img.shields.io/badge/python-3.9+-blue.svg)](https://www.python.org/downloads/release/python-390/)
4
+ [![Stable Version](https://img.shields.io/pypi/v/flspp?label=stable)](https://pypi.org/project/flspp/)
5
+
6
+ # FLS++
7
+
8
+ An implementation of the FLS++ algorithm for k-means Clustering, as presented in [1]. This is an improvement of the LS++ algorithm presented by Lattanzi and Sohler in [2].
9
+
10
+ One can think of the algorithm as working in 3 phases:
11
+
12
+ 1. Initializing centers with k-means++ (as presented in [3]).
13
+ 2. Improving the center set by performing local search swaps (for details, see [1]).
14
+ 3. Converging the solution with LLoyd's algorithm (also known as "the" k-means algorithm).
15
+
16
+ In [2] it is shown that `O(k log log k)` local search iterations yield a constant factor approximation. However, in both [1] and [2] it is shown that, in practice, a very small number of iterations (e.g. 20) already yields very good results, at very reasonable runtime.
17
+
18
+ The interface is built in the same way as scikit-learn's KMeans for better compatibility.
19
+
20
+ ## References
21
+
22
+ [1] Theo Conrads, Lukas Drexler, Joshua Könen, Daniel R. Schmidt and Melanie Schmidt. "Local Search k-means++ with Foresight" (2024)
23
+
24
+ [2] Silvio Lattanzi and Christian Sohler. A better k-means++ algorithm via local search. In Proc.444
25
+ of the 36th ICML, volume 97 of Proceedings of Machine Learning Research, pages 3662–3671.445
26
+ PMLR, 09–15 Jun 2019
27
+
28
+ [3] David Arthur and Sergei Vassilvitskii. K-means++: The advantages of careful seeding. In409
29
+ Proceedings of the 18th SODA, page 1027–1035, USA, 2007
30
+
31
+ ## Installation
32
+
33
+ ```bash
34
+ pip install flspp
35
+ ```
36
+
37
+ ## Example
38
+
39
+ ```python
40
+ from flspp import FLSpp
41
+
42
+ example_data = [
43
+ [1.0, 1.0, 1.0],
44
+ [1.1, 1.1, 1.1],
45
+ [1.2, 1.2, 1.2],
46
+ [2.0, 2.0, 2.0],
47
+ [2.1, 2.1, 2.1],
48
+ [2.2, 2.2, 2.2],
49
+ ]
50
+
51
+ flspp = FLSpp(n_clusters=2)
52
+ labels = flspp.fit_predict(example_data)
53
+ centers = flspp.cluster_centers_
54
+
55
+ print(labels) # [1, 1, 1, 0, 0, 0]
56
+ print(centers) # [[2.1, 2.1, 2.1], [1.1, 1.1, 1.1]]
57
+ ```
58
+
59
+ ## Development
60
+
61
+ Install [poetry](https://python-poetry.org/docs/#installation)
62
+ ```bash
63
+ curl -sSL https://install.python-poetry.org | python3 -
64
+ ```
65
+
66
+ Install clang
67
+ ```bash
68
+ sudo apt-get install clang
69
+ ```
70
+
71
+ Set clang variables
72
+ ```bash
73
+ export CXX=/usr/bin/clang++
74
+ export CC=/usr/bin/clang
75
+ ```
76
+
77
+ Install the package
78
+ ```bash
79
+ poetry install
80
+ ```
81
+
82
+ If the installation does not work and you do not see the C++ output, you can build the package to see the stack trace
83
+ ```bash
84
+ poetry build
85
+ ```
86
+
87
+ Run the tests
88
+ ```bash
89
+ poetry run python -m unittest discover tests -v
90
+ ```
91
+
92
+ ## Citation
93
+
94
+ If you use this code, please cite the following paper:
95
+
96
+ ```
97
+ Theo Conrads, Lukas Drexler, Joshua Könen, Daniel R. Schmidt and Melanie Schmidt. "Local Search k-means++ with Foresight" (2024). Accepted at SEA 2024.
98
+ ```
flspp-0.1.0/build.py ADDED
@@ -0,0 +1,46 @@
1
+ from typing import Any, Dict
2
+
3
+ from setuptools import Extension
4
+ from setuptools.command.build_ext import build_ext
5
+ from setuptools.errors import CompileError
6
+
7
+ extension = Extension(
8
+ name="flspp._core",
9
+ sources=[
10
+ "flspp/_core.cpp",
11
+ "flspp/clustering_algorithm.cpp",
12
+ "flspp/clustering.cpp",
13
+ "flspp/makros.cpp",
14
+ "flspp/random_generator.cpp",
15
+ ],
16
+ include_dirs=["flspp"],
17
+ )
18
+
19
+ # Thank you https://github.com/dstein64/kmeans1d!
20
+
21
+
22
+ class BuildExt(build_ext):
23
+ """A custom build extension for adding -stdlib arguments for clang++."""
24
+
25
+ def build_extensions(self) -> None:
26
+ # '-std=c++11' is added to `extra_compile_args` so the code can compile
27
+ # with clang++. This works across compilers (ignored by MSVC).
28
+ for extension in self.extensions:
29
+ extension.extra_compile_args.append("-std=c++11")
30
+
31
+ try:
32
+ build_ext.build_extensions(self)
33
+ except CompileError:
34
+ # Workaround Issue #2.
35
+ # '-stdlib=libc++' is added to `extra_compile_args` and `extra_link_args`
36
+ # so the code can compile on macOS with Anaconda.
37
+ for extension in self.extensions:
38
+ extension.extra_compile_args.append("-stdlib=libc++")
39
+ extension.extra_link_args.append("-stdlib=libc++")
40
+ build_ext.build_extensions(self)
41
+
42
+
43
+ def build(setup_kwargs: Dict[str, Any]) -> None:
44
+ setup_kwargs.update(
45
+ {"ext_modules": [extension], "cmdclass": {"build_ext": BuildExt}}
46
+ )
@@ -0,0 +1,3 @@
1
+ from flspp.core import FLSpp
2
+
3
+ __all__ = ["FLSpp"]
@@ -0,0 +1,109 @@
1
+ #include <Python.h>
2
+
3
+ #include "random_generator.h"
4
+ #include "clustering_algorithm.h"
5
+ #include "additional_vector_stuff.h"
6
+ #include "makros.h"
7
+
8
+ std::vector<Point> array_to_vector(double **array, int rows, int columns)
9
+ {
10
+
11
+ std::vector<Point> points;
12
+
13
+ for (int i = 0; i < rows; ++i)
14
+ {
15
+ std::vector<double> row;
16
+ for (int j = 0; j < columns; ++j)
17
+ {
18
+ row.push_back(array[i][j]);
19
+ }
20
+ Point p = Point(columns, i, row);
21
+ points.push_back(p);
22
+ }
23
+
24
+ return points;
25
+ }
26
+
27
+ // Thank you https://github.com/dstein64/kmeans1d!
28
+
29
+ extern "C"
30
+ {
31
+ // "__declspec(dllexport)" causes the function to be exported when compiling on Windows.
32
+ // Otherwise, the function is not exported and the code raises
33
+ // "AttributeError: function 'cluster' not found".
34
+ // Exporting is a Windows platform requirement, not just a Visual Studio requirement
35
+ // (https://stackoverflow.com/a/22288874/1509433). The _WIN32 macro covers the Visual
36
+ // Studio compiler (MSVC) and MinGW. The __CYGWIN__ macro covers gcc and clang under
37
+ // Cygwin.
38
+ #if defined(_WIN32) || defined(__CYGWIN__)
39
+ __declspec(dllexport)
40
+ #endif
41
+ double
42
+ cluster(double **array,
43
+ uint n,
44
+ uint d,
45
+ uint k,
46
+ uint lloyd_iterations,
47
+ uint local_search_iterations,
48
+ int *labels,
49
+ double *centers,
50
+ int *iterations)
51
+ {
52
+
53
+ /*
54
+ * double** array: input points (as an array of coordinate arrays)
55
+ * int n: number of input points
56
+ * int d: dimension/number of features
57
+ * int k: number of centers
58
+ * int lloyd_iterations: how many iterations of lloyd's algorithm are performed (choose -1 for precision-based termination)
59
+ * int local_search_iterations: how many local search steps /center swaps are performed
60
+ * int* cluster: array of length n, which will eventually contain the labels for each point (i.e. cluster membership)
61
+ * double** centers: (k x d)-array, which will eventually contain coordinates of final centers
62
+ */
63
+
64
+ // transform input array to vector of "Points" (to be able to use existing constructor for FLSPP object)
65
+ std::vector<Point> points = array_to_vector(array, n, d);
66
+
67
+ // create FLSPP object from vector of points
68
+ FLSPP my_flspp(points);
69
+
70
+ // output_algorithm object contains information such as final labels and final centers
71
+ output_algorithm flspp_output = my_flspp.algorithm(k);
72
+
73
+ memcpy(labels, flspp_output.final_labels.data(), n * sizeof(int));
74
+
75
+ // flspp_output has vector<Point> attribute final_centers, containing the final centers as instances of class Point
76
+ for (int j = 0; j < k; ++j)
77
+ {
78
+ // get coordinates of j-th center
79
+ std::vector<double> center_coordinates = flspp_output.final_centers[j].coordinates;
80
+ // We save it in a continuous array
81
+ for (int l = 0; l < d; ++l)
82
+ {
83
+ centers[j * d + l] = center_coordinates[l];
84
+ // std::cout << centers[j][l] << " ";
85
+ }
86
+ // std::cout << std::endl;
87
+ }
88
+
89
+ *iterations = flspp_output.iterations;
90
+
91
+ return flspp_output.cost;
92
+ }
93
+ } // extern "C"
94
+
95
+ static PyMethodDef module_methods[] = {
96
+ {NULL, NULL, 0, NULL}};
97
+
98
+ static struct PyModuleDef _coremodule = {
99
+ PyModuleDef_HEAD_INIT,
100
+ "flspp._core",
101
+ NULL,
102
+ -1,
103
+ module_methods,
104
+ };
105
+
106
+ PyMODINIT_FUNC PyInit__core(void)
107
+ {
108
+ return PyModule_Create(&_coremodule);
109
+ }
@@ -0,0 +1,39 @@
1
+ #pragma once
2
+ #include <vector>
3
+ #include <iterator>
4
+ #include "clustering.h"
5
+ #include "makros.h"
6
+
7
+ // overload comparison of vectors
8
+ // template<typename T>
9
+ // bool operator==(const std::vector<T>& lhs, const std::vector<T>& rhs);
10
+
11
+ // templates can not be compiled => They can not be included in a cpp file
12
+ // https://stackoverflow.com/questions/1639797/template-issue-causes-linker-error-c
13
+
14
+ template <typename T>
15
+ inline bool operator==(const std::vector<T> &lhs, const std::vector<T> &rhs)
16
+ {
17
+ if (lhs.size() != rhs.size())
18
+ return false;
19
+ for (int i = 0; i < lhs.size(); i++)
20
+ {
21
+ if (lhs[i] != rhs[i])
22
+ return false;
23
+ }
24
+ return true;
25
+ }
26
+
27
+ template <typename T>
28
+ std::ostream &operator<<(std::ostream &os, const std::vector<T> &vec)
29
+ {
30
+ for (auto elem : vec)
31
+ {
32
+ os << elem << " ";
33
+ }
34
+ return os;
35
+ }
36
+
37
+ // printing vectors
38
+ // template <typename T>
39
+ // std::ostream& operator << (std::ostream& os, const std::vector<T>& vec);
@@ -0,0 +1,70 @@
1
+ //
2
+ // Created by lukas on 01.12.22.
3
+ // Implementation of classes defined in clustering.h
4
+
5
+ #include "clustering.h"
6
+
7
+ Point::Point(int dim, int ind, std::vector<double> coord) : dimension(dim), index(ind), coordinates(coord)
8
+ {
9
+ }
10
+
11
+ Point::Point(Point const &p) : dimension(p.dimension), index(p.index), coordinates(p.coordinates)
12
+ {
13
+ }
14
+
15
+ Point::Point()
16
+ {
17
+ }
18
+
19
+ bool operator==(const Point &lhs, const Point &rhs)
20
+ {
21
+ // we only compare the coordinates, since some of the other values may not be initialized
22
+ return lhs.coordinates == rhs.coordinates;
23
+ }
24
+
25
+ bool operator!=(const Point &lhs, const Point &rhs)
26
+ {
27
+ return !(lhs == rhs);
28
+ }
29
+
30
+ std::ostream &operator<<(std::ostream &os, const Point &p)
31
+ {
32
+ os << "[ " << p.coordinates << "]";
33
+
34
+ return os;
35
+ }
36
+
37
+ void Point::operator+=(const Point &other)
38
+ {
39
+ if (coordinates.size() != other.coordinates.size())
40
+ {
41
+ throw std::invalid_argument("Points must have the same number of dimensions.");
42
+ }
43
+
44
+ for (size_t i = 0; i < coordinates.size(); ++i)
45
+ {
46
+ coordinates[i] += other.coordinates[i];
47
+ }
48
+ }
49
+
50
+ double euclidean_distance_squared(Point &x, Point &y)
51
+ {
52
+ double distance = 0;
53
+ double a, b;
54
+ for (int i = 0; i < x.dimension; ++i)
55
+ {
56
+ // distance += (x.coordinates[i] - y.coordinates[i]) * (x.coordinates[i] - y.coordinates[i]);
57
+ // distance += std::pow(x.coordinates[i] - y.coordinates[i],2);
58
+ a = x.coordinates[i];
59
+ b = y.coordinates[i];
60
+ distance += (a - b) * (a - b);
61
+ }
62
+ return distance;
63
+ }
64
+
65
+ Cluster::Cluster(int ind, Point centr) : index(ind), centroid(centr) {}
66
+
67
+ void Cluster::add_member(Point p)
68
+ {
69
+ members.push_back(p);
70
+ }
@@ -0,0 +1,56 @@
1
+ //
2
+ // Created by lukas on 21.11.22.
3
+ //
4
+
5
+ // #ifndef FLS___CLUSTERING_H
6
+ // #define FLS___CLUSTERING_H
7
+
8
+ #pragma once
9
+ #include <vector>
10
+ #include <iterator>
11
+ #include <iostream>
12
+ #include <cmath>
13
+
14
+ #include "additional_vector_stuff.h"
15
+ #include "makros.h"
16
+
17
+ // MAYBE TWO DISTINCT CLASSES: ONE FOR "NORMAL" POINTS, ONE FOR CENTERS?
18
+ class Point
19
+ {
20
+ public:
21
+ int dimension;
22
+ int index;
23
+
24
+ std::vector<double> coordinates;
25
+
26
+ Point(int dim, int ind, std::vector<double> coord);
27
+ Point(Point const &p);
28
+ Point();
29
+
30
+ void operator+=(const Point &other);
31
+ };
32
+
33
+ bool operator==(const Point &lhs, const Point &rhs);
34
+ bool operator!=(const Point &lhs, const Point &rhs);
35
+
36
+ // Overload the += operator to add another Point to this Point
37
+
38
+ std::ostream &operator<<(std::ostream &os, const Point &p);
39
+
40
+ double euclidean_distance_squared(Point &x, Point &y);
41
+
42
+ class Cluster
43
+ {
44
+ public:
45
+ int index;
46
+ Point centroid;
47
+
48
+ Cluster(int ind, Point centr);
49
+
50
+ void add_member(Point p);
51
+
52
+ private:
53
+ std::vector<Point> members;
54
+ };
55
+
56
+ // #endif //FLS___CLUSTERING_H