fair-kmeans 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.
Files changed (35) hide show
  1. fair_kmeans-0.1.0/LICENSE +21 -0
  2. fair_kmeans-0.1.0/PKG-INFO +139 -0
  3. fair_kmeans-0.1.0/README.md +120 -0
  4. fair_kmeans-0.1.0/build_extension.py +44 -0
  5. fair_kmeans-0.1.0/fair_kmeans/__init__.py +3 -0
  6. fair_kmeans-0.1.0/fair_kmeans/_core.cpp +218 -0
  7. fair_kmeans-0.1.0/fair_kmeans/core.py +312 -0
  8. fair_kmeans-0.1.0/fair_kmeans/fair_clustering_tools.cpp +440 -0
  9. fair_kmeans-0.1.0/fair_kmeans/fair_clustering_tools.h +65 -0
  10. fair_kmeans-0.1.0/fair_kmeans/lemon/LICENSE +32 -0
  11. fair_kmeans-0.1.0/fair_kmeans/lemon/assert.h +214 -0
  12. fair_kmeans-0.1.0/fair_kmeans/lemon/bin_heap.h +347 -0
  13. fair_kmeans-0.1.0/fair_kmeans/lemon/bits/alteration_notifier.h +472 -0
  14. fair_kmeans-0.1.0/fair_kmeans/lemon/bits/array_map.h +351 -0
  15. fair_kmeans-0.1.0/fair_kmeans/lemon/bits/default_map.h +182 -0
  16. fair_kmeans-0.1.0/fair_kmeans/lemon/bits/enable_if.h +131 -0
  17. fair_kmeans-0.1.0/fair_kmeans/lemon/bits/graph_extender.h +1413 -0
  18. fair_kmeans-0.1.0/fair_kmeans/lemon/bits/lock.h +65 -0
  19. fair_kmeans-0.1.0/fair_kmeans/lemon/bits/map_extender.h +332 -0
  20. fair_kmeans-0.1.0/fair_kmeans/lemon/bits/stl_iterators.h +99 -0
  21. fair_kmeans-0.1.0/fair_kmeans/lemon/bits/traits.h +388 -0
  22. fair_kmeans-0.1.0/fair_kmeans/lemon/bits/vector_map.h +244 -0
  23. fair_kmeans-0.1.0/fair_kmeans/lemon/capacity_scaling.h +1015 -0
  24. fair_kmeans-0.1.0/fair_kmeans/lemon/concept_check.h +77 -0
  25. fair_kmeans-0.1.0/fair_kmeans/lemon/concepts/maps.h +225 -0
  26. fair_kmeans-0.1.0/fair_kmeans/lemon/config.h +26 -0
  27. fair_kmeans-0.1.0/fair_kmeans/lemon/core.h +2505 -0
  28. fair_kmeans-0.1.0/fair_kmeans/lemon/error.h +276 -0
  29. fair_kmeans-0.1.0/fair_kmeans/lemon/maps.h +4110 -0
  30. fair_kmeans-0.1.0/fair_kmeans/lemon/static_graph.h +485 -0
  31. fair_kmeans-0.1.0/fair_kmeans/point.cpp +143 -0
  32. fair_kmeans-0.1.0/fair_kmeans/point.h +40 -0
  33. fair_kmeans-0.1.0/fair_kmeans/util.py +44 -0
  34. fair_kmeans-0.1.0/pyproject.toml +42 -0
  35. fair_kmeans-0.1.0/setup.py +31 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Algorithms and Data Structures Group of the Heinrich Heine University Düsseldorf
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,139 @@
1
+ Metadata-Version: 2.1
2
+ Name: fair-kmeans
3
+ Version: 0.1.0
4
+ Summary: Fair K-Means produces a fair clustering assignment according to the fairness definition of Chierichetti et al. Each point has a binary color, and the goal is to assign the points to clusters such that the number of points with different colors in each cluster is the same and the cost of the clusters is minimized.
5
+ License: LICENSE
6
+ Author: Melanie Schmidt
7
+ Author-email: mschmidt@hhu.de
8
+ Requires-Python: >=3.9,<4.0
9
+ Classifier: License :: Other/Proprietary License
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.9
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Requires-Dist: numpy (>=1.26.4,<2.0.0)
16
+ Requires-Dist: scikit-learn (>=1.4.2,<2.0.0)
17
+ Description-Content-Type: text/markdown
18
+
19
+ [![Build Status](https://github.com/algo-hhu/fair-kmeans/actions/workflows/mypy-flake-test.yml/badge.svg)](https://github.com/algo-hhu/fair-kmeans/actions)
20
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
21
+ [![Supported Python version](https://img.shields.io/badge/python-3.9+-blue.svg)](https://www.python.org/downloads/release/python-390/)
22
+ [![Stable Version](https://img.shields.io/pypi/v/fair-kmeans?label=stable)](https://pypi.org/project/fair-kmeans/)
23
+
24
+ # Fair K-Means
25
+
26
+ Fair K-Means produces a fair clustering assignment according to the fairness definition of Chierichetti et al. [1]. Each point has a binary color assigned to it. The goal is to assign the points to clusters such that the number of points with different colors in each cluster is the same. The algorithm also works with weights, so each point can participate with a different weight in the coloring.
27
+
28
+ The algorithm works as follows, assuming that the binary colors are red and blue:
29
+ 1. A matching between the red and blue points is computed such that the cost (the point distances) of the matching is minimized.
30
+ 2. The mean of each matched pair is computed.
31
+ 3. A K-Means++ clustering of all the means is computed, and the point pairs are assigned to the clusters of their means.
32
+
33
+ The matching between the red and blue points is computed using the [Lemon C++ Library](https://lemon.cs.elte.hu/trac/lemon). The library is included in the package and does not need to be installed separately. Only the needed files were included, and a complete version of the library can be found [here](https://lemon.cs.elte.hu/trac/lemon). A copyright notice is included [here](fair_kmeans/lemon/LICENSE).
34
+
35
+ ### References
36
+
37
+ [1] Flavio Chierichetti, Ravi Kumar, Silvio Lattanzi, and Sergei Vassilvitskii, Fair clustering through fairlets, Proceedings of the 30th Annual Conference on Neural Information Processing Systems (NIPS), 2017, pp. 5036–5044.
38
+
39
+ ## Installation
40
+
41
+ ```bash
42
+ pip install fair-kmeans
43
+ ```
44
+
45
+ ## Example
46
+
47
+ ```python
48
+ from fair_kmeans import FairKMeans
49
+
50
+ example_data = [
51
+ [1.0, 1.0, 1.0],
52
+ [1.1, 1.1, 1.1],
53
+ [1.2, 1.2, 1.2],
54
+ [2.0, 2.0, 2.0],
55
+ [2.1, 2.1, 2.1],
56
+ [2.2, 2.2, 2.2],
57
+ ]
58
+
59
+ example_colors = [1, 1, 1, 0, 0, 0]
60
+
61
+ km = FairKMeans(n_clusters=2, random_state=0)
62
+ km.fit(example_data, color=example_colors)
63
+ labels = km.labels_
64
+ centers = km.cluster_centers_
65
+
66
+ print(labels) # [1, 0, 0, 1, 0, 0]
67
+ print(centers) # [[1.65, 1.65, 1.65], [1.5, 1.5, 1.5]]
68
+ ```
69
+
70
+ ## Example with Weights
71
+
72
+ ```python
73
+ from fair_kmeans import FairKMeans
74
+
75
+ example_data = [
76
+ [1.0, 1.0, 1.0],
77
+ [1.1, 1.1, 1.1],
78
+ [1.2, 1.2, 1.2],
79
+ [2.0, 2.0, 2.0],
80
+ [2.1, 2.1, 2.1],
81
+ [2.2, 2.2, 2.2],
82
+ ]
83
+
84
+ example_colors = [1, 1, 1, 0, 0, 0]
85
+ example_weights = [2, 2, 1, 1, 1, 3]
86
+
87
+ km = FairKMeans(n_clusters=2, random_state=0)
88
+ km.fit(example_data, color=example_colors, sample_weight=example_weights)
89
+ labels = km.labels_
90
+ centers = km.cluster_centers_
91
+
92
+ print(labels) # [1, 1, 0, 1, 1, 0]
93
+ print(centers) # [[0.85, 0.85, 0.85], [1.28, 1.28, 1.28]]
94
+ ```
95
+
96
+ ## Development
97
+
98
+ Install [poetry](https://python-poetry.org/docs/#installation)
99
+ ```bash
100
+ curl -sSL https://install.python-poetry.org | python3 -
101
+ ```
102
+
103
+ Install clang
104
+ ```bash
105
+ sudo apt-get install clang
106
+ ```
107
+
108
+ Set clang variables
109
+ ```bash
110
+ export CXX=/usr/bin/clang++
111
+ export CC=/usr/bin/clang
112
+ ```
113
+
114
+ Install the package
115
+ ```bash
116
+ poetry install
117
+ ```
118
+
119
+ If the installation does not work and you do not see the C++ output, you can build the package to see the stack trace
120
+ ```bash
121
+ poetry build
122
+ ```
123
+
124
+ Run the tests
125
+ ```bash
126
+ poetry run python -m unittest discover tests -v
127
+ ```
128
+
129
+ ## Citation
130
+
131
+ If you use this code, please cite [the following paper](https://arxiv.org/abs/2406.02739v1):
132
+
133
+ ```
134
+ Melanie Schmidt, Chris Schwiegelshohn and Christian Sohler.
135
+ "Fair Coresets and Streaming Algorithms for Fair k-Means Clustering" (2020).
136
+ Approximation and Online Algorithms. WAOA 2019.
137
+ Lecture Notes in Computer Science, vol 11926. Springer.
138
+ ```
139
+
@@ -0,0 +1,120 @@
1
+ [![Build Status](https://github.com/algo-hhu/fair-kmeans/actions/workflows/mypy-flake-test.yml/badge.svg)](https://github.com/algo-hhu/fair-kmeans/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/fair-kmeans?label=stable)](https://pypi.org/project/fair-kmeans/)
5
+
6
+ # Fair K-Means
7
+
8
+ Fair K-Means produces a fair clustering assignment according to the fairness definition of Chierichetti et al. [1]. Each point has a binary color assigned to it. The goal is to assign the points to clusters such that the number of points with different colors in each cluster is the same. The algorithm also works with weights, so each point can participate with a different weight in the coloring.
9
+
10
+ The algorithm works as follows, assuming that the binary colors are red and blue:
11
+ 1. A matching between the red and blue points is computed such that the cost (the point distances) of the matching is minimized.
12
+ 2. The mean of each matched pair is computed.
13
+ 3. A K-Means++ clustering of all the means is computed, and the point pairs are assigned to the clusters of their means.
14
+
15
+ The matching between the red and blue points is computed using the [Lemon C++ Library](https://lemon.cs.elte.hu/trac/lemon). The library is included in the package and does not need to be installed separately. Only the needed files were included, and a complete version of the library can be found [here](https://lemon.cs.elte.hu/trac/lemon). A copyright notice is included [here](fair_kmeans/lemon/LICENSE).
16
+
17
+ ### References
18
+
19
+ [1] Flavio Chierichetti, Ravi Kumar, Silvio Lattanzi, and Sergei Vassilvitskii, Fair clustering through fairlets, Proceedings of the 30th Annual Conference on Neural Information Processing Systems (NIPS), 2017, pp. 5036–5044.
20
+
21
+ ## Installation
22
+
23
+ ```bash
24
+ pip install fair-kmeans
25
+ ```
26
+
27
+ ## Example
28
+
29
+ ```python
30
+ from fair_kmeans import FairKMeans
31
+
32
+ example_data = [
33
+ [1.0, 1.0, 1.0],
34
+ [1.1, 1.1, 1.1],
35
+ [1.2, 1.2, 1.2],
36
+ [2.0, 2.0, 2.0],
37
+ [2.1, 2.1, 2.1],
38
+ [2.2, 2.2, 2.2],
39
+ ]
40
+
41
+ example_colors = [1, 1, 1, 0, 0, 0]
42
+
43
+ km = FairKMeans(n_clusters=2, random_state=0)
44
+ km.fit(example_data, color=example_colors)
45
+ labels = km.labels_
46
+ centers = km.cluster_centers_
47
+
48
+ print(labels) # [1, 0, 0, 1, 0, 0]
49
+ print(centers) # [[1.65, 1.65, 1.65], [1.5, 1.5, 1.5]]
50
+ ```
51
+
52
+ ## Example with Weights
53
+
54
+ ```python
55
+ from fair_kmeans import FairKMeans
56
+
57
+ example_data = [
58
+ [1.0, 1.0, 1.0],
59
+ [1.1, 1.1, 1.1],
60
+ [1.2, 1.2, 1.2],
61
+ [2.0, 2.0, 2.0],
62
+ [2.1, 2.1, 2.1],
63
+ [2.2, 2.2, 2.2],
64
+ ]
65
+
66
+ example_colors = [1, 1, 1, 0, 0, 0]
67
+ example_weights = [2, 2, 1, 1, 1, 3]
68
+
69
+ km = FairKMeans(n_clusters=2, random_state=0)
70
+ km.fit(example_data, color=example_colors, sample_weight=example_weights)
71
+ labels = km.labels_
72
+ centers = km.cluster_centers_
73
+
74
+ print(labels) # [1, 1, 0, 1, 1, 0]
75
+ print(centers) # [[0.85, 0.85, 0.85], [1.28, 1.28, 1.28]]
76
+ ```
77
+
78
+ ## Development
79
+
80
+ Install [poetry](https://python-poetry.org/docs/#installation)
81
+ ```bash
82
+ curl -sSL https://install.python-poetry.org | python3 -
83
+ ```
84
+
85
+ Install clang
86
+ ```bash
87
+ sudo apt-get install clang
88
+ ```
89
+
90
+ Set clang variables
91
+ ```bash
92
+ export CXX=/usr/bin/clang++
93
+ export CC=/usr/bin/clang
94
+ ```
95
+
96
+ Install the package
97
+ ```bash
98
+ poetry install
99
+ ```
100
+
101
+ If the installation does not work and you do not see the C++ output, you can build the package to see the stack trace
102
+ ```bash
103
+ poetry build
104
+ ```
105
+
106
+ Run the tests
107
+ ```bash
108
+ poetry run python -m unittest discover tests -v
109
+ ```
110
+
111
+ ## Citation
112
+
113
+ If you use this code, please cite [the following paper](https://arxiv.org/abs/2406.02739v1):
114
+
115
+ ```
116
+ Melanie Schmidt, Chris Schwiegelshohn and Christian Sohler.
117
+ "Fair Coresets and Streaming Algorithms for Fair k-Means Clustering" (2020).
118
+ Approximation and Online Algorithms. WAOA 2019.
119
+ Lecture Notes in Computer Science, vol 11926. Springer.
120
+ ```
@@ -0,0 +1,44 @@
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="fair_kmeans._core",
9
+ sources=[
10
+ "fair_kmeans/_core.cpp",
11
+ "fair_kmeans/fair_clustering_tools.cpp",
12
+ "fair_kmeans/point.cpp",
13
+ ],
14
+ include_dirs=["fair_kmeans"],
15
+ )
16
+
17
+ # Thank you https://github.com/dstein64/kmeans1d!
18
+
19
+
20
+ class BuildExt(build_ext):
21
+ """A custom build extension for adding -stdlib arguments for clang++."""
22
+
23
+ def build_extensions(self) -> None:
24
+ # '-std=c++11' is added to `extra_compile_args` so the code can compile
25
+ # with clang++. This works across compilers (ignored by MSVC).
26
+ for extension in self.extensions:
27
+ extension.extra_compile_args.append("-std=c++11")
28
+
29
+ try:
30
+ build_ext.build_extensions(self)
31
+ except CompileError:
32
+ # Workaround Issue #2.
33
+ # '-stdlib=libc++' is added to `extra_compile_args` and `extra_link_args`
34
+ # so the code can compile on macOS with Anaconda.
35
+ for extension in self.extensions:
36
+ extension.extra_compile_args.append("-stdlib=libc++")
37
+ extension.extra_link_args.append("-stdlib=libc++")
38
+ build_ext.build_extensions(self)
39
+
40
+
41
+ def build(setup_kwargs: Dict[str, Any]) -> None:
42
+ setup_kwargs.update(
43
+ {"ext_modules": [extension], "cmdclass": {"build_ext": BuildExt}}
44
+ )
@@ -0,0 +1,3 @@
1
+ from fair_kmeans.core import FairKMeans
2
+
3
+ __all__ = ["FairKMeans"]
@@ -0,0 +1,218 @@
1
+ #include <Python.h>
2
+ #include <iostream>
3
+ #include <cstdint>
4
+
5
+ #include "fair_clustering_tools.h"
6
+
7
+ typedef unsigned int uint;
8
+
9
+
10
+
11
+ bool checkWeightValidity(std::vector<int> &weightsum)
12
+ {
13
+ int first_sum = weightsum[0];
14
+
15
+ for (size_t i = 1; i < weightsum.size(); ++i)
16
+ {
17
+ if (weightsum[i] != first_sum)
18
+ {
19
+ std::cout << "Total weight sum of color 0 is " << first_sum << ", but weight sum of color " << i << " is " << weightsum[1] << ". All weights must be the same." << std::endl;
20
+ return false;
21
+ }
22
+ }
23
+ return true;
24
+ }
25
+
26
+ // Splits points into weight, color and coordinates.
27
+ // Processes the data into a n-color-dim array points,
28
+ // where each index is one color.
29
+ // Number and weightsum give the number
30
+ // and total weight of points in either array.
31
+ void postprocessData(double *array, double *sampleWeights, int *colors, uint n, uint d, std::vector<std::vector<Point>> &points, std::vector<int> &number, std::vector<int> &weightsum)
32
+ {
33
+ int j = 0;
34
+ for (uint i = 0; i < n * d; i += d)
35
+ {
36
+ double w = sampleWeights[j];
37
+ int c = colors[j];
38
+ number[c] += 1;
39
+ weightsum[c] += w;
40
+ std::vector<double> coords(&array[i], &array[i] + d);
41
+ // Add j as the position of the point in the array
42
+ points[c].push_back(Point(w, c, j, coords));
43
+ j++;
44
+ }
45
+ }
46
+
47
+ // Convert the array of centers to a vector of points
48
+ // Postprocess the center data into a vector of Point objects
49
+ // centers have weight 1 and no color (-1).
50
+ void postprocessCenters(double *centers, std::vector<Point> &centersVector, uint k, uint d)
51
+ {
52
+ // Create center vector with points objects by copying from centerdata
53
+ int j = 0;
54
+ for (uint i = 0; i < k * d; i += d)
55
+ {
56
+ std::vector<double> coords(&centers[i], &centers[i] + d);
57
+ // Centers have weight 1 and no color (-1)
58
+ centersVector[j] = Point(1, -1, coords);
59
+ j++;
60
+ }
61
+ }
62
+
63
+ // Copy the centers back to the centers array to return to Python
64
+ void copyBackCenters(double *centers, std::vector<Point> &centersVector)
65
+ {
66
+ for (size_t i = 0; i < centersVector.size(); ++i)
67
+ {
68
+ size_t d = centersVector[i].getDimension();
69
+ for (size_t j = 0; j < d; ++j)
70
+ {
71
+ centers[i * d + j] = centersVector[i].getCoordinatePointer()[j];
72
+ }
73
+ }
74
+ }
75
+
76
+ void assignLabels(std::vector<std::vector<Point>> &points, int *labels)
77
+ {
78
+ for (size_t c = 0; c < points.size(); ++c)
79
+ {
80
+ for (size_t j = 0; j < points[c].size(); ++j)
81
+ {
82
+ labels[points[c][j].getPosition()] = points[c][j].getLabel();
83
+ }
84
+ }
85
+ }
86
+
87
+
88
+ // Thank you https://github.com/dstein64/kmeans1d!
89
+
90
+ extern "C"
91
+ {
92
+ // "__declspec(dllexport)" causes the function to be exported when compiling on Windows.
93
+ // Otherwise, the function is not exported and the code raises
94
+ // "AttributeError: function 'cluster' not found".
95
+ // Exporting is a Windows platform requirement, not just a Visual Studio requirement
96
+ // (https://stackoverflow.com/a/22288874/1509433). The _WIN32 macro covers the Visual
97
+ // Studio compiler (MSVC) and MinGW. The __CYGWIN__ macro covers gcc and clang under
98
+ // Cygwin.
99
+ #if defined(_WIN32) || defined(__CYGWIN__)
100
+ __declspec(dllexport)
101
+ #endif
102
+ double
103
+ fairKMeans(double *array,
104
+ double *sampleWeights,
105
+ int *colors,
106
+ uint n,
107
+ uint d,
108
+ uint k,
109
+ uint nc,
110
+ uint maxIterations,
111
+ double tolerance,
112
+ std::uint64_t seed,
113
+ int *labels,
114
+ double *centers,
115
+ bool updateCenters,
116
+ uint *iterations)
117
+ {
118
+
119
+ std::vector<int> weightsum(nc, 0);
120
+ std::vector<int> number(nc, 0);
121
+ // One vector of points per color
122
+ std::vector<std::vector<Point>> points(nc);
123
+
124
+ postprocessData(array, sampleWeights, colors, n, d, points, number, weightsum);
125
+
126
+ // std::cout << "Points:" << std::endl;
127
+ // for (size_t c = 0; c < nc; ++c)
128
+ // {
129
+ // for (size_t i = 0; i < points[c].size(); ++i)
130
+ // {
131
+ // std::cout << points[c][i].print() << std::endl;
132
+ // }
133
+ // }
134
+
135
+ std::vector<Point> centersVector(k);
136
+ postprocessCenters(centers, centersVector, k, d);
137
+
138
+ if (!checkWeightValidity(weightsum))
139
+ {
140
+ return -1;
141
+ }
142
+
143
+ // std::cout << "Initial Centers:" << std::endl;
144
+ // for (size_t i = 0; i < centersVector.size(); ++i)
145
+ // {
146
+ // std::cout << centersVector[i].print() << std::endl;
147
+ // }
148
+
149
+ double cost = std::numeric_limits<double>::max();
150
+ double previousCost = cost;
151
+ bool change = true;
152
+
153
+ while (*iterations < maxIterations && change)
154
+ {
155
+
156
+ // Computing the fair assignment and better centers
157
+ // If we don't want to update the centers, then we shoudn't fill all clusters
158
+ CentersValueChange result = compute_fair_assignment(points, centersVector, number, weightsum, updateCenters, seed);
159
+
160
+ // If the returned cost is negative, the CapacityScaling algorithm failed
161
+ if (std::get<1>(result) < 0.0)
162
+ {
163
+ return std::get<1>(result);
164
+ }
165
+
166
+ std::vector<Point> &newCenters = std::get<0>(result);
167
+
168
+ // std::cout << std::endl;
169
+ // std::cout << "New Centers:" << std::endl;
170
+ // for (size_t i = 0; i < newCenters.size(); ++i)
171
+ // {
172
+ // std::cout << "Center " << i << ": " << newCenters[i].print() << std::endl;
173
+ // }
174
+
175
+ centersVector = newCenters;
176
+ cost = std::get<1>(result);
177
+
178
+
179
+ // std::cout << "Cost: " << cost << std::endl;
180
+ // std::cout << "Previous Cost: " << previousCost << std::endl;
181
+
182
+ ++*iterations;
183
+ change = false;
184
+
185
+ // If we don't see any improvement anymore, we stop
186
+ if (cost < (1 - tolerance) * previousCost)
187
+ {
188
+ previousCost = cost;
189
+ change = true;
190
+ }
191
+ }
192
+
193
+ if (cost >= 0)
194
+ {
195
+ if (updateCenters) copyBackCenters(centers, centersVector);
196
+
197
+ assignLabels(points, labels);
198
+ }
199
+
200
+ return cost;
201
+ }
202
+ } // extern "C"
203
+
204
+ static PyMethodDef module_methods[] = {
205
+ {NULL, NULL, 0, NULL}};
206
+
207
+ static struct PyModuleDef _coremodule = {
208
+ PyModuleDef_HEAD_INIT,
209
+ "fair_kmeans._core",
210
+ NULL,
211
+ -1,
212
+ module_methods,
213
+ };
214
+
215
+ PyMODINIT_FUNC PyInit__core(void)
216
+ {
217
+ return PyModule_Create(&_coremodule);
218
+ }