chalc 4.0.0.dev1__tar.gz → 5.0.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.
- {chalc-4.0.0.dev1 → chalc-5.0.0}/PKG-INFO +1 -1
- chalc-5.0.0/docs/source/example.ipynb +302 -0
- chalc-5.0.0/docs/source/example.py +153 -0
- {chalc-4.0.0.dev1 → chalc-5.0.0}/docs/source/installation.rst +24 -3
- {chalc-4.0.0.dev1 → chalc-5.0.0}/pyproject.toml +1 -1
- {chalc-4.0.0.dev1 → chalc-5.0.0}/src/chalc/filtration.pyi +6 -0
- {chalc-4.0.0.dev1 → chalc-5.0.0}/src/chalc/plotting.py +54 -40
- chalc-4.0.0.dev1/docs/source/example.ipynb +0 -168076
- {chalc-4.0.0.dev1 → chalc-5.0.0}/.clang-format +0 -0
- {chalc-4.0.0.dev1 → chalc-5.0.0}/.gitignore +0 -0
- {chalc-4.0.0.dev1 → chalc-5.0.0}/.gitmodules +0 -0
- {chalc-4.0.0.dev1 → chalc-5.0.0}/CMakeLists.txt +0 -0
- {chalc-4.0.0.dev1 → chalc-5.0.0}/COPYING +0 -0
- {chalc-4.0.0.dev1 → chalc-5.0.0}/README.md +0 -0
- {chalc-4.0.0.dev1 → chalc-5.0.0}/build_docs.sh +0 -0
- {chalc-4.0.0.dev1 → chalc-5.0.0}/docs/Makefile +0 -0
- {chalc-4.0.0.dev1 → chalc-5.0.0}/docs/README.md +0 -0
- {chalc-4.0.0.dev1 → chalc-5.0.0}/docs/make.bat +0 -0
- {chalc-4.0.0.dev1 → chalc-5.0.0}/docs/requirements.txt +0 -0
- {chalc-4.0.0.dev1 → chalc-5.0.0}/docs/source/API.rst +0 -0
- {chalc-4.0.0.dev1 → chalc-5.0.0}/docs/source/conf.py +0 -0
- {chalc-4.0.0.dev1 → chalc-5.0.0}/docs/source/index.rst +0 -0
- {chalc-4.0.0.dev1 → chalc-5.0.0}/docs/source/license.rst +0 -0
- {chalc-4.0.0.dev1 → chalc-5.0.0}/docs/source/references.bib +0 -0
- {chalc-4.0.0.dev1 → chalc-5.0.0}/docs/static/css/custom.css +0 -0
- {chalc-4.0.0.dev1 → chalc-5.0.0}/src/ConstrainedMiniball/.clang-format +0 -0
- {chalc-4.0.0.dev1 → chalc-5.0.0}/src/ConstrainedMiniball/.git +0 -0
- {chalc-4.0.0.dev1 → chalc-5.0.0}/src/ConstrainedMiniball/.gitignore +0 -0
- {chalc-4.0.0.dev1 → chalc-5.0.0}/src/ConstrainedMiniball/CMakeLists.txt +0 -0
- {chalc-4.0.0.dev1 → chalc-5.0.0}/src/ConstrainedMiniball/CMakePresets.json +0 -0
- {chalc-4.0.0.dev1 → chalc-5.0.0}/src/ConstrainedMiniball/COPYING +0 -0
- {chalc-4.0.0.dev1 → chalc-5.0.0}/src/ConstrainedMiniball/README.md +0 -0
- {chalc-4.0.0.dev1 → chalc-5.0.0}/src/ConstrainedMiniball/cmb.hpp +0 -0
- {chalc-4.0.0.dev1 → chalc-5.0.0}/src/ConstrainedMiniball/example.cpp +0 -0
- {chalc-4.0.0.dev1 → chalc-5.0.0}/src/ConstrainedMiniball/tests/degenerate_case.cpp +0 -0
- {chalc-4.0.0.dev1 → chalc-5.0.0}/src/ConstrainedMiniball/tests/gmpzf_input.cpp +0 -0
- {chalc-4.0.0.dev1 → chalc-5.0.0}/src/ConstrainedMiniball/tests/paper_example_1.cpp +0 -0
- {chalc-4.0.0.dev1 → chalc-5.0.0}/src/ConstrainedMiniball/tests/paper_example_2.cpp +0 -0
- {chalc-4.0.0.dev1 → chalc-5.0.0}/src/ConstrainedMiniball/tests/test_utils.hpp +0 -0
- {chalc-4.0.0.dev1 → chalc-5.0.0}/src/ConstrainedMiniball/tests/triangle_2d.cpp +0 -0
- {chalc-4.0.0.dev1 → chalc-5.0.0}/src/ConstrainedMiniball/tests/triangle_3d.cpp +0 -0
- {chalc-4.0.0.dev1 → chalc-5.0.0}/src/ConstrainedMiniball/vcpkg.json +0 -0
- {chalc-4.0.0.dev1 → chalc-5.0.0}/src/chalc/__init__.py +0 -0
- {chalc-4.0.0.dev1 → chalc-5.0.0}/src/chalc/_utils.py +0 -0
- {chalc-4.0.0.dev1 → chalc-5.0.0}/src/chalc/chromatic/chromatic.cxx +0 -0
- {chalc-4.0.0.dev1 → chalc-5.0.0}/src/chalc/chromatic/chromatic.h +0 -0
- {chalc-4.0.0.dev1 → chalc-5.0.0}/src/chalc/chromatic.pyi +0 -0
- {chalc-4.0.0.dev1 → chalc-5.0.0}/src/chalc/chromatic_py.cxx +0 -0
- {chalc-4.0.0.dev1 → chalc-5.0.0}/src/chalc/common.h +0 -0
- {chalc-4.0.0.dev1 → chalc-5.0.0}/src/chalc/filtration/filtration.cxx +0 -0
- {chalc-4.0.0.dev1 → chalc-5.0.0}/src/chalc/filtration/filtration.h +0 -0
- {chalc-4.0.0.dev1 → chalc-5.0.0}/src/chalc/filtration_py.cxx +0 -0
- {chalc-4.0.0.dev1 → chalc-5.0.0}/src/chalc/py.typed +0 -0
- {chalc-4.0.0.dev1 → chalc-5.0.0}/src/chalc/sixpack.py +0 -0
- {chalc-4.0.0.dev1 → chalc-5.0.0}/tests/test_chalc.py +0 -0
- {chalc-4.0.0.dev1 → chalc-5.0.0}/vcpkg.json +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: chalc
|
|
3
|
-
Version:
|
|
3
|
+
Version: 5.0.0
|
|
4
4
|
Summary: Persistent homology of chromatic Delaunay filtrations
|
|
5
5
|
Keywords: topological data analysis,TDA,chromatic Delaunay,computational geometry,computational topology,persistent homology
|
|
6
6
|
Author-Email: Abhinav Natarajan <natarajan@maths.ox.ac.uk>
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cells": [
|
|
3
|
+
{
|
|
4
|
+
"cell_type": "markdown",
|
|
5
|
+
"metadata": {},
|
|
6
|
+
"source": [
|
|
7
|
+
"# Example Usage"
|
|
8
|
+
]
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"cell_type": "code",
|
|
12
|
+
"execution_count": null,
|
|
13
|
+
"id": "8d608221",
|
|
14
|
+
"metadata": {
|
|
15
|
+
"lines_to_next_cell": 1,
|
|
16
|
+
"nbsphinx": "hidden"
|
|
17
|
+
},
|
|
18
|
+
"outputs": [],
|
|
19
|
+
"source": [
|
|
20
|
+
"%matplotlib inline\n",
|
|
21
|
+
"%config InlineBackend.figure_formats = ['svg']\n",
|
|
22
|
+
"from IPython.core.display import display, Markdown\n",
|
|
23
|
+
"def printmd(object):\n",
|
|
24
|
+
" display(Markdown(str(object).replace('\\n', '<br>')))"
|
|
25
|
+
]
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"cell_type": "code",
|
|
29
|
+
"execution_count": null,
|
|
30
|
+
"id": "6952d22c",
|
|
31
|
+
"metadata": {},
|
|
32
|
+
"outputs": [],
|
|
33
|
+
"source": [
|
|
34
|
+
"import chalc as ch\n",
|
|
35
|
+
"import numpy as np\n",
|
|
36
|
+
"import matplotlib.pyplot as plt"
|
|
37
|
+
]
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"cell_type": "code",
|
|
41
|
+
"execution_count": null,
|
|
42
|
+
"metadata": {
|
|
43
|
+
"nbsphinx": "hidden"
|
|
44
|
+
},
|
|
45
|
+
"outputs": [],
|
|
46
|
+
"source": [
|
|
47
|
+
"plt.rcParams[\"animation.html\"] = \"jshtml\"\n",
|
|
48
|
+
"import h5py, logging, sys\n",
|
|
49
|
+
"\n",
|
|
50
|
+
"# Disable some deprecation warnings from the seaborn plotting library\n",
|
|
51
|
+
"logging.basicConfig(stream=sys.stdout, level=logging.WARNING)\n",
|
|
52
|
+
"logging.captureWarnings(True)"
|
|
53
|
+
]
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"cell_type": "markdown",
|
|
57
|
+
"metadata": {},
|
|
58
|
+
"source": [
|
|
59
|
+
"For our data we sample 100 points on a circle with some noise and 100 points from inside the unit disk."
|
|
60
|
+
]
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"cell_type": "code",
|
|
64
|
+
"execution_count": null,
|
|
65
|
+
"metadata": {
|
|
66
|
+
"lines_to_next_cell": 2
|
|
67
|
+
},
|
|
68
|
+
"outputs": [],
|
|
69
|
+
"source": [
|
|
70
|
+
"rng = np.random.default_rng(40)\n",
|
|
71
|
+
"num_points_circle = 100\n",
|
|
72
|
+
"num_points_disk = 100\n",
|
|
73
|
+
"mean = [0, 0]\n",
|
|
74
|
+
"cov = np.eye(2)*0.01\n",
|
|
75
|
+
"circle = np.array([[np.sin(2*np.pi*t), np.cos(2*np.pi*t)] for t in rng.random(num_points_circle)]).T +\\\n",
|
|
76
|
+
" rng.multivariate_normal(mean, cov, num_points_circle).T # points as columns\n",
|
|
77
|
+
"disk = np.sqrt(rng.random(num_points_disk)) * np.array([[np.sin(2*np.pi*t), np.cos(2*np.pi*t)] for t in rng.random(num_points_disk)]).T\n",
|
|
78
|
+
"points = np.concatenate((circle, disk), axis=1)\n",
|
|
79
|
+
"plt.scatter(circle[0, :], circle[1, :], s=10)\n",
|
|
80
|
+
"plt.scatter(disk[0, :], disk[1, :], s=10)\n",
|
|
81
|
+
"plt.gca().set_aspect('equal')\n",
|
|
82
|
+
"colours = [0]*num_points_circle + [1]*num_points_disk\n",
|
|
83
|
+
"plt.title('Point cloud')\n",
|
|
84
|
+
"plt.show()"
|
|
85
|
+
]
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
"cell_type": "raw",
|
|
89
|
+
"metadata": {
|
|
90
|
+
"raw_mimetype": "text/restructuredtext"
|
|
91
|
+
},
|
|
92
|
+
"source": [
|
|
93
|
+
"We can compute the chromatic alpha/Delaunay--Cech/Delaunay--Rips complex $K$ of the point cloud using the functions :func:`chromatic.alpha <chalc.chromatic.alpha>`, :func:`chromatic.delcech <chalc.chromatic.delcech>`, and :func:`chromatic.delrips <chalc.chromatic.delrips>` respectively.\n",
|
|
94
|
+
"In each of these cases, :math:`K` has far fewer simplices than either the Čech or Vietoris--Rips complex, which each have :math:`\\displaystyle \\binom{200}{2} = 19900` edges and :math:`\\displaystyle \\binom{200}{3} = 1313400` 2-simplices."
|
|
95
|
+
]
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
"cell_type": "code",
|
|
99
|
+
"execution_count": null,
|
|
100
|
+
"metadata": {},
|
|
101
|
+
"outputs": [],
|
|
102
|
+
"source": [
|
|
103
|
+
"K, _ = ch.chromatic.alpha(points, colours)\n",
|
|
104
|
+
"printmd(f'$K$ has {len(K.simplices[1])} 1-simplices and {len(K.simplices[2])} 2-simplices')"
|
|
105
|
+
]
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
"cell_type": "raw",
|
|
109
|
+
"id": "fd88a7b6",
|
|
110
|
+
"metadata": {
|
|
111
|
+
"raw_mimetype": "text/restructuredtext"
|
|
112
|
+
},
|
|
113
|
+
"source": [
|
|
114
|
+
":math:`K` is a :class:`FilteredComplex <chalc.filtration.FilteredComplex>` object, and simplices in the filtration are objects of type :class:`Simplex <chalc.filtration.Simplex>` (although you should not generally need to interact with the API of these objects).\n",
|
|
115
|
+
"We can visualise the filtration at any given time using :func:`plotting.draw_filtration <chalc.plotting.draw_filtration>`. For example, at time 0.3 we have:"
|
|
116
|
+
]
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
"cell_type": "code",
|
|
120
|
+
"execution_count": null,
|
|
121
|
+
"metadata": {},
|
|
122
|
+
"outputs": [],
|
|
123
|
+
"source": [
|
|
124
|
+
"fig, ax = plt.subplots(1, 3, figsize=(12, 4))\n",
|
|
125
|
+
"ch.plotting.draw_filtration(K, points, time=0.3, include_colours=[0], ax=ax[0])\n",
|
|
126
|
+
"ax[0].set_title('Colour 0')\n",
|
|
127
|
+
"ch.plotting.draw_filtration(K, points, time=0.3, include_colours=[1], ax=ax[1])\n",
|
|
128
|
+
"ax[1].set_title('Colour 1')\n",
|
|
129
|
+
"ch.plotting.draw_filtration(K, points, time=0.3, ax=ax[2])\n",
|
|
130
|
+
"ax[2].set_title('Both colours')\n",
|
|
131
|
+
"for i in range(3):\n",
|
|
132
|
+
" ax[i].set_xlim(-1.2, 1.2)\n",
|
|
133
|
+
" ax[i].set_ylim(-1.2, 1.2)\n",
|
|
134
|
+
"fig.suptitle('Complexes at $t=0.3$')\n",
|
|
135
|
+
"plt.tight_layout()\n",
|
|
136
|
+
"plt.show()"
|
|
137
|
+
]
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
"cell_type": "raw",
|
|
141
|
+
"id": "167229a5",
|
|
142
|
+
"metadata": {
|
|
143
|
+
"raw_mimetype": "text/restructuredtext"
|
|
144
|
+
},
|
|
145
|
+
"source": [
|
|
146
|
+
"The functions :func:`sixpack.from_filtration <chalc.sixpack.from_filtration>` and :func:`sixpack.compute <chalc.sixpack.compute>` compute the 6-pack of persistent homology diagrams of the chromatic alpha/Delaunay--Čech complex/Delaunay--Rips complexes."
|
|
147
|
+
]
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
"cell_type": "code",
|
|
151
|
+
"execution_count": null,
|
|
152
|
+
"metadata": {},
|
|
153
|
+
"outputs": [],
|
|
154
|
+
"source": [
|
|
155
|
+
"# using the previously computed filtration\n",
|
|
156
|
+
"dgms_alpha = ch.sixpack.from_filtration(K, dom=[0])\n",
|
|
157
|
+
"# or directly from the point cloud\n",
|
|
158
|
+
"dgms_delcech = ch.sixpack.compute(points, colours, dom=[0], method=\"delcech\")\n",
|
|
159
|
+
"dgms_delrips = ch.sixpack.compute(points, colours, dom=[0], method=\"delrips\")"
|
|
160
|
+
]
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
"cell_type": "raw",
|
|
164
|
+
"id": "7c357ec4",
|
|
165
|
+
"metadata": {
|
|
166
|
+
"raw_mimetype": "text/restructuredtext"
|
|
167
|
+
},
|
|
168
|
+
"source": [
|
|
169
|
+
"Each 6-pack is an object of type :class:`DiagramEnsemble <chalc.sixpack.DiagramEnsemble>`, with attributes ``ker``, ``cok``, ``im``, ``dom``, ``cod``, ``rel``, ``entrance_times``, and ``dimensions``.\n",
|
|
170
|
+
"Each of the first 6 of those attributes is an object of type :class:`SimplexPairings <chalc.sixpack.SimplexPairings>` that has sets of paired and unpaired simplices, while the last two attributes are lists indicate the filtration time and dimension of those simplices. \n",
|
|
171
|
+
"For example-"
|
|
172
|
+
]
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
"cell_type": "code",
|
|
176
|
+
"execution_count": null,
|
|
177
|
+
"metadata": {},
|
|
178
|
+
"outputs": [],
|
|
179
|
+
"source": [
|
|
180
|
+
"print(dgms_alpha.ker)"
|
|
181
|
+
]
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
"cell_type": "raw",
|
|
185
|
+
"id": "f4f57b77",
|
|
186
|
+
"metadata": {
|
|
187
|
+
"raw_mimetype": "text/restructuredtext"
|
|
188
|
+
},
|
|
189
|
+
"source": [
|
|
190
|
+
"A convenience function :func:`get <chalc.sixpack.DiagramEnsemble.get>` is provided to extract the diagrams as a matrix of birth/death times:"
|
|
191
|
+
]
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
"cell_type": "code",
|
|
195
|
+
"execution_count": null,
|
|
196
|
+
"metadata": {},
|
|
197
|
+
"outputs": [],
|
|
198
|
+
"source": [
|
|
199
|
+
"np.set_printoptions(threshold=10)\n",
|
|
200
|
+
"print(dgms_alpha.get('ker', 1)) # get the kernel in dimension one\n",
|
|
201
|
+
"# dgms_alpha.get('ker', [0, 1]) to get the kernel in dimensions zero and one\n",
|
|
202
|
+
"# dgms_alpha.get('ker') to get the kernel in all dimensions from zero to max(dgms_alpha.dimensions)"
|
|
203
|
+
]
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
"cell_type": "raw",
|
|
207
|
+
"id": "004442bf",
|
|
208
|
+
"metadata": {
|
|
209
|
+
"raw_mimetype": "text/restructuredtext"
|
|
210
|
+
},
|
|
211
|
+
"source": [
|
|
212
|
+
"The convenience functions :func:`plotting.plot_sixpack <chalc.plotting.plot_sixpack>` and :func:`plotting.plot_diagram <chalc.plotting.plot_diagram>` are provided for plotting either a 6-pack or a specific diagram from a 6-pack."
|
|
213
|
+
]
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
"cell_type": "code",
|
|
217
|
+
"execution_count": null,
|
|
218
|
+
"metadata": {},
|
|
219
|
+
"outputs": [],
|
|
220
|
+
"source": [
|
|
221
|
+
"fig1, ax1 = ch.plotting.plot_sixpack(dgms_alpha, tolerance = 1e-3)\n",
|
|
222
|
+
"fig1.suptitle('Chromatic Alpha')\n",
|
|
223
|
+
"fig1.set_figwidth(15)\n",
|
|
224
|
+
"fig1.set_figheight(9)\n",
|
|
225
|
+
"fig1.subplots_adjust(top=0.92)\n",
|
|
226
|
+
"plt.show()\n",
|
|
227
|
+
"fig2, ax2 = plt.subplots(1, 3)\n",
|
|
228
|
+
"# You can specify the dimensions of features to include in the plot.\n",
|
|
229
|
+
"ch.plotting.plot_diagram(dgms_alpha, 'rel', dimensions = {0, 2}, truncation = 0.3, ax = ax2[0], tolerance = 1e-3)\n",
|
|
230
|
+
"ax2[0].set_title('Chromatic Alpha')\n",
|
|
231
|
+
"# You can also specify a single dimension as an integer.\n",
|
|
232
|
+
"ch.plotting.plot_diagram(dgms_delcech, 'rel', dimensions = 1, truncation = 0.3, ax = ax2[1], tolerance = 1e-3)\n",
|
|
233
|
+
"ax2[1].set_title('Chromatic Delaunay-Cech')\n",
|
|
234
|
+
"# If no dimensions are specified, all features will be included.\n",
|
|
235
|
+
"ch.plotting.plot_diagram(dgms_delrips, 'rel', truncation = 0.3, ax = ax2[2], tolerance = 1e-3)\n",
|
|
236
|
+
"ax2[2].set_title('Chromatic Delaunay-Rips')\n",
|
|
237
|
+
"fig2.suptitle('Individual relative diagrams')\n",
|
|
238
|
+
"fig2.set_figwidth(15)\n",
|
|
239
|
+
"plt.show()"
|
|
240
|
+
]
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
"cell_type": "markdown",
|
|
244
|
+
"metadata": {},
|
|
245
|
+
"source": [
|
|
246
|
+
"We can save the diagrams to file or load a diagram from file:"
|
|
247
|
+
]
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
"cell_type": "code",
|
|
251
|
+
"execution_count": null,
|
|
252
|
+
"metadata": {},
|
|
253
|
+
"outputs": [],
|
|
254
|
+
"source": [
|
|
255
|
+
"with h5py.File('test.h5', 'w') as f:\n",
|
|
256
|
+
" ch.sixpack.save_diagrams(dgms_alpha, f)\n",
|
|
257
|
+
"\n",
|
|
258
|
+
"with h5py.File('test.h5', 'r') as f:\n",
|
|
259
|
+
" dgms_alpha = ch.sixpack.load_diagrams(f)"
|
|
260
|
+
]
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
"cell_type": "markdown",
|
|
264
|
+
"metadata": {},
|
|
265
|
+
"source": [
|
|
266
|
+
"We can visualise the 2-skeleton of the filtration for points in 2D:"
|
|
267
|
+
]
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
"cell_type": "code",
|
|
271
|
+
"execution_count": null,
|
|
272
|
+
"metadata": {},
|
|
273
|
+
"outputs": [],
|
|
274
|
+
"source": [
|
|
275
|
+
"animation = ch.plotting.animate_filtration(\n",
|
|
276
|
+
" K, points, filtration_times=list(np.linspace(0, 1.0, 45)), animation_length=5)\n",
|
|
277
|
+
"animation"
|
|
278
|
+
]
|
|
279
|
+
}
|
|
280
|
+
],
|
|
281
|
+
"metadata": {
|
|
282
|
+
"kernelspec": {
|
|
283
|
+
"display_name": "Python 3 (ipykernel)",
|
|
284
|
+
"language": "python",
|
|
285
|
+
"name": "python3"
|
|
286
|
+
},
|
|
287
|
+
"language_info": {
|
|
288
|
+
"codemirror_mode": {
|
|
289
|
+
"name": "ipython",
|
|
290
|
+
"version": 3
|
|
291
|
+
},
|
|
292
|
+
"file_extension": ".py",
|
|
293
|
+
"mimetype": "text/x-python",
|
|
294
|
+
"name": "python",
|
|
295
|
+
"nbconvert_exporter": "python",
|
|
296
|
+
"pygments_lexer": "ipython3",
|
|
297
|
+
"version": "3.12.5"
|
|
298
|
+
}
|
|
299
|
+
},
|
|
300
|
+
"nbformat": 4,
|
|
301
|
+
"nbformat_minor": 4
|
|
302
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# ---
|
|
2
|
+
# jupyter:
|
|
3
|
+
# jupytext:
|
|
4
|
+
# text_representation:
|
|
5
|
+
# extension: .py
|
|
6
|
+
# format_name: percent
|
|
7
|
+
# format_version: '1.3'
|
|
8
|
+
# jupytext_version: 1.16.4
|
|
9
|
+
# kernelspec:
|
|
10
|
+
# display_name: Python 3 (ipykernel)
|
|
11
|
+
# language: python
|
|
12
|
+
# name: python3
|
|
13
|
+
# ---
|
|
14
|
+
|
|
15
|
+
# %% [markdown]
|
|
16
|
+
# # Example Usage
|
|
17
|
+
|
|
18
|
+
# %% nbsphinx="hidden"
|
|
19
|
+
# %matplotlib inline
|
|
20
|
+
# %config InlineBackend.figure_formats = ['svg']
|
|
21
|
+
from IPython.core.display import display, Markdown
|
|
22
|
+
def printmd(object):
|
|
23
|
+
display(Markdown(str(object).replace('\n', '<br>')))
|
|
24
|
+
|
|
25
|
+
# %%
|
|
26
|
+
import chalc as ch
|
|
27
|
+
import numpy as np
|
|
28
|
+
import matplotlib.pyplot as plt
|
|
29
|
+
|
|
30
|
+
# %% nbsphinx="hidden"
|
|
31
|
+
plt.rcParams["animation.html"] = "jshtml"
|
|
32
|
+
import h5py, logging, sys
|
|
33
|
+
|
|
34
|
+
# Disable some deprecation warnings from the seaborn plotting library
|
|
35
|
+
logging.basicConfig(stream=sys.stdout, level=logging.WARNING)
|
|
36
|
+
logging.captureWarnings(True)
|
|
37
|
+
|
|
38
|
+
# %% [markdown]
|
|
39
|
+
# For our data we sample 100 points on a circle with some noise and 100 points from inside the unit disk.
|
|
40
|
+
|
|
41
|
+
# %%
|
|
42
|
+
rng = np.random.default_rng(40)
|
|
43
|
+
num_points_circle = 100
|
|
44
|
+
num_points_disk = 100
|
|
45
|
+
mean = [0, 0]
|
|
46
|
+
cov = np.eye(2)*0.01
|
|
47
|
+
circle = np.array([[np.sin(2*np.pi*t), np.cos(2*np.pi*t)] for t in rng.random(num_points_circle)]).T +\
|
|
48
|
+
rng.multivariate_normal(mean, cov, num_points_circle).T # points as columns
|
|
49
|
+
disk = np.sqrt(rng.random(num_points_disk)) * np.array([[np.sin(2*np.pi*t), np.cos(2*np.pi*t)] for t in rng.random(num_points_disk)]).T
|
|
50
|
+
points = np.concatenate((circle, disk), axis=1)
|
|
51
|
+
plt.scatter(circle[0, :], circle[1, :], s=10)
|
|
52
|
+
plt.scatter(disk[0, :], disk[1, :], s=10)
|
|
53
|
+
plt.gca().set_aspect('equal')
|
|
54
|
+
colours = [0]*num_points_circle + [1]*num_points_disk
|
|
55
|
+
plt.title('Point cloud')
|
|
56
|
+
plt.show()
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
# %% [raw] raw_mimetype="text/restructuredtext"
|
|
60
|
+
# We can compute the chromatic alpha/Delaunay--Cech/Delaunay--Rips complex $K$ of the point cloud using the functions :func:`chromatic.alpha <chalc.chromatic.alpha>`, :func:`chromatic.delcech <chalc.chromatic.delcech>`, and :func:`chromatic.delrips <chalc.chromatic.delrips>` respectively.
|
|
61
|
+
# In each of these cases, :math:`K` has far fewer simplices than either the Čech or Vietoris--Rips complex, which each have :math:`\displaystyle \binom{200}{2} = 19900` edges and :math:`\displaystyle \binom{200}{3} = 1313400` 2-simplices.
|
|
62
|
+
|
|
63
|
+
# %%
|
|
64
|
+
K, _ = ch.chromatic.alpha(points, colours)
|
|
65
|
+
printmd(f'$K$ has {len(K.simplices[1])} 1-simplices and {len(K.simplices[2])} 2-simplices')
|
|
66
|
+
|
|
67
|
+
# %% [raw] raw_mimetype="text/restructuredtext"
|
|
68
|
+
# :math:`K` is a :class:`FilteredComplex <chalc.filtration.FilteredComplex>` object, and simplices in the filtration are objects of type :class:`Simplex <chalc.filtration.Simplex>` (although you should not generally need to interact with the API of these objects).
|
|
69
|
+
# We can visualise the filtration at any given time using :func:`plotting.draw_filtration <chalc.plotting.draw_filtration>`. For example, at time 0.3 we have:
|
|
70
|
+
|
|
71
|
+
# %%
|
|
72
|
+
fig, ax = plt.subplots(1, 3, figsize=(12, 4))
|
|
73
|
+
ch.plotting.draw_filtration(K, points, time=0.3, include_colours=[0], ax=ax[0])
|
|
74
|
+
ax[0].set_title('Colour 0')
|
|
75
|
+
ch.plotting.draw_filtration(K, points, time=0.3, include_colours=[1], ax=ax[1])
|
|
76
|
+
ax[1].set_title('Colour 1')
|
|
77
|
+
ch.plotting.draw_filtration(K, points, time=0.3, ax=ax[2])
|
|
78
|
+
ax[2].set_title('Both colours')
|
|
79
|
+
for i in range(3):
|
|
80
|
+
ax[i].set_xlim(-1.2, 1.2)
|
|
81
|
+
ax[i].set_ylim(-1.2, 1.2)
|
|
82
|
+
fig.suptitle('Complexes at $t=0.3$')
|
|
83
|
+
plt.tight_layout()
|
|
84
|
+
plt.show()
|
|
85
|
+
|
|
86
|
+
# %% [raw] raw_mimetype="text/restructuredtext"
|
|
87
|
+
# The functions :func:`sixpack.from_filtration <chalc.sixpack.from_filtration>` and :func:`sixpack.compute <chalc.sixpack.compute>` compute the 6-pack of persistent homology diagrams of the chromatic alpha/Delaunay--Čech complex/Delaunay--Rips complexes.
|
|
88
|
+
|
|
89
|
+
# %%
|
|
90
|
+
# using the previously computed filtration
|
|
91
|
+
dgms_alpha = ch.sixpack.from_filtration(K, dom=[0])
|
|
92
|
+
# or directly from the point cloud
|
|
93
|
+
dgms_delcech = ch.sixpack.compute(points, colours, dom=[0], method="delcech")
|
|
94
|
+
dgms_delrips = ch.sixpack.compute(points, colours, dom=[0], method="delrips")
|
|
95
|
+
|
|
96
|
+
# %% [raw] raw_mimetype="text/restructuredtext"
|
|
97
|
+
# Each 6-pack is an object of type :class:`DiagramEnsemble <chalc.sixpack.DiagramEnsemble>`, with attributes ``ker``, ``cok``, ``im``, ``dom``, ``cod``, ``rel``, ``entrance_times``, and ``dimensions``.
|
|
98
|
+
# Each of the first 6 of those attributes is an object of type :class:`SimplexPairings <chalc.sixpack.SimplexPairings>` that has sets of paired and unpaired simplices, while the last two attributes are lists indicate the filtration time and dimension of those simplices.
|
|
99
|
+
# For example-
|
|
100
|
+
|
|
101
|
+
# %%
|
|
102
|
+
print(dgms_alpha.ker)
|
|
103
|
+
|
|
104
|
+
# %% [raw] raw_mimetype="text/restructuredtext"
|
|
105
|
+
# A convenience function :func:`get <chalc.sixpack.DiagramEnsemble.get>` is provided to extract the diagrams as a matrix of birth/death times:
|
|
106
|
+
|
|
107
|
+
# %%
|
|
108
|
+
np.set_printoptions(threshold=10)
|
|
109
|
+
print(dgms_alpha.get('ker', 1)) # get the kernel in dimension one
|
|
110
|
+
# dgms_alpha.get('ker', [0, 1]) to get the kernel in dimensions zero and one
|
|
111
|
+
# dgms_alpha.get('ker') to get the kernel in all dimensions from zero to max(dgms_alpha.dimensions)
|
|
112
|
+
|
|
113
|
+
# %% [raw] raw_mimetype="text/restructuredtext"
|
|
114
|
+
# The convenience functions :func:`plotting.plot_sixpack <chalc.plotting.plot_sixpack>` and :func:`plotting.plot_diagram <chalc.plotting.plot_diagram>` are provided for plotting either a 6-pack or a specific diagram from a 6-pack.
|
|
115
|
+
|
|
116
|
+
# %%
|
|
117
|
+
fig1, ax1 = ch.plotting.plot_sixpack(dgms_alpha, tolerance = 1e-3)
|
|
118
|
+
fig1.suptitle('Chromatic Alpha')
|
|
119
|
+
fig1.set_figwidth(15)
|
|
120
|
+
fig1.set_figheight(9)
|
|
121
|
+
fig1.subplots_adjust(top=0.92)
|
|
122
|
+
plt.show()
|
|
123
|
+
fig2, ax2 = plt.subplots(1, 3)
|
|
124
|
+
# You can specify the dimensions of features to include in the plot.
|
|
125
|
+
ch.plotting.plot_diagram(dgms_alpha, 'rel', dimensions = {0, 2}, truncation = 0.3, ax = ax2[0], tolerance = 1e-3)
|
|
126
|
+
ax2[0].set_title('Chromatic Alpha')
|
|
127
|
+
# You can also specify a single dimension as an integer.
|
|
128
|
+
ch.plotting.plot_diagram(dgms_delcech, 'rel', dimensions = 1, truncation = 0.3, ax = ax2[1], tolerance = 1e-3)
|
|
129
|
+
ax2[1].set_title('Chromatic Delaunay-Cech')
|
|
130
|
+
# If no dimensions are specified, all features will be included.
|
|
131
|
+
ch.plotting.plot_diagram(dgms_delrips, 'rel', truncation = 0.3, ax = ax2[2], tolerance = 1e-3)
|
|
132
|
+
ax2[2].set_title('Chromatic Delaunay-Rips')
|
|
133
|
+
fig2.suptitle('Individual relative diagrams')
|
|
134
|
+
fig2.set_figwidth(15)
|
|
135
|
+
plt.show()
|
|
136
|
+
|
|
137
|
+
# %% [markdown]
|
|
138
|
+
# We can save the diagrams to file or load a diagram from file:
|
|
139
|
+
|
|
140
|
+
# %%
|
|
141
|
+
with h5py.File('test.h5', 'w') as f:
|
|
142
|
+
ch.sixpack.save_diagrams(dgms_alpha, f)
|
|
143
|
+
|
|
144
|
+
with h5py.File('test.h5', 'r') as f:
|
|
145
|
+
dgms_alpha = ch.sixpack.load_diagrams(f)
|
|
146
|
+
|
|
147
|
+
# %% [markdown]
|
|
148
|
+
# We can visualise the 2-skeleton of the filtration for points in 2D:
|
|
149
|
+
|
|
150
|
+
# %%
|
|
151
|
+
animation = ch.plotting.animate_filtration(
|
|
152
|
+
K, points, filtration_times=list(np.linspace(0, 1.0, 45)), animation_length=5)
|
|
153
|
+
animation
|
|
@@ -31,7 +31,7 @@ Build dependencies
|
|
|
31
31
|
On Linux: GCC11 or later.
|
|
32
32
|
On MacOS: Clang 14 or later.
|
|
33
33
|
3. (Recommended) `Microsoft vcpkg <https://vcpkg.io/>`_ C++ dependency manager.
|
|
34
|
-
4. (MacOS only) The build tools automake, autoconf, and libtool. You can install these with
|
|
34
|
+
4. (MacOS only) The build tools automake, autoconf, and libtool. You can install these with ``brew install automake autoconf libtool``.
|
|
35
35
|
|
|
36
36
|
Build steps
|
|
37
37
|
^^^^^^^^^^^
|
|
@@ -92,13 +92,34 @@ Build the package wheel using ``pip wheel`` and install the compiled binary.
|
|
|
92
92
|
Building the Documentation
|
|
93
93
|
--------------------------
|
|
94
94
|
|
|
95
|
-
To build the documentation, first make sure that ``chalc`` is installed and available to the Python interpreter.
|
|
95
|
+
To build the documentation, first make sure that ``chalc`` is installed and available to the Python interpreter.
|
|
96
|
+
Then run the following commands from the root directory of the repository.
|
|
96
97
|
|
|
97
98
|
.. code-block:: console
|
|
98
99
|
|
|
99
100
|
$ cd docs
|
|
100
101
|
$ pip install -r requirements.txt
|
|
101
|
-
|
|
102
|
+
|
|
103
|
+
This installs the necessary dependencies used to generate the documentation from the API.
|
|
104
|
+
The commands to build the documentation depend on the platform you are using:
|
|
105
|
+
|
|
106
|
+
.. tab-set::
|
|
107
|
+
|
|
108
|
+
.. tab-item:: Bash
|
|
109
|
+
:sync: bash
|
|
110
|
+
|
|
111
|
+
.. code-block:: bash
|
|
112
|
+
|
|
113
|
+
$ make html
|
|
114
|
+
|
|
115
|
+
.. tab-item:: Powershell
|
|
116
|
+
:sync: powershell
|
|
117
|
+
|
|
118
|
+
.. code-block:: powershell
|
|
119
|
+
|
|
120
|
+
> python -m pybind11_stubgen chalc.chromatic --numpy-array-use-type-var --output-dir ../src
|
|
121
|
+
> python -m pybind11_stubgen chalc.filtration --numpy-array-use-type-var --output-dir ../src
|
|
122
|
+
> sphinx-build -M html source build
|
|
102
123
|
|
|
103
124
|
This will build the documentation into the folder ``docs/build`` with root ``index.html``.
|
|
104
125
|
In order to avoid polluting your global Python package tree, it is a good idea to install ``chalc`` and run the ``pip install ..`` command above in a virtual environment.
|
|
@@ -4,7 +4,7 @@ build-backend = "scikit_build_core.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "chalc"
|
|
7
|
-
version = "
|
|
7
|
+
version = "5.0.0"
|
|
8
8
|
authors = [{ name = "Abhinav Natarajan", email = "natarajan@maths.ox.ac.uk" }]
|
|
9
9
|
description = "Persistent homology of chromatic Delaunay filtrations"
|
|
10
10
|
readme = { file = "README.md", content-type = "text/markdown" }
|
|
@@ -11,6 +11,9 @@ class FilteredComplex:
|
|
|
11
11
|
|
|
12
12
|
Class representing a filtered simplicial complex.
|
|
13
13
|
"""
|
|
14
|
+
@staticmethod
|
|
15
|
+
def _pybind11_conduit_v1_(*args, **kwargs):
|
|
16
|
+
...
|
|
14
17
|
def __init__(self, n: int, k: int) -> None:
|
|
15
18
|
"""
|
|
16
19
|
Construct a discrete filtered simplicial complex with default filtration time of 0.
|
|
@@ -113,6 +116,9 @@ class Simplex:
|
|
|
113
116
|
Class representing a simplex in a filtered simplicial complex.
|
|
114
117
|
|
|
115
118
|
"""
|
|
119
|
+
@staticmethod
|
|
120
|
+
def _pybind11_conduit_v1_(*args, **kwargs):
|
|
121
|
+
...
|
|
116
122
|
def __repr__(self) -> str:
|
|
117
123
|
...
|
|
118
124
|
def set_colour(self, colour: int) -> None:
|