Skip to content
Closed
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
2 changes: 1 addition & 1 deletion .github/workflows/linux_build_wheels.yml
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ jobs:
with:
user: __token__
password: ${{ secrets.TEST_PYPI_APIKEY }}
repository_url: https://test.pypi.org/legacy/
repository-url: https://test.pypi.org/legacy/

- name: Upload to PyPI on published release
if: ${{ github.event_name == 'release' && github.event.action == 'published' }}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/mac_build_wheels.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ jobs:
with:
user: __token__
password: ${{ secrets.TEST_PYPI_APIKEY }}
repository_url: https://test.pypi.org/legacy/
repository-url: https://test.pypi.org/legacy/

- name: Upload to PyPI on published release
if: ${{ github.event_name == 'release' && github.event.action == 'published' }}
Expand Down
43 changes: 43 additions & 0 deletions R_package/banditpam/src/kmedoids_algorithm.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,25 @@
#include <string>

namespace km {

/**
* @brief Enum for different loss function types
*/
enum class LossType {
MANHATTAN,
COS,
COSINE,
INF,
EUCLIDEAN,
LP_NORM,
UNKNOWN
};

/**
* @brief Enum for different distance categories
*/
enum class AlgorithmStep { MISC, BUILD, SWAP };

/**
* @brief KMedoids class. Creates a KMedoids object that can be used to find the medoids
* for a particular set of input data.
Expand Down Expand Up @@ -462,6 +481,30 @@ class KMedoids {
*/
void checkAlgorithm(const std::string& algorithm) const;

/**
* @brief Converts a string loss function name to LossType enum
*
* @param loss The loss function string
* @returns The corresponding LossType enum value
*/
LossType getLossType(const std::string &loss) const {
if (loss == "manhattan") {
return LossType::MANHATTAN;
} else if (loss == "cos") {
return LossType::COS;
} else if (loss == "cosine") {
return LossType::COSINE;
} else if (loss == "inf") {
return LossType::INF;
} else if (loss == "euclidean") {
return LossType::EUCLIDEAN;
} else if (std::regex_match(loss, std::regex("l\\d*"))) {
return LossType::LP_NORM;
} else {
return LossType::UNKNOWN;
}
}

/// Number of medoids to use -- the "k" in k-medoids
size_t nMedoids;

Expand Down
18 changes: 8 additions & 10 deletions headers/algorithms/kmedoids_algorithm.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,7 @@ enum class LossType {
/**
* @brief Enum for different distance categories
*/
enum class AlgorithmStep {
MISC,
BUILD,
SWAP
};
enum class AlgorithmStep { MISC, BUILD, SWAP };

/**
* @brief KMedoids class. Creates a KMedoids object that can be used to find the
Expand Down Expand Up @@ -468,17 +464,18 @@ class KMedoids {
* @returns The Manhattan distance between points i and j
*/
float manhattan(const arma::fmat &data, const size_t i, const size_t j) const;

/**
* @brief Assigns ranks to each element in the input vector.
* Smallest element receives rank 1, the next 2, and so on.
* Ties are assigned the average of the ranks they would encompass.
*
* @param vec A vector containing the elements to be ranked.
*
* @returns A vector of the same size as `vec`, with each element replaced by its rank.
* @returns A vector of the same size as `vec`, with each element replaced by
* its rank.
*/
arma::fvec rank(const arma::fvec& vec) const;
arma::fvec rank(const arma::fvec &vec) const;

/**
* @brief Computes the Pearson correlation between the
Expand All @@ -491,7 +488,8 @@ class KMedoids {
* @returns The Pearson correlation between points i and j
*/
float pearson(const arma::fmat &data, const size_t i, const size_t j) const;
float clippedCos(const arma::fmat &data, const size_t i, const size_t j) const;
float clippedCos(const arma::fmat &data, const size_t i,
const size_t j) const;

/**
* @brief Computes the Spearman correlation between the
Expand All @@ -517,7 +515,7 @@ class KMedoids {

/**
* @brief Converts a string loss function name to LossType enum
*
*
* @param loss The loss function string
* @returns The corresponding LossType enum value
*/
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[project]
license = {text = "MIT"}
license = "MIT"
version = "6.0.2"
name = "banditpam"
requires-python = ">= 3.10"
Expand Down
7 changes: 3 additions & 4 deletions readme_ex1.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
# Generate data from a Gaussian Mixture Model with the given means:
np.random.seed(0)
n_per_cluster = 40
means = np.array([[0,0], [-5,5], [5,5]])
means = np.array([[0, 0], [-5, 5], [5, 5]])
X = np.vstack([np.random.randn(n_per_cluster, 2) + mu for mu in means])

# Fit the data with BanditPAM:
Expand All @@ -18,9 +18,8 @@
# Visualize the data and the medoids:
for p_idx, point in enumerate(X):
if p_idx in map(int, kmed.medoids):
plt.scatter(X[p_idx, 0], X[p_idx, 1], color='red', s = 40)
plt.scatter(X[p_idx, 0], X[p_idx, 1], color='red', s=40)
else:
plt.scatter(X[p_idx, 0], X[p_idx, 1], color='blue', s = 10)
plt.scatter(X[p_idx, 0], X[p_idx, 1], color='blue', s=10)

plt.show()

2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
pandas>=0.24.1
pybind11>=2.5.0
pybind11>=3.0.0
numpy>=1.16.2
matplotlib>=3.2.1
myst-parser>=0.16.0
28 changes: 13 additions & 15 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -447,7 +447,8 @@ def build_extensions(self): # noqa: C901
for package in ["libomp"]:
package_prefix = get_package_prefix(package)
comp_opts.append("-I{}/include".format(package_prefix))
comp_opts.append("-L{}/lib".format(package_prefix)) # Remove?
# Remove addition to comp_opts?
comp_opts.append("-L{}/lib".format(package_prefix))
link_opts.append("-L{}/lib".format(package_prefix))
elif sys.platform == "linux":
link_opts += ["-lm", "-lpthread"]
Expand All @@ -456,10 +457,10 @@ def build_extensions(self): # noqa: C901

compiler_name = compiler_check()
# if sys.platform == "darwin":
# if compiler_name == "gcc":
# link_opts.append("-lomp")
# else:
# link_opts.append("-lomp")
# if compiler_name == "gcc":
# link_opts.append("-lomp")
# else:
# link_opts.append("-lomp")
if sys.platform == "linux" or sys.platform == "linux2":
if compiler_name == "gcc":
link_opts += ["-lgomp", "-lm", "-lpthread"]
Expand Down Expand Up @@ -488,8 +489,8 @@ def build_extensions(self): # noqa: C901
ext.extra_link_args = link_opts
ext.extra_link_args += [
"-v",
] # "-arch", "x86_64"]
ext.extra_link_args = link_opts
] # "-arch", "x86_64"]
ext.extra_link_args = link_opts

build_ext.build_extensions(self)

Expand All @@ -502,11 +503,9 @@ def main():
"headers",
os.path.join("headers", "algorithms"),
os.path.join("headers", "python_bindings"),
os.path.join("../", "carma", "include"),
# os.path.join("headers", "carma", "include"),
# os.path.join("headers", "carma", "include", "carma_bits"),
os.path.join("headers", "carma", "include"),
os.path.join("headers", "carma", "include", "carma_bits"),
os.path.join("/", "usr", "local", "include"),

]
elif sys.platform == "darwin": # OSX
include_dirs = [
Expand Down Expand Up @@ -562,8 +561,8 @@ def main():

compiler_name = compiler_check()
if sys.platform == "darwin" and os.environ.get(GHA, False):
# Do NOT link omp here because it'll add an -lomp flag to dynamically link it
# (and we want to statically link it)
# Do NOT link omp here because it'll add an -lomp flag to
# dynamically link it (and we want to statically link it)
libraries = ["armadillo"]
elif sys.platform == "win32":
libraries = ["libopenblas"]
Expand Down Expand Up @@ -612,7 +611,6 @@ def main():
os.path.join(
"src", "python_bindings", "swap_times_python.cpp"
),

],
include_dirs=include_dirs,
library_dirs=library_dirs,
Expand Down Expand Up @@ -651,7 +649,7 @@ def main():
url="https://github.com/motiwari/BanditPAM",
long_description=long_description,
ext_modules=ext_modules,
setup_requires=["pybind11>=2.5.0", "numpy>=1.18"],
setup_requires=["pybind11>=3.0.0", "numpy>=1.18"],
data_files=my_data_files,
include_package_data=True,
cmdclass={"build_ext": BuildExt},
Expand Down
18 changes: 8 additions & 10 deletions src/algorithms/banditpam.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,8 @@ arma::frowvec BanditPAM::buildSigma(
#pragma omp parallel for if (this->parallelize)
for (size_t i = 0; i < N; i++) {
for (size_t j = 0; j < batchSize; j++) {
float cost =
KMedoids::cachedLoss(data, distMat, i, referencePoints(j), AlgorithmStep::MISC);
float cost = KMedoids::cachedLoss(data, distMat, i, referencePoints(j),
AlgorithmStep::MISC);
if (useAbsolute) {
sample(j) = cost;
} else {
Expand Down Expand Up @@ -136,9 +136,8 @@ arma::frowvec BanditPAM::buildTarget(
for (size_t i = 0; i < target->n_rows; i++) {
float total = 0;
for (size_t j = 0; j < referencePoints.n_rows; j++) {
float cost =
KMedoids::cachedLoss(data, distMat, (*target)(i), referencePoints(j),
AlgorithmStep::BUILD);
float cost = KMedoids::cachedLoss(
data, distMat, (*target)(i), referencePoints(j), AlgorithmStep::BUILD);
if (useAbsolute) {
total += cost;
} else {
Expand Down Expand Up @@ -286,8 +285,8 @@ arma::fmat BanditPAM::swapSigma(

// calculate change in loss for some subset of the data
for (size_t j = 0; j < batchSize; j++) {
float cost =
KMedoids::cachedLoss(data, distMat, n, referencePoints(j), AlgorithmStep::MISC);
float cost = KMedoids::cachedLoss(data, distMat, n, referencePoints(j),
AlgorithmStep::MISC);

if (k == (*assignments)(referencePoints(j))) {
if (cost < (*secondBestDistances)(referencePoints(j))) {
Expand Down Expand Up @@ -365,9 +364,8 @@ arma::fmat BanditPAM::swapTarget(
for (size_t i = 0; i < T; i++) {
// TODO(@motiwari): pragma omp parallel for?
for (size_t j = 0; j < tmpBatchSize; j++) {
float cost =
KMedoids::cachedLoss(data, distMat, (*targets)(i), referencePoints(j),
AlgorithmStep::SWAP);
float cost = KMedoids::cachedLoss(
data, distMat, (*targets)(i), referencePoints(j), AlgorithmStep::SWAP);
size_t k = (*assignments)(referencePoints(j));
if (cost < (*bestDistances)(referencePoints(j))) {
// We might be able to change this to
Expand Down
17 changes: 8 additions & 9 deletions src/algorithms/banditpam_orig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,8 @@ arma::frowvec BanditPAM_orig::buildSigma(
for (size_t i = 0; i < N; i++) {
for (size_t j = 0; j < batchSize; j++) {
// 0 for MISC
float cost =
KMedoids::cachedLoss(data, distMat, i, referencePoints(j), km::AlgorithmStep::MISC);
float cost = KMedoids::cachedLoss(data, distMat, i, referencePoints(j),
km::AlgorithmStep::MISC);
if (useAbsolute) {
sample(j) = cost;
} else {
Expand Down Expand Up @@ -138,9 +138,8 @@ arma::frowvec BanditPAM_orig::buildTarget(
for (size_t i = 0; i < target->n_rows; i++) {
float total = 0;
for (size_t j = 0; j < referencePoints.n_rows; j++) {
float cost =
KMedoids::cachedLoss(data, distMat, (*target)(i), referencePoints(j),
AlgorithmStep::BUILD);
float cost = KMedoids::cachedLoss(
data, distMat, (*target)(i), referencePoints(j), AlgorithmStep::BUILD);
if (useAbsolute) {
total += cost;
} else {
Expand Down Expand Up @@ -288,8 +287,8 @@ arma::fmat BanditPAM_orig::swapSigma(

// calculate change in loss for some subset of the data
for (size_t j = 0; j < batchSize; j++) {
float cost =
KMedoids::cachedLoss(data, distMat, n, referencePoints(j), AlgorithmStep::MISC);
float cost = KMedoids::cachedLoss(data, distMat, n, referencePoints(j),
AlgorithmStep::MISC);

if (k == (*assignments)(referencePoints(j))) {
if (cost < (*secondBestDistances)(referencePoints(j))) {
Expand Down Expand Up @@ -353,8 +352,8 @@ arma::fvec BanditPAM_orig::swapTarget(
size_t k = (*targets)(i) % medoidIndices->n_cols;
// calculate total loss for some subset of the data
for (size_t j = 0; j < tmpBatchSize; j++) {
float cost =
KMedoids::cachedLoss(data, distMat, n, referencePoints(j), AlgorithmStep::SWAP);
float cost = KMedoids::cachedLoss(data, distMat, n, referencePoints(j),
AlgorithmStep::SWAP);
if (k == (*assignments)(referencePoints(j))) {
if (cost < (*secondBestDistances)(referencePoints(j))) {
total += cost;
Expand Down
Loading
Loading