Skip to content

Commit f776e83

Browse files
authored
Merge pull request #6 from lias-laboratory/ci-wheel
Added CI Workflow for publishing automatically
2 parents a6816e5 + 3fb487d commit f776e83

File tree

7 files changed

+177
-61
lines changed

7 files changed

+177
-61
lines changed

.github/workflows/build_wheels.yml

+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
name: Build and upload to PyPI
2+
3+
on:
4+
workflow_dispatch:
5+
pull_request:
6+
branches:
7+
- main
8+
push:
9+
branches:
10+
- main
11+
12+
jobs:
13+
build_wheels:
14+
name: Build wheels on ${{ matrix.os }}
15+
runs-on: ${{ matrix.os }}
16+
strategy:
17+
#fail-fast: false #commented until windows support is added
18+
matrix:
19+
# macos-13 is an intel runner, macos-14 is apple silicon
20+
os: [ubuntu-latest, windows-latest, macos-13, macos-14]
21+
22+
steps:
23+
- uses: actions/checkout@v4
24+
25+
- name: Build wheels
26+
uses: pypa/cibuildwheel@v2.22.0
27+
28+
- uses: actions/upload-artifact@v4
29+
with:
30+
name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }}
31+
path: ./wheelhouse/*.whl
32+
33+
build_sdist:
34+
name: Build source distribution
35+
runs-on: ubuntu-latest
36+
steps:
37+
- uses: actions/checkout@v4
38+
39+
- name: Build sdist
40+
run: pipx run build --sdist
41+
42+
- uses: actions/upload-artifact@v4
43+
with:
44+
name: cibw-sdist
45+
path: dist/*.tar.gz
46+
47+
upload_pypi:
48+
needs: [build_wheels, build_sdist]
49+
runs-on: ubuntu-latest
50+
environment: pypi
51+
permissions:
52+
id-token: write
53+
if: github.event_name == 'release' && github.event.action == 'published'
54+
# or, alternatively, upload to PyPI on every tag starting with 'v' (remove on: release above to use this)
55+
#if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
56+
steps:
57+
- uses: actions/download-artifact@v4
58+
with:
59+
# unpacks all CIBW artifacts into dist/
60+
pattern: cibw-*
61+
path: dist
62+
merge-multiple: true
63+
64+
- name: Generate artifact attestations
65+
uses: actions/attest-build-provenance@v1.4.4
66+
with:
67+
subject-path: "dist/*"
68+
69+
- uses: pypa/gh-action-pypi-publish@release/v1
70+
#with:
71+
# To test: repository-url: https://test.pypi.org/legacy/

pyproject.toml

+8-1
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "radius-clustering"
7-
version = "1.0.0"
7+
version = "1.0.1"
88
description = "A Clustering under radius constraints algorithm using minimum dominating sets"
99
readme = "README.md"
1010
authors = [
1111
{name = "Quentin Haenn"},
12+
{name = "Lias Laboratory"}
1213
]
1314

1415
dependencies = [
@@ -37,6 +38,7 @@ classifiers=[
3738
"Programming Language :: Python :: 3.10",
3839
"Programming Language :: Python :: 3.11",
3940
"Programming Language :: Python :: 3.12",
41+
"Programming Language :: Python :: 3.13",
4042
"Programming Language :: Python :: Implementation :: CPython",
4143
]
4244
keywords = ["Unsupervised learning","clustering", "minimum dominating sets","clustering under radius constraint"]
@@ -137,3 +139,8 @@ docstring-code-format = true
137139
# Set the line length limit used when formatting code snippets in
138140
# docstrings.
139141
docstring-code-line-length = "dynamic"
142+
143+
144+
[tool.cibuildwheel]
145+
# Skip building for PyPy, python 3.6/7/8 and 13t, and 32-bit platforms.
146+
skip = ["pp*", "cp36-*", "cp37-*", "cp38-*", "*-win32", "*linux_i686", "*musllinux*"]

radius_clustering/radius_clustering.py

+16-4
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
import os
1212
import numpy as np
13-
import scipy.spatial as sp_spatial
13+
from sklearn.metrics import pairwise_distances
1414
from sklearn.base import BaseEstimator, ClusterMixin
1515
from sklearn.utils.validation import check_array
1616

@@ -38,7 +38,7 @@ class RadiusClustering(BaseEstimator, ClusterMixin):
3838
-----------
3939
X : array-like, shape (n_samples, n_features)
4040
The input data.
41-
centers : list
41+
centers\_ : list
4242
The indices of the cluster centers.
4343
labels\_ : array-like, shape (n_samples,)
4444
The cluster labels for each point in the input data.
@@ -50,6 +50,9 @@ def __init__(self, manner="approx", threshold=0.5):
5050
self.manner = manner
5151
self.threshold = threshold
5252

53+
def _check_symmetric(self, a, tol=1e-8):
54+
return np.allclose(a, a.T, atol=tol)
55+
5356
def fit(self, X, y=None):
5457
"""
5558
Fit the MDS clustering model to the input data.
@@ -87,10 +90,19 @@ def fit(self, X, y=None):
8790
self.X = check_array(X)
8891

8992
# Create dist and adj matrices
90-
dist_mat = sp_spatial.distance_matrix(self.X, self.X)
93+
if not self._check_symmetric(self.X):
94+
dist_mat = pairwise_distances(self.X, metric="euclidean")
95+
else:
96+
dist_mat = self.X
9197
adj_mask = np.triu((dist_mat <= self.threshold), k=1)
9298
self.nb_edges = np.sum(adj_mask)
93-
self.edges = np.argwhere(adj_mask).astype(np.int32)
99+
if self.nb_edges == 0:
100+
self.centers_ = list(range(self.X.shape[0]))
101+
self.labels_ = self.centers_
102+
self.effective_radius = 0
103+
self._mds_exec_time = 0
104+
return self
105+
self.edges = np.argwhere(adj_mask).astype(np.uint32) #TODO: changer en uint32
94106
self.dist_mat = dist_mat
95107

96108
self._clustering()

radius_clustering/utils/emos.pyx

+2-2
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ cdef extern from "mds3-util.h":
1717
int set_size
1818
double exec_time
1919

20-
Result* emos_main(int* edges, int nb_edge, int n)
20+
Result* emos_main(unsigned int* edges, int nb_edge, int n)
2121

2222
void cleanup()
2323

@@ -26,7 +26,7 @@ cdef extern from "mds3-util.h":
2626
import numpy as np
2727
cimport numpy as np
2828

29-
def py_emos_main(np.ndarray[int, ndim=1] edges, int n, int nb_edge):
29+
def py_emos_main(np.ndarray[unsigned int, ndim=1] edges, int n, int nb_edge):
3030
cdef Result* result = emos_main(&edges[0], n, nb_edge)
3131

3232
dominating_set = [result.dominating_set[i] - 1 for i in range(result.set_size)]

radius_clustering/utils/main-emos.c

+44-30
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,27 @@ Copyright (C) 2024, Haenn Quentin.
1010
#include <stdio.h>
1111
#include <stdlib.h>
1212
#include <string.h>
13-
#include <signal.h>
1413
#include <time.h>
15-
#include <sys/times.h>
16-
#include <sys/types.h>
17-
#include <limits.h>
18-
#include <unistd.h>
19-
#include <sys/resource.h>
20-
#include <math.h>
2114
#include <assert.h>
15+
#include <math.h>
16+
17+
#ifdef _WIN32
18+
#include <windows.h>
19+
#include <process.h>
20+
#include <direct.h>
21+
#define SIGINT 2
22+
typedef void (*SignalHandlerFn)(int);
23+
#elif defined(__APPLE__) || defined(__linux__)
24+
#include <sys/time.h>
25+
#include <sys/resource.h>
26+
#include <sys/types.h>
27+
#include <unistd.h>
28+
#include <signal.h>
29+
#else
30+
#error "Unsupported platform"
31+
#endif
32+
33+
2234
#include "mds3-util.h"
2335
#include "util_heap.h"
2436

@@ -1373,7 +1385,7 @@ void solve_subproblems(){
13731385
static void print_final_solution(char *inst){
13741386
printf("--------------------------------\n");
13751387
printf("Solution: ");
1376-
for(int i=0;i<USED(VEC_SOLUTION);i++){
1388+
for(size_t i=0;i<USED(VEC_SOLUTION);i++){
13771389
printf("%d ",ITEM(VEC_SOLUTION,i));
13781390
}
13791391
printf("\n");
@@ -1476,7 +1488,7 @@ void check_consistance(){
14761488
assert(!domed(CFG[i]));
14771489
}
14781490
int level=-1;
1479-
for(int idx=0;idx<USED(BRA_STK);idx++){
1491+
for(size_t idx=0;idx<USED(BRA_STK);idx++){
14801492
if(ITEM(BRA_STK,idx)==NONE){
14811493
level++;
14821494
}else if(idx<=BRAIDX[level])
@@ -1584,15 +1596,35 @@ void cleanup(){
15841596
}
15851597
}
15861598

1599+
15871600
void handler(int sig) {
15881601
cleanup();
15891602
exit(sig);
15901603
}
15911604

1592-
struct Result* emos_main(int* edges, int n, int nb_edge) {
1605+
#ifdef _WIN32
1606+
static BOOL WINAPI win32_handler(DWORD signal) {
1607+
if (signal == CTRL_C_EVENT) {
1608+
handler(SIGINT);
1609+
return TRUE;
1610+
}
1611+
return FALSE;
1612+
}
1613+
1614+
static void setup_signal_handler(SignalHandlerFn handler_fn) {
1615+
SetConsoleCtrlHandler(win32_handler, TRUE);
1616+
}
1617+
#else
1618+
static void setup_signal_handler(void (*handler_fn)(int)) {
1619+
signal(SIGINT, handler_fn);
1620+
}
1621+
#endif
1622+
1623+
1624+
struct Result* emos_main(unsigned int* edges, int n, int nb_edge) {
15931625

15941626
// Set the signal handler
1595-
signal(SIGINT, handler);
1627+
setup_signal_handler(handler);
15961628

15971629
_read_graph_from_edge_list(edges, n, nb_edge);
15981630
NB_NODE_O = NB_NODE;
@@ -1614,7 +1646,7 @@ struct Result* emos_main(int* edges, int n, int nb_edge) {
16141646

16151647
// Get the results
16161648
int* dominating_set = (int*)malloc(USED(VEC_SOLUTION) * sizeof(int));
1617-
for (int i= 0; i<USED(VEC_SOLUTION); i++) {
1649+
for (size_t i= 0; i<USED(VEC_SOLUTION); i++) {
16181650
dominating_set[i] = ITEM(VEC_SOLUTION, i);
16191651
}
16201652

@@ -1645,22 +1677,4 @@ void free_results(struct Result* result) {
16451677
free(result);
16461678
}
16471679
}
1648-
1649-
/** int main(int argc, char *argv[]) {
1650-
1651-
print_compile_options();
1652-
parse_parmerters(argc,argv);
1653-
if(read_instance(argv[1])) {
1654-
initialize();
1655-
#ifndef NOR
1656-
reduce_graph();
1657-
#endif
1658-
partition_oneproblem();
1659-
solve_subproblems();
1660-
check_final_solution();
1661-
print_final_solution(getInstanceName(argv[1]));
1662-
printf("### %s pruning rate %0.2lf total %llu pruned %llu\n",getInstanceName(argv[1]), (total_branches-pruned_branches)/((double)total_branches),total_branches,total_branches-pruned_branches);
1663-
}
1664-
return 0;
1665-
} */
16661680

radius_clustering/utils/mds3-util.h

+36-10
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,16 @@
1515
#include <stdlib.h>
1616
#include <string.h>
1717
#include <assert.h>
18+
#include <time.h>
19+
20+
#ifdef _WIN32
21+
#include <windows.h>
22+
#elif defined(__APPLE__) || defined(__linux__)
1823
#include <sys/time.h>
1924
#include <sys/resource.h>
25+
#else
26+
#error "Unsupported platform"
27+
#endif
2028

2129
#define WORD_LENGTH 100
2230
#define TRUE 1
@@ -200,10 +208,27 @@ struct Result {
200208
};
201209

202210
static double get_utime() {
203-
struct rusage utime;
204-
getrusage(RUSAGE_SELF, &utime);
205-
return (double) (utime.ru_utime.tv_sec
206-
+ (double) utime.ru_utime.tv_usec / 1000000);
211+
#ifdef _WIN32
212+
FILETIME createTime;
213+
FILETIME exitTime;
214+
FILETIME kernelTime;
215+
FILETIME userTime;
216+
if (GetProcessTimes(GetCurrentProcess(),
217+
&createTime, &exitTime,
218+
&kernelTime, &userTime) != 0) {
219+
ULARGE_INTEGER li = {{userTime.dwLowDateTime, userTime.dwHighDateTime}};
220+
return li.QuadPart * 1e-7;
221+
}
222+
return 0.0;
223+
#elif defined(__APPLE__) || defined(__linux__)
224+
struct rusage utime;
225+
if (getrusage(RUSAGE_SELF, &utime) == 0) {
226+
return (double)utime.ru_utime.tv_sec + (double)utime.ru_utime.tv_usec * 1e-6;
227+
}
228+
return 0.0;
229+
#else
230+
return (double)clock() / CLOCKS_PER_SEC;
231+
#endif
207232
}
208233

209234
static int cmp_branching_vertex_score(const void * a, const void *b){
@@ -230,7 +255,8 @@ static void parse_parmerters(int argc, char *argv[]) {
230255
}
231256

232257
static void allcoate_memory_for_adjacency_list(int nb_node, int nb_edge,int offset) {
233-
int i, block_size = 40960000, free_size = 0;
258+
int i, block_size = 40960000;
259+
unsigned int free_size = 0;
234260
Init_Adj_List = (int *) malloc((2 * nb_edge + nb_node) * sizeof(int));
235261
if (Init_Adj_List == NULL ) {
236262
for (i = 1; i <= NB_NODE; i++) {
@@ -317,7 +343,7 @@ static int _read_graph_from_adjacency_matrix(int** adj_matrix, int num_nodes) {
317343
return TRUE;
318344
}
319345

320-
static int _read_graph_from_edge_list(int* edges, int n, int nb_edges) {
346+
static int _read_graph_from_edge_list(unsigned int* edges, int n, int nb_edges) {
321347
int i, j, l_node, r_node, nb_edge = 0, max_node = n, offset = 0;
322348
int node = 1;
323349

@@ -740,10 +766,10 @@ extern int select_branching_node();
740766
extern void search_domset();
741767
extern int fast_search_initial_solution();
742768
extern void solve_subproblems();
743-
extern struct Result* emos_main(int* edges, int n, int nb_edge);
744-
extern int* get_dominating_set();
745-
extern int get_set_size();
746-
extern double get_exec_time();
769+
extern struct Result* emos_main(unsigned int* edges, int n, int nb_edge);
770+
extern int* get_dominating_set(struct Result* result);
771+
extern int get_set_size(struct Result* result);
772+
extern double get_exec_time(struct Result* result);
747773
extern void free_results(struct Result* result);
748774

749775
// Declare global variables as extern

0 commit comments

Comments
 (0)