Skip to content

Commit d8a61db

Browse files
authored
Merge pull request #189 from lucasimi/develop
Develop
2 parents 04a289e + 1ac0d24 commit d8a61db

11 files changed

+81
-33
lines changed

.github/workflows/publish.yml .github/workflows/publish-pypi.yml

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
1-
name: publish
1+
name: publish-pypi
22

33
on:
44
workflow_run:
5-
workflows: ["publish-test"]
5+
workflows: ["publish-testpypi"]
66
types:
77
- completed
88
status: success
9-
workflow_dispatch:
109

1110
jobs:
1211
publish:
13-
name: Publish
12+
name: Publish [PyPI]
1413
runs-on: ubuntu-latest
1514
environment: pypi
1615
permissions:

.github/workflows/publish-test.yml .github/workflows/publish-testpypi.yml

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
1-
name: publish-test
1+
name: publish-testpypi
22

33
on:
44
push:
55
tags:
66
- 'v[0-9]+.[0-9]+.[0-9]+'
7-
workflow_dispatch:
87

98
jobs:
10-
publish-test:
11-
name: Publish
9+
publish:
10+
name: Publish [TestPyPI]
1211
runs-on: ubuntu-latest
1312
environment: pypi
1413
permissions:

.github/workflows/bench.yml .github/workflows/test-bench.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: bench
1+
name: test-bench
22

33
on:
44
push:
@@ -7,7 +7,7 @@ on:
77
- main
88

99
jobs:
10-
test-job:
10+
test-bench-job:
1111
runs-on: ubuntu-latest
1212
steps:
1313
- name: Check out repository code

.github/workflows/test.yml .github/workflows/test-unit.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: test
1+
name: test-unit
22

33
on:
44
push:
@@ -7,7 +7,7 @@ on:
77
- main
88

99
jobs:
10-
test-job:
10+
test-unit-job:
1111
runs-on: ubuntu-latest
1212
steps:
1313
- name: Check out repository code

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
[![Source Code](https://img.shields.io/badge/lucasimi-tda--mapper--python-blue?logo=github&logoColor=silver)](https://github.com/lucasimi/tda-mapper-python)
44
[![PyPI version](https://img.shields.io/pypi/v/tda-mapper?logo=python&logoColor=silver)](https://pypi.python.org/pypi/tda-mapper)
55
[![downloads](https://img.shields.io/pypi/dm/tda-mapper?logo=python&logoColor=silver)](https://pypi.python.org/pypi/tda-mapper)
6-
[![test](https://img.shields.io/github/actions/workflow/status/lucasimi/tda-mapper-python/test.yml?logo=github&logoColor=silver&branch=main&label=test)](https://github.com/lucasimi/tda-mapper-python/actions/workflows/test.yml)
7-
[![publish](https://img.shields.io/github/actions/workflow/status/lucasimi/tda-mapper-python/publish.yml?logo=github&logoColor=silver&label=publish)](https://github.com/lucasimi/tda-mapper-python/actions/workflows/publish.yml)
6+
[![test](https://img.shields.io/github/actions/workflow/status/lucasimi/tda-mapper-python/test-unit.yml?logo=github&logoColor=silver&branch=main&label=test)](https://github.com/lucasimi/tda-mapper-python/actions/workflows/test-unit.yml)
7+
[![publish](https://img.shields.io/github/actions/workflow/status/lucasimi/tda-mapper-python/publish-pypi.yml?logo=github&logoColor=silver&label=publish)](https://github.com/lucasimi/tda-mapper-python/actions/workflows/publish-pypi.yml)
88
[![docs](https://img.shields.io/readthedocs/tda-mapper/main?logo=readthedocs&logoColor=silver)](https://tda-mapper.readthedocs.io/en/main/)
99
[![codecov](https://img.shields.io/codecov/c/github/lucasimi/tda-mapper-python?logo=codecov&logoColor=silver)](https://codecov.io/github/lucasimi/tda-mapper-python)
1010
[![DOI](https://img.shields.io/badge/DOI-10.5281/zenodo.10642381-blue?logo=doi&logoColor=silver)](https://doi.org/10.5281/zenodo.10642381)

docs/source/index.rst

+4-4
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@
44
:target: https://pypi.python.org/pypi/tda-mapper
55
.. |downloads| image:: https://img.shields.io/pypi/dm/tda-mapper?logo=python&logoColor=silver
66
:target: https://pypi.python.org/pypi/tda-mapper
7-
.. |test| image:: https://img.shields.io/github/actions/workflow/status/lucasimi/tda-mapper-python/test.yml?logo=github&logoColor=silver&branch=main&label=test
8-
:target: https://github.com/lucasimi/tda-mapper-python/actions/workflows/test.yml
9-
.. |publish| image:: https://img.shields.io/github/actions/workflow/status/lucasimi/tda-mapper-python/publish.yml?logo=github&logoColor=silver&label=publish
10-
:target: https://github.com/lucasimi/tda-mapper-python/actions/workflows/publish.yml
7+
.. |test| image:: https://img.shields.io/github/actions/workflow/status/lucasimi/tda-mapper-python/test-unit.yml?logo=github&logoColor=silver&branch=main&label=test
8+
:target: https://github.com/lucasimi/tda-mapper-python/actions/workflows/test-unit.yml
9+
.. |publish| image:: https://img.shields.io/github/actions/workflow/status/lucasimi/tda-mapper-python/publish-pypi.yml?logo=github&logoColor=silver&label=publish
10+
:target: https://github.com/lucasimi/tda-mapper-python/actions/workflows/publish-pypi.yml
1111
.. |docs| image:: https://img.shields.io/readthedocs/tda-mapper/main?logo=readthedocs&logoColor=silver
1212
:target: https://tda-mapper.readthedocs.io/en/main/
1313
.. |codecov| image:: https://img.shields.io/codecov/c/github/lucasimi/tda-mapper-python?logo=codecov&logoColor=silver

pyproject.toml

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "tda-mapper"
7-
version = "0.8.1"
7+
version = "0.9.0"
88
description = "A simple and efficient Python implementation of Mapper algorithm for Topological Data Analysis"
99
readme = "README.md"
1010
authors = [{ name = "Luca Simi", email = "lucasimi90@gmail.com" }]
@@ -19,6 +19,7 @@ keywords = ["tda", "mapper", "topology", "topological data analysis"]
1919
dependencies = [
2020
"matplotlib>=3.3.4",
2121
"networkx>=2.5",
22+
"igraph>=0.11.8",
2223
"numba>=0.54",
2324
"numpy>=1.20.1, <2.0.0",
2425
"plotly>=4.14.3",

src/tdamapper/clustering.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ def __init__(self, *args, **kwargs):
4242

4343
class _MapperClustering(EstimatorMixin, ParamsMixin):
4444

45-
def __init__(self, cover=None, clustering=None, n_jobs=1):
45+
def __init__(self, cover=None, clustering=None, n_jobs=-1):
4646
self.cover = cover
4747
self.clustering = clustering
4848
self.n_jobs = n_jobs

src/tdamapper/core.py

+7-7
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@
5858
)
5959

6060

61-
def mapper_labels(X, y, cover, clustering, n_jobs=1):
61+
def mapper_labels(X, y, cover, clustering, n_jobs=-1):
6262
"""
6363
Identify the nodes of the Mapper graph.
6464
@@ -87,7 +87,7 @@ def mapper_labels(X, y, cover, clustering, n_jobs=1):
8787
interface, typically from :mod:`sklearn.cluster`.
8888
:param n_jobs: The maximum number of parallel clustering jobs. This
8989
parameter is passed to the constructor of :class:`joblib.Parallel`.
90-
Defaults to 1.
90+
Defaults to -1.
9191
:type n_jobs: int
9292
:return: A list of node labels for each point in the dataset.
9393
:rtype: list[list[int]]
@@ -112,7 +112,7 @@ def _run_clustering(local_ids):
112112
return itm_lbls
113113

114114

115-
def mapper_connected_components(X, y, cover, clustering, n_jobs=1):
115+
def mapper_connected_components(X, y, cover, clustering, n_jobs=-1):
116116
"""
117117
Identify the connected components of the Mapper graph.
118118
@@ -139,7 +139,7 @@ def mapper_connected_components(X, y, cover, clustering, n_jobs=1):
139139
interface, typically from :mod:`sklearn.cluster`.
140140
:param n_jobs: The maximum number of parallel clustering jobs. This
141141
parameter is passed to the constructor of :class:`joblib.Parallel`.
142-
Defaults to 1.
142+
Defaults to -1.
143143
:type n_jobs: int
144144
:return: A list of labels. The label at position i identifies the connected
145145
component of the point at position i in the dataset.
@@ -162,7 +162,7 @@ def mapper_connected_components(X, y, cover, clustering, n_jobs=1):
162162
return labels
163163

164164

165-
def mapper_graph(X, y, cover, clustering, n_jobs=1):
165+
def mapper_graph(X, y, cover, clustering, n_jobs=-1):
166166
"""
167167
Create the Mapper graph.
168168
@@ -189,7 +189,7 @@ def mapper_graph(X, y, cover, clustering, n_jobs=1):
189189
interface, typically from :mod:`sklearn.cluster`.
190190
:param n_jobs: The maximum number of parallel clustering jobs. This
191191
parameter is passed to the constructor of :class:`joblib.Parallel`.
192-
Defaults to 1.
192+
Defaults to -1.
193193
:type n_jobs: int
194194
:return: The Mapper graph.
195195
:rtype: :class:`networkx.Graph`
@@ -378,7 +378,7 @@ def __init__(
378378
clustering=None,
379379
failsafe=True,
380380
verbose=True,
381-
n_jobs=1,
381+
n_jobs=-1,
382382
):
383383
self.cover = cover
384384
self.clustering = clustering

src/tdamapper/learn.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,15 @@ class MapperClustering(_MapperClustering):
3535
:mod:`sklearn.cluster`
3636
:param n_jobs: The maximum number of parallel clustering jobs. This
3737
parameter is passed to the constructor of :class:`joblib.Parallel`.
38-
Defaults to 1.
38+
Defaults to -1.
3939
:type n_jobs: int
4040
"""
4141

4242
def __init__(
4343
self,
4444
cover=None,
4545
clustering=None,
46-
n_jobs=1,
46+
n_jobs=-1,
4747
):
4848
super().__init__(
4949
cover=cover,
@@ -99,7 +99,7 @@ class MapperAlgorithm(_MapperAlgorithm):
9999
:type verbose: bool, optional
100100
:param n_jobs: The maximum number of parallel clustering jobs. This
101101
parameter is passed to the constructor of :class:`joblib.Parallel`.
102-
Defaults to 1.
102+
Defaults to -1.
103103
:type n_jobs: int
104104
"""
105105

@@ -109,7 +109,7 @@ def __init__(
109109
clustering=None,
110110
failsafe=True,
111111
verbose=True,
112-
n_jobs=1,
112+
n_jobs=-1,
113113
):
114114
super().__init__(
115115
cover=cover,

src/tdamapper/plot.py

+51-2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
This module provides functionalities to visualize the Mapper graph.
33
"""
44
import networkx as nx
5+
import igraph as ig
56

67
import numpy as np
78

@@ -31,20 +32,68 @@ class MapperPlot:
3132
:param seed: The random seed used to construct the graph embedding.
3233
Defaults to None.
3334
:type seed: int, optional
35+
:param layout_engine: The engine used to compute the graph layout in the
36+
specified dimensions. Possible values are 'igraph' and 'networkx'.
37+
Defaults to 'igraph'.
38+
:type layout_engine: str, optional
3439
"""
3540

36-
def __init__(self, graph, dim, iterations=50, seed=None):
41+
def __init__(
42+
self,
43+
graph,
44+
dim,
45+
iterations=50,
46+
seed=None,
47+
layout_engine='igraph',
48+
):
3749
self.graph = graph
3850
self.dim = dim
3951
self.iterations = iterations
4052
self.seed = seed
41-
self.positions = nx.spring_layout(
53+
self.layout_engine = layout_engine
54+
self.positions = self._compute_pos()
55+
56+
def _compute_pos(self):
57+
if self.layout_engine == 'igraph':
58+
return self._compute_pos_ig()
59+
elif self.layout_engine == 'networkx':
60+
return self._compute_pos_nx()
61+
else:
62+
raise ValueError(
63+
f'Unknown engine {self.layout_engine}. '
64+
"Only possible values are 'igraph' and 'networkx'"
65+
)
66+
67+
def _compute_pos_nx(self):
68+
return nx.spring_layout(
4269
self.graph,
4370
dim=self.dim,
4471
seed=self.seed,
4572
iterations=self.iterations,
4673
)
4774

75+
def _compute_pos_ig(self):
76+
if self.graph.number_of_nodes() == 0:
77+
return {}
78+
rng = np.random.default_rng(self.seed)
79+
random_pos = rng.random((len(self.graph.nodes()), self.dim))
80+
graph_ig = ig.Graph.from_networkx(self.graph)
81+
if self.dim == 2:
82+
layout = graph_ig.layout_fruchterman_reingold(
83+
niter=self.iterations,
84+
seed=random_pos,
85+
)
86+
pos = {node: (layout[i][0], layout[i][1]) for i, node in
87+
enumerate(self.graph.nodes())}
88+
elif self.dim == 3:
89+
layout = graph_ig.layout_fruchterman_reingold_3d(
90+
niter=self.iterations,
91+
seed=random_pos,
92+
)
93+
pos = {node: (layout[i][0], layout[i][1], layout[i][2])
94+
for i, node in enumerate(self.graph.nodes())}
95+
return pos
96+
4897
def plot_matplotlib(
4998
self,
5099
colors,

0 commit comments

Comments
 (0)