lophat 0.1.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.
@@ -0,0 +1,73 @@
1
+ /target
2
+
3
+ # Byte-compiled / optimized / DLL files
4
+ __pycache__/
5
+ .pytest_cache/
6
+ *.py[cod]
7
+
8
+ # C extensions
9
+ *.so
10
+
11
+ # Distribution / packaging
12
+ .Python
13
+ .venv/
14
+ .env/
15
+ env/
16
+ bin/
17
+ build/
18
+ develop-eggs/
19
+ dist/
20
+ eggs/
21
+ lib/
22
+ lib64/
23
+ parts/
24
+ sdist/
25
+ var/
26
+ include/
27
+ man/
28
+ venv/
29
+ *.egg-info/
30
+ .installed.cfg
31
+ *.egg
32
+
33
+ # Installer logs
34
+ pip-log.txt
35
+ pip-delete-this-directory.txt
36
+ pip-selfcheck.json
37
+
38
+ # Unit test / coverage reports
39
+ htmlcov/
40
+ .tox/
41
+ .coverage
42
+ .cache
43
+ nosetests.xml
44
+ coverage.xml
45
+
46
+ # Translations
47
+ *.mo
48
+
49
+ # Mr Developer
50
+ .mr.developer.cfg
51
+ .project
52
+ .pydevproject
53
+
54
+ # Rope
55
+ .ropeproject
56
+
57
+ # Django stuff:
58
+ *.log
59
+ *.pot
60
+
61
+ .DS_Store
62
+
63
+ # Sphinx documentation
64
+ docs/_build/
65
+
66
+ # PyCharm
67
+ .idea/
68
+
69
+ # VSCode
70
+ .vscode/
71
+
72
+ # Pyenv
73
+ .python-version
@@ -0,0 +1,408 @@
1
+ # This file is automatically @generated by Cargo.
2
+ # It is not intended for manual editing.
3
+ version = 3
4
+
5
+ [[package]]
6
+ name = "autocfg"
7
+ version = "1.1.0"
8
+ source = "registry+https://github.com/rust-lang/crates.io-index"
9
+ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
10
+
11
+ [[package]]
12
+ name = "bitflags"
13
+ version = "1.3.2"
14
+ source = "registry+https://github.com/rust-lang/crates.io-index"
15
+ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
16
+
17
+ [[package]]
18
+ name = "cfg-if"
19
+ version = "1.0.0"
20
+ source = "registry+https://github.com/rust-lang/crates.io-index"
21
+ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
22
+
23
+ [[package]]
24
+ name = "crossbeam"
25
+ version = "0.8.2"
26
+ source = "registry+https://github.com/rust-lang/crates.io-index"
27
+ checksum = "2801af0d36612ae591caa9568261fddce32ce6e08a7275ea334a06a4ad021a2c"
28
+ dependencies = [
29
+ "cfg-if",
30
+ "crossbeam-channel",
31
+ "crossbeam-deque",
32
+ "crossbeam-epoch",
33
+ "crossbeam-queue",
34
+ "crossbeam-utils",
35
+ ]
36
+
37
+ [[package]]
38
+ name = "crossbeam-channel"
39
+ version = "0.5.7"
40
+ source = "registry+https://github.com/rust-lang/crates.io-index"
41
+ checksum = "cf2b3e8478797446514c91ef04bafcb59faba183e621ad488df88983cc14128c"
42
+ dependencies = [
43
+ "cfg-if",
44
+ "crossbeam-utils",
45
+ ]
46
+
47
+ [[package]]
48
+ name = "crossbeam-deque"
49
+ version = "0.8.3"
50
+ source = "registry+https://github.com/rust-lang/crates.io-index"
51
+ checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef"
52
+ dependencies = [
53
+ "cfg-if",
54
+ "crossbeam-epoch",
55
+ "crossbeam-utils",
56
+ ]
57
+
58
+ [[package]]
59
+ name = "crossbeam-epoch"
60
+ version = "0.9.14"
61
+ source = "registry+https://github.com/rust-lang/crates.io-index"
62
+ checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695"
63
+ dependencies = [
64
+ "autocfg",
65
+ "cfg-if",
66
+ "crossbeam-utils",
67
+ "memoffset",
68
+ "scopeguard",
69
+ ]
70
+
71
+ [[package]]
72
+ name = "crossbeam-queue"
73
+ version = "0.3.8"
74
+ source = "registry+https://github.com/rust-lang/crates.io-index"
75
+ checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add"
76
+ dependencies = [
77
+ "cfg-if",
78
+ "crossbeam-utils",
79
+ ]
80
+
81
+ [[package]]
82
+ name = "crossbeam-utils"
83
+ version = "0.8.15"
84
+ source = "registry+https://github.com/rust-lang/crates.io-index"
85
+ checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b"
86
+ dependencies = [
87
+ "cfg-if",
88
+ ]
89
+
90
+ [[package]]
91
+ name = "either"
92
+ version = "1.8.1"
93
+ source = "registry+https://github.com/rust-lang/crates.io-index"
94
+ checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
95
+
96
+ [[package]]
97
+ name = "hermit-abi"
98
+ version = "0.2.6"
99
+ source = "registry+https://github.com/rust-lang/crates.io-index"
100
+ checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
101
+ dependencies = [
102
+ "libc",
103
+ ]
104
+
105
+ [[package]]
106
+ name = "indoc"
107
+ version = "1.0.9"
108
+ source = "registry+https://github.com/rust-lang/crates.io-index"
109
+ checksum = "bfa799dd5ed20a7e349f3b4639aa80d74549c81716d9ec4f994c9b5815598306"
110
+
111
+ [[package]]
112
+ name = "libc"
113
+ version = "0.2.139"
114
+ source = "registry+https://github.com/rust-lang/crates.io-index"
115
+ checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
116
+
117
+ [[package]]
118
+ name = "lock_api"
119
+ version = "0.4.9"
120
+ source = "registry+https://github.com/rust-lang/crates.io-index"
121
+ checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df"
122
+ dependencies = [
123
+ "autocfg",
124
+ "scopeguard",
125
+ ]
126
+
127
+ [[package]]
128
+ name = "lophat"
129
+ version = "0.1.0"
130
+ dependencies = [
131
+ "crossbeam",
132
+ "pinboard",
133
+ "pyo3",
134
+ "rayon",
135
+ ]
136
+
137
+ [[package]]
138
+ name = "memoffset"
139
+ version = "0.8.0"
140
+ source = "registry+https://github.com/rust-lang/crates.io-index"
141
+ checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1"
142
+ dependencies = [
143
+ "autocfg",
144
+ ]
145
+
146
+ [[package]]
147
+ name = "num_cpus"
148
+ version = "1.15.0"
149
+ source = "registry+https://github.com/rust-lang/crates.io-index"
150
+ checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b"
151
+ dependencies = [
152
+ "hermit-abi",
153
+ "libc",
154
+ ]
155
+
156
+ [[package]]
157
+ name = "once_cell"
158
+ version = "1.17.1"
159
+ source = "registry+https://github.com/rust-lang/crates.io-index"
160
+ checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
161
+
162
+ [[package]]
163
+ name = "parking_lot"
164
+ version = "0.12.1"
165
+ source = "registry+https://github.com/rust-lang/crates.io-index"
166
+ checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
167
+ dependencies = [
168
+ "lock_api",
169
+ "parking_lot_core",
170
+ ]
171
+
172
+ [[package]]
173
+ name = "parking_lot_core"
174
+ version = "0.9.7"
175
+ source = "registry+https://github.com/rust-lang/crates.io-index"
176
+ checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521"
177
+ dependencies = [
178
+ "cfg-if",
179
+ "libc",
180
+ "redox_syscall",
181
+ "smallvec",
182
+ "windows-sys",
183
+ ]
184
+
185
+ [[package]]
186
+ name = "pinboard"
187
+ version = "2.1.0"
188
+ source = "registry+https://github.com/rust-lang/crates.io-index"
189
+ checksum = "6502324eee9cd4cd7dc8f22b0ed7683185a09e9ddcc0ac3028e010e2721662a3"
190
+ dependencies = [
191
+ "crossbeam-epoch",
192
+ ]
193
+
194
+ [[package]]
195
+ name = "proc-macro2"
196
+ version = "1.0.51"
197
+ source = "registry+https://github.com/rust-lang/crates.io-index"
198
+ checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6"
199
+ dependencies = [
200
+ "unicode-ident",
201
+ ]
202
+
203
+ [[package]]
204
+ name = "pyo3"
205
+ version = "0.18.1"
206
+ source = "registry+https://github.com/rust-lang/crates.io-index"
207
+ checksum = "06a3d8e8a46ab2738109347433cb7b96dffda2e4a218b03ef27090238886b147"
208
+ dependencies = [
209
+ "cfg-if",
210
+ "indoc",
211
+ "libc",
212
+ "memoffset",
213
+ "parking_lot",
214
+ "pyo3-build-config",
215
+ "pyo3-ffi",
216
+ "pyo3-macros",
217
+ "unindent",
218
+ ]
219
+
220
+ [[package]]
221
+ name = "pyo3-build-config"
222
+ version = "0.18.1"
223
+ source = "registry+https://github.com/rust-lang/crates.io-index"
224
+ checksum = "75439f995d07ddfad42b192dfcf3bc66a7ecfd8b4a1f5f6f046aa5c2c5d7677d"
225
+ dependencies = [
226
+ "once_cell",
227
+ "target-lexicon",
228
+ ]
229
+
230
+ [[package]]
231
+ name = "pyo3-ffi"
232
+ version = "0.18.1"
233
+ source = "registry+https://github.com/rust-lang/crates.io-index"
234
+ checksum = "839526a5c07a17ff44823679b68add4a58004de00512a95b6c1c98a6dcac0ee5"
235
+ dependencies = [
236
+ "libc",
237
+ "pyo3-build-config",
238
+ ]
239
+
240
+ [[package]]
241
+ name = "pyo3-macros"
242
+ version = "0.18.1"
243
+ source = "registry+https://github.com/rust-lang/crates.io-index"
244
+ checksum = "bd44cf207476c6a9760c4653559be4f206efafb924d3e4cbf2721475fc0d6cc5"
245
+ dependencies = [
246
+ "proc-macro2",
247
+ "pyo3-macros-backend",
248
+ "quote",
249
+ "syn",
250
+ ]
251
+
252
+ [[package]]
253
+ name = "pyo3-macros-backend"
254
+ version = "0.18.1"
255
+ source = "registry+https://github.com/rust-lang/crates.io-index"
256
+ checksum = "dc1f43d8e30460f36350d18631ccf85ded64c059829208fe680904c65bcd0a4c"
257
+ dependencies = [
258
+ "proc-macro2",
259
+ "quote",
260
+ "syn",
261
+ ]
262
+
263
+ [[package]]
264
+ name = "quote"
265
+ version = "1.0.23"
266
+ source = "registry+https://github.com/rust-lang/crates.io-index"
267
+ checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
268
+ dependencies = [
269
+ "proc-macro2",
270
+ ]
271
+
272
+ [[package]]
273
+ name = "rayon"
274
+ version = "1.7.0"
275
+ source = "registry+https://github.com/rust-lang/crates.io-index"
276
+ checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b"
277
+ dependencies = [
278
+ "either",
279
+ "rayon-core",
280
+ ]
281
+
282
+ [[package]]
283
+ name = "rayon-core"
284
+ version = "1.11.0"
285
+ source = "registry+https://github.com/rust-lang/crates.io-index"
286
+ checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d"
287
+ dependencies = [
288
+ "crossbeam-channel",
289
+ "crossbeam-deque",
290
+ "crossbeam-utils",
291
+ "num_cpus",
292
+ ]
293
+
294
+ [[package]]
295
+ name = "redox_syscall"
296
+ version = "0.2.16"
297
+ source = "registry+https://github.com/rust-lang/crates.io-index"
298
+ checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
299
+ dependencies = [
300
+ "bitflags",
301
+ ]
302
+
303
+ [[package]]
304
+ name = "scopeguard"
305
+ version = "1.1.0"
306
+ source = "registry+https://github.com/rust-lang/crates.io-index"
307
+ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
308
+
309
+ [[package]]
310
+ name = "smallvec"
311
+ version = "1.10.0"
312
+ source = "registry+https://github.com/rust-lang/crates.io-index"
313
+ checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
314
+
315
+ [[package]]
316
+ name = "syn"
317
+ version = "1.0.109"
318
+ source = "registry+https://github.com/rust-lang/crates.io-index"
319
+ checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
320
+ dependencies = [
321
+ "proc-macro2",
322
+ "quote",
323
+ "unicode-ident",
324
+ ]
325
+
326
+ [[package]]
327
+ name = "target-lexicon"
328
+ version = "0.12.6"
329
+ source = "registry+https://github.com/rust-lang/crates.io-index"
330
+ checksum = "8ae9980cab1db3fceee2f6c6f643d5d8de2997c58ee8d25fb0cc8a9e9e7348e5"
331
+
332
+ [[package]]
333
+ name = "unicode-ident"
334
+ version = "1.0.8"
335
+ source = "registry+https://github.com/rust-lang/crates.io-index"
336
+ checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
337
+
338
+ [[package]]
339
+ name = "unindent"
340
+ version = "0.1.11"
341
+ source = "registry+https://github.com/rust-lang/crates.io-index"
342
+ checksum = "e1766d682d402817b5ac4490b3c3002d91dfa0d22812f341609f97b08757359c"
343
+
344
+ [[package]]
345
+ name = "windows-sys"
346
+ version = "0.45.0"
347
+ source = "registry+https://github.com/rust-lang/crates.io-index"
348
+ checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
349
+ dependencies = [
350
+ "windows-targets",
351
+ ]
352
+
353
+ [[package]]
354
+ name = "windows-targets"
355
+ version = "0.42.1"
356
+ source = "registry+https://github.com/rust-lang/crates.io-index"
357
+ checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7"
358
+ dependencies = [
359
+ "windows_aarch64_gnullvm",
360
+ "windows_aarch64_msvc",
361
+ "windows_i686_gnu",
362
+ "windows_i686_msvc",
363
+ "windows_x86_64_gnu",
364
+ "windows_x86_64_gnullvm",
365
+ "windows_x86_64_msvc",
366
+ ]
367
+
368
+ [[package]]
369
+ name = "windows_aarch64_gnullvm"
370
+ version = "0.42.1"
371
+ source = "registry+https://github.com/rust-lang/crates.io-index"
372
+ checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608"
373
+
374
+ [[package]]
375
+ name = "windows_aarch64_msvc"
376
+ version = "0.42.1"
377
+ source = "registry+https://github.com/rust-lang/crates.io-index"
378
+ checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7"
379
+
380
+ [[package]]
381
+ name = "windows_i686_gnu"
382
+ version = "0.42.1"
383
+ source = "registry+https://github.com/rust-lang/crates.io-index"
384
+ checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640"
385
+
386
+ [[package]]
387
+ name = "windows_i686_msvc"
388
+ version = "0.42.1"
389
+ source = "registry+https://github.com/rust-lang/crates.io-index"
390
+ checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605"
391
+
392
+ [[package]]
393
+ name = "windows_x86_64_gnu"
394
+ version = "0.42.1"
395
+ source = "registry+https://github.com/rust-lang/crates.io-index"
396
+ checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45"
397
+
398
+ [[package]]
399
+ name = "windows_x86_64_gnullvm"
400
+ version = "0.42.1"
401
+ source = "registry+https://github.com/rust-lang/crates.io-index"
402
+ checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463"
403
+
404
+ [[package]]
405
+ name = "windows_x86_64_msvc"
406
+ version = "0.42.1"
407
+ source = "registry+https://github.com/rust-lang/crates.io-index"
408
+ checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd"
@@ -0,0 +1,15 @@
1
+ [package]
2
+ name = "lophat"
3
+ version = "0.1.0"
4
+ edition = "2021"
5
+
6
+ # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7
+ [lib]
8
+ name = "lophat"
9
+ crate-type = ["cdylib"]
10
+
11
+ [dependencies]
12
+ crossbeam = "0.8.2"
13
+ pinboard = "2.1.0"
14
+ pyo3 = "0.18.1"
15
+ rayon = "1.7.0"
lophat-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 Thomas Chaplin
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
lophat-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,59 @@
1
+ Metadata-Version: 2.1
2
+ Name: lophat
3
+ Version: 0.1.0
4
+ Classifier: Programming Language :: Rust
5
+ Classifier: Programming Language :: Python :: Implementation :: CPython
6
+ Classifier: Programming Language :: Python :: Implementation :: PyPy
7
+ License-File: LICENSE
8
+ Requires-Python: >=3.7
9
+ Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
10
+
11
+ <div align="center">
12
+
13
+ <h1>LoPHAT</h1>
14
+
15
+ <b>Lo</b>ckfree <b>P</b>ersistent <b>H</b>omology <b>A</b>lgorithm <b>T</b>oolbox
16
+
17
+ </div>
18
+
19
+ ## Overview
20
+
21
+ LoPHAT is a Rust library implementing the lockfree algorithm for computing persistent homology (PH), introduced in [[1]](#1).
22
+ Python bindings are provided via PyO3, with an interface familiar to those who have used PHAT [[2]](#2).
23
+
24
+ The primary goal of this library is to make the algorithm accessible to those wishing to compute PH of ___arbitrary filtered chain complexes___.
25
+ In particular, LoPHAT is **not** specialised to compute PH of common filtrations or even filtered simplicial complexes.
26
+ As such, you should expect LoPHAT to under-perform as compared to [giotto-ph [3]](#3) or [oineus [4]](#4), both of which use the algorithm of [[1]](#1).
27
+
28
+ The only changes from the algorithm described in [[1]](#1) are:
29
+ * We use the `pinboard` library for memory managment of the matrices.
30
+ * We store the $j^{th}$ column of $R$ and $V$ alongside each other in memory, allowing a full $R=DV$ decomposition (rather than just computing pairings).
31
+ * Rather than split the matrix into chunks, we use a work-stealing paradigm, via the `rayon` library.
32
+
33
+ > **Warning**
34
+ > LoPHAT is currently in alpha.
35
+ > The implementation is not optimised, the API is not fixed and no tests have been written.
36
+ > Use at your own risk.
37
+
38
+ ## Usage
39
+
40
+ ## References
41
+
42
+ <a id="1">[1]</a> Morozov, Dmitriy, and Arnur Nigmetov.
43
+ "Towards lockfree persistent homology."
44
+ Proceedings of the 32nd ACM Symposium on Parallelism in Algorithms and Architectures. 2020.
45
+
46
+ <a id="2">[2]</a> Bauer, Ulrich, et al.
47
+ "Phat–persistent homology algorithms toolbox." Journal of symbolic computation 78 (2017): 76-90.
48
+ [Bitbucket](https://bitbucket.org/phat-code/phat/src/master/)
49
+
50
+ <a id="3">[3]</a> Pérez, Julián Burella, et al.
51
+ "giotto-ph: A python library for high-performance computation of persistent homology of Vietoris-Rips filtrations."
52
+ arXiv preprint [arXiv:2107.05412](https://arxiv.org/abs/2107.05412) (2021).
53
+ [GitHub](https://github.com/giotto-ai/giotto-ph)
54
+
55
+ <a id="4">[4]</a> Nigmetov, Arnur, Morozov, Dmitriy, and USDOE.
56
+ Oineus v1.0. Computer software.
57
+ [https://www.osti.gov//servlets/purl/1774764](https://www.osti.gov//servlets/purl/1774764). USDOE. 1 Apr. 2021.
58
+ Web. [doi:10.11578/dc.20210407.1](https://doi.org/10.11578/dc.20210407.1). [GitHub](https://github.com/anigmetov/oineus)
59
+
lophat-0.1.0/README.md ADDED
@@ -0,0 +1,48 @@
1
+ <div align="center">
2
+
3
+ <h1>LoPHAT</h1>
4
+
5
+ <b>Lo</b>ckfree <b>P</b>ersistent <b>H</b>omology <b>A</b>lgorithm <b>T</b>oolbox
6
+
7
+ </div>
8
+
9
+ ## Overview
10
+
11
+ LoPHAT is a Rust library implementing the lockfree algorithm for computing persistent homology (PH), introduced in [[1]](#1).
12
+ Python bindings are provided via PyO3, with an interface familiar to those who have used PHAT [[2]](#2).
13
+
14
+ The primary goal of this library is to make the algorithm accessible to those wishing to compute PH of ___arbitrary filtered chain complexes___.
15
+ In particular, LoPHAT is **not** specialised to compute PH of common filtrations or even filtered simplicial complexes.
16
+ As such, you should expect LoPHAT to under-perform as compared to [giotto-ph [3]](#3) or [oineus [4]](#4), both of which use the algorithm of [[1]](#1).
17
+
18
+ The only changes from the algorithm described in [[1]](#1) are:
19
+ * We use the `pinboard` library for memory managment of the matrices.
20
+ * We store the $j^{th}$ column of $R$ and $V$ alongside each other in memory, allowing a full $R=DV$ decomposition (rather than just computing pairings).
21
+ * Rather than split the matrix into chunks, we use a work-stealing paradigm, via the `rayon` library.
22
+
23
+ > **Warning**
24
+ > LoPHAT is currently in alpha.
25
+ > The implementation is not optimised, the API is not fixed and no tests have been written.
26
+ > Use at your own risk.
27
+
28
+ ## Usage
29
+
30
+ ## References
31
+
32
+ <a id="1">[1]</a> Morozov, Dmitriy, and Arnur Nigmetov.
33
+ "Towards lockfree persistent homology."
34
+ Proceedings of the 32nd ACM Symposium on Parallelism in Algorithms and Architectures. 2020.
35
+
36
+ <a id="2">[2]</a> Bauer, Ulrich, et al.
37
+ "Phat–persistent homology algorithms toolbox." Journal of symbolic computation 78 (2017): 76-90.
38
+ [Bitbucket](https://bitbucket.org/phat-code/phat/src/master/)
39
+
40
+ <a id="3">[3]</a> Pérez, Julián Burella, et al.
41
+ "giotto-ph: A python library for high-performance computation of persistent homology of Vietoris-Rips filtrations."
42
+ arXiv preprint [arXiv:2107.05412](https://arxiv.org/abs/2107.05412) (2021).
43
+ [GitHub](https://github.com/giotto-ai/giotto-ph)
44
+
45
+ <a id="4">[4]</a> Nigmetov, Arnur, Morozov, Dmitriy, and USDOE.
46
+ Oineus v1.0. Computer software.
47
+ [https://www.osti.gov//servlets/purl/1774764](https://www.osti.gov//servlets/purl/1774764). USDOE. 1 Apr. 2021.
48
+ Web. [doi:10.11578/dc.20210407.1](https://doi.org/10.11578/dc.20210407.1). [GitHub](https://github.com/anigmetov/oineus)
@@ -0,0 +1,16 @@
1
+ [build-system]
2
+ requires = ["maturin>=0.14,<0.15"]
3
+ build-backend = "maturin"
4
+
5
+ [project]
6
+ name = "lophat"
7
+ requires-python = ">=3.7"
8
+ classifiers = [
9
+ "Programming Language :: Rust",
10
+ "Programming Language :: Python :: Implementation :: CPython",
11
+ "Programming Language :: Python :: Implementation :: PyPy",
12
+ ]
13
+
14
+
15
+ [tool.maturin]
16
+ features = ["pyo3/extension-module"]
@@ -0,0 +1,55 @@
1
+ use std::cmp::Ordering;
2
+
3
+ pub trait Column: Sync + Clone + Send + Default {
4
+ fn pivot(&self) -> Option<usize>;
5
+ fn add_col(&mut self, other: &Self);
6
+ fn add_entry(&mut self, entry: usize);
7
+ }
8
+
9
+ #[derive(Debug, Default, Clone)]
10
+ pub struct VecColumn {
11
+ pub internal: Vec<usize>,
12
+ }
13
+
14
+ impl VecColumn {
15
+ // Returns the index where we should try to insert next entry
16
+ fn add_entry_starting_at(&mut self, entry: usize, starting_idx: usize) -> usize {
17
+ let mut working_idx = starting_idx;
18
+ while let Some(value_at_idx) = self.internal.iter().nth(working_idx) {
19
+ match value_at_idx.cmp(&entry) {
20
+ Ordering::Less => {
21
+ working_idx += 1;
22
+ continue;
23
+ }
24
+ Ordering::Equal => {
25
+ self.internal.remove(working_idx);
26
+ return working_idx;
27
+ }
28
+ Ordering::Greater => {
29
+ self.internal.insert(working_idx, entry);
30
+ return working_idx + 1;
31
+ }
32
+ }
33
+ }
34
+ // Bigger than all idxs in col - add to end
35
+ self.internal.push(entry);
36
+ return self.internal.len() - 1;
37
+ }
38
+ }
39
+
40
+ impl Column for VecColumn {
41
+ fn pivot(&self) -> Option<usize> {
42
+ self.internal.iter().last().copied()
43
+ }
44
+
45
+ fn add_entry(&mut self, entry: usize) {
46
+ self.add_entry_starting_at(entry, 0);
47
+ }
48
+
49
+ fn add_col(&mut self, other: &Self) {
50
+ let mut working_idx = 0;
51
+ for entry in other.internal.iter() {
52
+ working_idx = self.add_entry_starting_at(*entry, working_idx);
53
+ }
54
+ }
55
+ }
@@ -0,0 +1,71 @@
1
+ use crate::Column;
2
+ use pyo3::prelude::*;
3
+ use std::collections::{HashMap, HashSet};
4
+
5
+ #[pyclass]
6
+ #[derive(Default, Debug, Clone)]
7
+ pub struct PersistenceDiagram {
8
+ #[pyo3(get)]
9
+ unpaired: HashSet<usize>,
10
+ #[pyo3(get)]
11
+ paired: Vec<(usize, usize)>,
12
+ }
13
+
14
+ #[derive(Debug, Default)]
15
+ pub struct RVDecomposition<C: Column> {
16
+ pub r: Vec<C>,
17
+ pub v: Vec<C>,
18
+ }
19
+
20
+ fn col_idx_with_same_low<C: Column>(col: &C, low_inverse: &HashMap<usize, usize>) -> Option<usize> {
21
+ let pivot = col.pivot()?;
22
+ low_inverse.get(&pivot).copied()
23
+ }
24
+
25
+ impl<C: Column> RVDecomposition<C> {
26
+ // Receives column, reduces it with left-to-right addition from R
27
+ // Adds reduction to self
28
+ pub fn reduce_column(&mut self, mut column: C, low_inverse: &mut HashMap<usize, usize>) {
29
+ // v_col tracks how the final reduced column is built up
30
+ // Currently column contains 1 lot of the latest column in D
31
+ let mut v_col = C::default();
32
+ v_col.add_entry(self.r.len());
33
+ // Reduce the column, keeping track of how we do this in V
34
+ while let Some(col_idx) = col_idx_with_same_low(&column, &low_inverse) {
35
+ column.add_col(&self.r[col_idx]);
36
+ v_col.add_col(&self.v[col_idx]);
37
+ }
38
+ // Update low inverse
39
+ let final_pivot = column.pivot();
40
+ if let Some(final_pivot) = final_pivot {
41
+ // This column has a lowest 1 and is being inserted at the end of R
42
+ low_inverse.insert(final_pivot, self.r.len());
43
+ }
44
+ // Push to decomposition
45
+ self.r.push(column);
46
+ self.v.push(v_col);
47
+ }
48
+
49
+ pub fn diagram(&self) -> PersistenceDiagram {
50
+ let mut diagram = PersistenceDiagram::default();
51
+ for (idx, col) in self.r.iter().enumerate() {
52
+ if let Some(lowest_idx) = col.pivot() {
53
+ // Negative column
54
+ diagram.unpaired.remove(&lowest_idx);
55
+ diagram.paired.push((lowest_idx, idx))
56
+ } else {
57
+ // Positive column
58
+ diagram.unpaired.insert(idx);
59
+ }
60
+ }
61
+ diagram
62
+ }
63
+ }
64
+
65
+ pub fn rv_decompose<C: Column>(matrix: impl Iterator<Item = C>) -> RVDecomposition<C> {
66
+ let mut low_inverse = HashMap::new();
67
+ matrix.fold(RVDecomposition::default(), |mut accum, next_col| {
68
+ accum.reduce_column(next_col, &mut low_inverse);
69
+ accum
70
+ })
71
+ }
@@ -0,0 +1,43 @@
1
+ use column::VecColumn;
2
+ use pyo3::prelude::*;
3
+ use pyo3::types::PyIterator;
4
+
5
+ mod column;
6
+ mod decomposition;
7
+ mod lock_free;
8
+
9
+ pub use column::Column;
10
+ pub use decomposition::{rv_decompose, PersistenceDiagram, RVDecomposition};
11
+ pub use lock_free::rv_decompose_lock_free;
12
+
13
+ #[pyfunction]
14
+ fn compute_pairings_serial(matrix: &PyIterator) -> PersistenceDiagram {
15
+ rv_decompose(matrix.map(|col| {
16
+ VecColumn {
17
+ internal: col
18
+ .and_then(PyAny::extract::<Vec<usize>>)
19
+ .expect("Column is a list of unsigned integers"),
20
+ }
21
+ }))
22
+ .diagram()
23
+ }
24
+
25
+ #[pyfunction]
26
+ fn compute_pairings(matrix: &PyIterator) -> PersistenceDiagram {
27
+ rv_decompose_lock_free(matrix.map(|col| {
28
+ VecColumn {
29
+ internal: col
30
+ .and_then(PyAny::extract::<Vec<usize>>)
31
+ .expect("Column is a list of unsigned integers"),
32
+ }
33
+ }))
34
+ .diagram()
35
+ }
36
+
37
+ /// A Python module implemented in Rust.
38
+ #[pymodule]
39
+ fn lophat(_py: Python, m: &PyModule) -> PyResult<()> {
40
+ m.add_function(wrap_pyfunction!(compute_pairings, m)?)?;
41
+ m.add_function(wrap_pyfunction!(compute_pairings_serial, m)?)?;
42
+ Ok(())
43
+ }
@@ -0,0 +1,98 @@
1
+ use std::fmt::Debug;
2
+
3
+ use crate::Column;
4
+ use crate::RVDecomposition;
5
+
6
+ use crossbeam::atomic::AtomicCell;
7
+ use pinboard::NonEmptyPinboard;
8
+ use rayon::prelude::*;
9
+
10
+ // Implements do while loop lines 6-9
11
+ fn get_col_with_pivot<C: Column>(
12
+ l: usize,
13
+ matrix: &Vec<NonEmptyPinboard<(C, C)>>,
14
+ pivots: &Vec<AtomicCell<Option<usize>>>,
15
+ ) -> Option<(usize, (C, C))> {
16
+ loop {
17
+ let piv = pivots[l].load();
18
+ if let Some(piv) = piv {
19
+ let cols = matrix[piv].read();
20
+ if cols.0.pivot() != Some(l) {
21
+ // Got a column but it now has the wrong pivot; loop again.
22
+ continue;
23
+ };
24
+ // Get column with correct pivot, return to caller.
25
+ return Some((piv, cols));
26
+ } else {
27
+ // There is not yet a column with this pivot, inform caller.
28
+ return None;
29
+ }
30
+ }
31
+ }
32
+
33
+ fn reduce_column<C: Column>(
34
+ j: usize,
35
+ matrix: &Vec<NonEmptyPinboard<(C, C)>>,
36
+ pivots: &Vec<AtomicCell<Option<usize>>>,
37
+ ) {
38
+ let mut working_j = j;
39
+ 'outer: loop {
40
+ let mut curr_column = matrix[working_j].read();
41
+ while let Some(l) = (&curr_column).0.pivot() {
42
+ let piv_with_column_opt = get_col_with_pivot(l, &matrix, &pivots);
43
+ if let Some((piv, piv_column)) = piv_with_column_opt {
44
+ // Lines 17-24
45
+ if piv < working_j {
46
+ curr_column.0.add_col(&piv_column.0);
47
+ curr_column.1.add_col(&piv_column.1);
48
+ } else if piv > working_j {
49
+ matrix[working_j].set(curr_column);
50
+ if pivots[l]
51
+ .compare_exchange(Some(piv), Some(working_j))
52
+ .is_ok()
53
+ {
54
+ working_j = piv;
55
+ }
56
+ continue 'outer;
57
+ } else {
58
+ panic!()
59
+ }
60
+ } else {
61
+ // piv = -1 case
62
+ matrix[working_j].set(curr_column);
63
+ if pivots[l].compare_exchange(None, Some(working_j)).is_ok() {
64
+ return;
65
+ } else {
66
+ continue 'outer;
67
+ }
68
+ }
69
+ }
70
+ // Lines 25-27 (curr_column = 0 clause)
71
+ if (&curr_column.0).pivot().is_none() {
72
+ matrix[working_j].set(curr_column);
73
+ return;
74
+ }
75
+ }
76
+ }
77
+
78
+ pub fn rv_decompose_lock_free<C: Column + Debug + 'static>(
79
+ matrix: impl Iterator<Item = C>,
80
+ ) -> RVDecomposition<C> {
81
+ let matrix: Vec<_> = matrix
82
+ .enumerate()
83
+ .map(|(idx, r_col)| {
84
+ let mut v_col = C::default();
85
+ v_col.add_entry(idx);
86
+ NonEmptyPinboard::new((r_col, v_col))
87
+ })
88
+ .collect();
89
+ let pivots: Vec<_> = (0..matrix.len()).map(|_| AtomicCell::new(None)).collect();
90
+ // Reduce matrix
91
+ // TODO: Can we advice rayon to split work in chunks?
92
+ (0..matrix.len())
93
+ .into_par_iter()
94
+ .for_each(|j| reduce_column(j, &matrix, &pivots));
95
+ // Wrap into RV decomposition
96
+ let (r, v) = matrix.into_iter().map(|pinboard| pinboard.read()).unzip();
97
+ RVDecomposition { r, v }
98
+ }