Skip to content

Added CI Workflow for publishing automatically #6

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Dec 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions .github/workflows/build_wheels.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
name: Build and upload to PyPI

on:
workflow_dispatch:
pull_request:
branches:
- main
push:
branches:
- main

jobs:
build_wheels:
name: Build wheels on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
#fail-fast: false #commented until windows support is added
matrix:
# macos-13 is an intel runner, macos-14 is apple silicon
os: [ubuntu-latest, windows-latest, macos-13, macos-14]

steps:
- uses: actions/checkout@v4

- name: Build wheels
uses: pypa/[email protected]

- uses: actions/upload-artifact@v4
with:
name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }}
path: ./wheelhouse/*.whl

build_sdist:
name: Build source distribution
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Build sdist
run: pipx run build --sdist

- uses: actions/upload-artifact@v4
with:
name: cibw-sdist
path: dist/*.tar.gz

upload_pypi:
needs: [build_wheels, build_sdist]
runs-on: ubuntu-latest
environment: pypi
permissions:
id-token: write
if: github.event_name == 'release' && github.event.action == 'published'
# or, alternatively, upload to PyPI on every tag starting with 'v' (remove on: release above to use this)
#if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
steps:
- uses: actions/download-artifact@v4
with:
# unpacks all CIBW artifacts into dist/
pattern: cibw-*
path: dist
merge-multiple: true

- name: Generate artifact attestations
uses: actions/[email protected]
with:
subject-path: "dist/*"

- uses: pypa/gh-action-pypi-publish@release/v1
#with:
# To test: repository-url: https://test.pypi.org/legacy/
9 changes: 8 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ build-backend = "setuptools.build_meta"

[project]
name = "radius-clustering"
version = "1.0.0"
version = "1.0.1"
description = "A Clustering under radius constraints algorithm using minimum dominating sets"
readme = "README.md"
authors = [
{name = "Quentin Haenn"},
{name = "Lias Laboratory"}
]

dependencies = [
Expand Down Expand Up @@ -37,6 +38,7 @@ classifiers=[
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: Implementation :: CPython",
]
keywords = ["Unsupervised learning","clustering", "minimum dominating sets","clustering under radius constraint"]
Expand Down Expand Up @@ -137,3 +139,8 @@ docstring-code-format = true
# Set the line length limit used when formatting code snippets in
# docstrings.
docstring-code-line-length = "dynamic"


[tool.cibuildwheel]
# Skip building for PyPy, python 3.6/7/8 and 13t, and 32-bit platforms.
skip = ["pp*", "cp36-*", "cp37-*", "cp38-*", "*-win32", "*linux_i686", "*musllinux*"]
20 changes: 16 additions & 4 deletions radius_clustering/radius_clustering.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

import os
import numpy as np
import scipy.spatial as sp_spatial
from sklearn.metrics import pairwise_distances
from sklearn.base import BaseEstimator, ClusterMixin
from sklearn.utils.validation import check_array

Expand Down Expand Up @@ -38,7 +38,7 @@ class RadiusClustering(BaseEstimator, ClusterMixin):
-----------
X : array-like, shape (n_samples, n_features)
The input data.
centers : list
centers\_ : list
The indices of the cluster centers.
labels\_ : array-like, shape (n_samples,)
The cluster labels for each point in the input data.
Expand All @@ -50,6 +50,9 @@ def __init__(self, manner="approx", threshold=0.5):
self.manner = manner
self.threshold = threshold

def _check_symmetric(self, a, tol=1e-8):
return np.allclose(a, a.T, atol=tol)

def fit(self, X, y=None):
"""
Fit the MDS clustering model to the input data.
Expand Down Expand Up @@ -87,10 +90,19 @@ def fit(self, X, y=None):
self.X = check_array(X)

# Create dist and adj matrices
dist_mat = sp_spatial.distance_matrix(self.X, self.X)
if not self._check_symmetric(self.X):
dist_mat = pairwise_distances(self.X, metric="euclidean")
else:
dist_mat = self.X
adj_mask = np.triu((dist_mat <= self.threshold), k=1)
self.nb_edges = np.sum(adj_mask)
self.edges = np.argwhere(adj_mask).astype(np.int32)
if self.nb_edges == 0:
self.centers_ = list(range(self.X.shape[0]))
self.labels_ = self.centers_
self.effective_radius = 0
self._mds_exec_time = 0
return self
self.edges = np.argwhere(adj_mask).astype(np.uint32) #TODO: changer en uint32
self.dist_mat = dist_mat

self._clustering()
Expand Down
4 changes: 2 additions & 2 deletions radius_clustering/utils/emos.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ cdef extern from "mds3-util.h":
int set_size
double exec_time

Result* emos_main(int* edges, int nb_edge, int n)
Result* emos_main(unsigned int* edges, int nb_edge, int n)

void cleanup()

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

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

dominating_set = [result.dominating_set[i] - 1 for i in range(result.set_size)]
Expand Down
74 changes: 44 additions & 30 deletions radius_clustering/utils/main-emos.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,27 @@ Copyright (C) 2024, Haenn Quentin.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <time.h>
#include <sys/times.h>
#include <sys/types.h>
#include <limits.h>
#include <unistd.h>
#include <sys/resource.h>
#include <math.h>
#include <assert.h>
#include <math.h>

#ifdef _WIN32
#include <windows.h>
#include <process.h>
#include <direct.h>
#define SIGINT 2
typedef void (*SignalHandlerFn)(int);
#elif defined(__APPLE__) || defined(__linux__)
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#else
#error "Unsupported platform"
#endif


#include "mds3-util.h"
#include "util_heap.h"

Expand Down Expand Up @@ -1373,7 +1385,7 @@ void solve_subproblems(){
static void print_final_solution(char *inst){
printf("--------------------------------\n");
printf("Solution: ");
for(int i=0;i<USED(VEC_SOLUTION);i++){
for(size_t i=0;i<USED(VEC_SOLUTION);i++){
printf("%d ",ITEM(VEC_SOLUTION,i));
}
printf("\n");
Expand Down Expand Up @@ -1476,7 +1488,7 @@ void check_consistance(){
assert(!domed(CFG[i]));
}
int level=-1;
for(int idx=0;idx<USED(BRA_STK);idx++){
for(size_t idx=0;idx<USED(BRA_STK);idx++){
if(ITEM(BRA_STK,idx)==NONE){
level++;
}else if(idx<=BRAIDX[level])
Expand Down Expand Up @@ -1584,15 +1596,35 @@ void cleanup(){
}
}


void handler(int sig) {
cleanup();
exit(sig);
}

struct Result* emos_main(int* edges, int n, int nb_edge) {
#ifdef _WIN32
static BOOL WINAPI win32_handler(DWORD signal) {
if (signal == CTRL_C_EVENT) {
handler(SIGINT);
return TRUE;
}
return FALSE;
}

static void setup_signal_handler(SignalHandlerFn handler_fn) {
SetConsoleCtrlHandler(win32_handler, TRUE);
}
#else
static void setup_signal_handler(void (*handler_fn)(int)) {
signal(SIGINT, handler_fn);
}
#endif


struct Result* emos_main(unsigned int* edges, int n, int nb_edge) {

// Set the signal handler
signal(SIGINT, handler);
setup_signal_handler(handler);

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

// Get the results
int* dominating_set = (int*)malloc(USED(VEC_SOLUTION) * sizeof(int));
for (int i= 0; i<USED(VEC_SOLUTION); i++) {
for (size_t i= 0; i<USED(VEC_SOLUTION); i++) {
dominating_set[i] = ITEM(VEC_SOLUTION, i);
}

Expand Down Expand Up @@ -1645,22 +1677,4 @@ void free_results(struct Result* result) {
free(result);
}
}

/** int main(int argc, char *argv[]) {

print_compile_options();
parse_parmerters(argc,argv);
if(read_instance(argv[1])) {
initialize();
#ifndef NOR
reduce_graph();
#endif
partition_oneproblem();
solve_subproblems();
check_final_solution();
print_final_solution(getInstanceName(argv[1]));
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);
}
return 0;
} */

46 changes: 36 additions & 10 deletions radius_clustering/utils/mds3-util.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,16 @@
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <time.h>

#ifdef _WIN32
#include <windows.h>
#elif defined(__APPLE__) || defined(__linux__)
#include <sys/time.h>
#include <sys/resource.h>
#else
#error "Unsupported platform"
#endif

#define WORD_LENGTH 100
#define TRUE 1
Expand Down Expand Up @@ -200,10 +208,27 @@ struct Result {
};

static double get_utime() {
struct rusage utime;
getrusage(RUSAGE_SELF, &utime);
return (double) (utime.ru_utime.tv_sec
+ (double) utime.ru_utime.tv_usec / 1000000);
#ifdef _WIN32
FILETIME createTime;
FILETIME exitTime;
FILETIME kernelTime;
FILETIME userTime;
if (GetProcessTimes(GetCurrentProcess(),
&createTime, &exitTime,
&kernelTime, &userTime) != 0) {
ULARGE_INTEGER li = {{userTime.dwLowDateTime, userTime.dwHighDateTime}};
return li.QuadPart * 1e-7;
}
return 0.0;
#elif defined(__APPLE__) || defined(__linux__)
struct rusage utime;
if (getrusage(RUSAGE_SELF, &utime) == 0) {
return (double)utime.ru_utime.tv_sec + (double)utime.ru_utime.tv_usec * 1e-6;
}
return 0.0;
#else
return (double)clock() / CLOCKS_PER_SEC;
#endif
}

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

static void allcoate_memory_for_adjacency_list(int nb_node, int nb_edge,int offset) {
int i, block_size = 40960000, free_size = 0;
int i, block_size = 40960000;
unsigned int free_size = 0;
Init_Adj_List = (int *) malloc((2 * nb_edge + nb_node) * sizeof(int));
if (Init_Adj_List == NULL ) {
for (i = 1; i <= NB_NODE; i++) {
Expand Down Expand Up @@ -317,7 +343,7 @@ static int _read_graph_from_adjacency_matrix(int** adj_matrix, int num_nodes) {
return TRUE;
}

static int _read_graph_from_edge_list(int* edges, int n, int nb_edges) {
static int _read_graph_from_edge_list(unsigned int* edges, int n, int nb_edges) {
int i, j, l_node, r_node, nb_edge = 0, max_node = n, offset = 0;
int node = 1;

Expand Down Expand Up @@ -740,10 +766,10 @@ extern int select_branching_node();
extern void search_domset();
extern int fast_search_initial_solution();
extern void solve_subproblems();
extern struct Result* emos_main(int* edges, int n, int nb_edge);
extern int* get_dominating_set();
extern int get_set_size();
extern double get_exec_time();
extern struct Result* emos_main(unsigned int* edges, int n, int nb_edge);
extern int* get_dominating_set(struct Result* result);
extern int get_set_size(struct Result* result);
extern double get_exec_time(struct Result* result);
extern void free_results(struct Result* result);

// Declare global variables as extern
Expand Down
Loading
Loading