opfython 1.0.12__tar.gz → 2.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.
- opfython-2.0.0/PKG-INFO +128 -0
- opfython-2.0.0/README.md +97 -0
- {opfython-1.0.12 → opfython-2.0.0}/opfython/__init__.py +1 -2
- {opfython-1.0.12 → opfython-2.0.0}/opfython/core/__init__.py +2 -3
- opfython-2.0.0/opfython/core/heap.py +208 -0
- {opfython-1.0.12 → opfython-2.0.0}/opfython/core/node.py +19 -70
- opfython-2.0.0/opfython/core/opf.py +169 -0
- opfython-2.0.0/opfython/core/subgraph.py +167 -0
- opfython-2.0.0/opfython/math/__init__.py +1 -0
- {opfython-1.0.12 → opfython-2.0.0}/opfython/math/distance.py +1 -2
- {opfython-1.0.12 → opfython-2.0.0}/opfython/math/general.py +3 -34
- opfython-2.0.0/opfython/math/random.py +23 -0
- {opfython-1.0.12 → opfython-2.0.0}/opfython/models/__init__.py +3 -4
- {opfython-1.0.12 → opfython-2.0.0}/opfython/models/knn_supervised.py +17 -96
- {opfython-1.0.12 → opfython-2.0.0}/opfython/models/semi_supervised.py +11 -43
- {opfython-1.0.12 → opfython-2.0.0}/opfython/models/supervised.py +49 -162
- {opfython-1.0.12 → opfython-2.0.0}/opfython/models/unsupervised.py +22 -137
- opfython-2.0.0/opfython/stream/__init__.py +1 -0
- opfython-2.0.0/opfython/stream/loader.py +55 -0
- {opfython-1.0.12 → opfython-2.0.0}/opfython/stream/parser.py +2 -23
- opfython-2.0.0/opfython/stream/splitter.py +87 -0
- opfython-2.0.0/opfython/subgraphs/__init__.py +3 -0
- opfython-2.0.0/opfython/subgraphs/knn.py +227 -0
- opfython-2.0.0/opfython/utils/__init__.py +1 -0
- {opfython-1.0.12 → opfython-2.0.0}/opfython/utils/constants.py +1 -2
- opfython-2.0.0/opfython/utils/converter.py +66 -0
- {opfython-1.0.12 → opfython-2.0.0}/opfython/utils/decorator.py +1 -2
- opfython-2.0.0/opfython/utils/exception.py +48 -0
- {opfython-1.0.12 → opfython-2.0.0}/opfython/utils/logging.py +4 -35
- opfython-2.0.0/pyproject.toml +64 -0
- opfython-1.0.12/PKG-INFO +0 -200
- opfython-1.0.12/README.md +0 -174
- opfython-1.0.12/opfython/core/heap.py +0 -378
- opfython-1.0.12/opfython/core/opf.py +0 -288
- opfython-1.0.12/opfython/core/subgraph.py +0 -232
- opfython-1.0.12/opfython/math/__init__.py +0 -2
- opfython-1.0.12/opfython/math/random.py +0 -48
- opfython-1.0.12/opfython/stream/__init__.py +0 -2
- opfython-1.0.12/opfython/stream/loader.py +0 -112
- opfython-1.0.12/opfython/stream/splitter.py +0 -150
- opfython-1.0.12/opfython/subgraphs/__init__.py +0 -4
- opfython-1.0.12/opfython/subgraphs/knn.py +0 -317
- opfython-1.0.12/opfython/utils/__init__.py +0 -2
- opfython-1.0.12/opfython/utils/converter.py +0 -173
- opfython-1.0.12/opfython/utils/exception.py +0 -98
- opfython-1.0.12/opfython.egg-info/PKG-INFO +0 -200
- opfython-1.0.12/opfython.egg-info/SOURCES.txt +0 -37
- opfython-1.0.12/opfython.egg-info/dependency_links.txt +0 -1
- opfython-1.0.12/opfython.egg-info/requires.txt +0 -11
- opfython-1.0.12/opfython.egg-info/top_level.txt +0 -1
- opfython-1.0.12/pyproject.toml +0 -2
- opfython-1.0.12/setup.cfg +0 -14
- opfython-1.0.12/setup.py +0 -45
- {opfython-1.0.12 → opfython-2.0.0}/LICENSE +0 -0
opfython-2.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: opfython
|
|
3
|
+
Version: 2.0.0
|
|
4
|
+
Summary: Python-Inspired Optimum-Path Forest Classifier
|
|
5
|
+
Author-email: Gustavo Rosa <gustavo.rosa@unesp.br>
|
|
6
|
+
Requires-Python: >=3.11
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: Intended Audience :: Education
|
|
11
|
+
Classifier: Intended Audience :: Science/Research
|
|
12
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
18
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
19
|
+
License-File: LICENSE
|
|
20
|
+
Requires-Dist: numba>=0.67.0
|
|
21
|
+
Requires-Dist: numpy>=2.4.4
|
|
22
|
+
Requires-Dist: coverage>=7.10.0 ; extra == "tests"
|
|
23
|
+
Requires-Dist: flake8>=7.3.0 ; extra == "tests"
|
|
24
|
+
Requires-Dist: pytest>=9.0.0 ; extra == "tests"
|
|
25
|
+
Project-URL: Documentation, https://opfython.readthedocs.io
|
|
26
|
+
Project-URL: Homepage, https://github.com/gugarosa/opfython
|
|
27
|
+
Project-URL: Issues, https://github.com/gugarosa/opfython/issues
|
|
28
|
+
Provides-Extra: tests
|
|
29
|
+
Import-Name: opfython
|
|
30
|
+
|
|
31
|
+
# OPFython
|
|
32
|
+
|
|
33
|
+
[](https://pypi.org/project/opfython/)
|
|
34
|
+
[](https://github.com/gugarosa/opfython/actions/workflows/ci.yml)
|
|
35
|
+
[](https://opfython.readthedocs.io/)
|
|
36
|
+
[](https://doi.org/10.1016/j.simpa.2021.100113)
|
|
37
|
+
[](LICENSE)
|
|
38
|
+
|
|
39
|
+
OPFython is a Python implementation of the Optimum-Path Forest family of
|
|
40
|
+
classifiers. It provides supervised, semi-supervised, unsupervised, and
|
|
41
|
+
KNN-supervised models backed by NumPy and Numba.
|
|
42
|
+
|
|
43
|
+
This implementation follows [LibOPF](https://github.com/jppbsi/LibOPF).
|
|
44
|
+
Please cite the original LibOPF authors as well as OPFython when using it in
|
|
45
|
+
research.
|
|
46
|
+
|
|
47
|
+
OPFython requires Python 3.11 or newer.
|
|
48
|
+
|
|
49
|
+
## Installation
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
uv add opfython
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Quick start
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
import numpy as np
|
|
59
|
+
|
|
60
|
+
from opfython.models import SupervisedOPF
|
|
61
|
+
|
|
62
|
+
X_train = np.asarray([[0.0, 0.0], [0.1, 0.2], [1.0, 1.0], [1.1, 0.9]])
|
|
63
|
+
Y_train = np.asarray([0, 0, 1, 1])
|
|
64
|
+
X_test = np.asarray([[0.05, 0.1], [1.05, 1.0]])
|
|
65
|
+
|
|
66
|
+
classifier = SupervisedOPF()
|
|
67
|
+
classifier.fit(X_train, Y_train)
|
|
68
|
+
predictions = classifier.predict(X_test)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Labels must be zero-based and sequential. Pre-computed distance matrices can
|
|
72
|
+
be supplied through each classifier's `pre_computed_distance` constructor
|
|
73
|
+
argument.
|
|
74
|
+
|
|
75
|
+
## Classifiers
|
|
76
|
+
|
|
77
|
+
| Class | Purpose |
|
|
78
|
+
|---|---|
|
|
79
|
+
| `SupervisedOPF` | Complete-graph supervised classification |
|
|
80
|
+
| `KNNSupervisedOPF` | Supervised classification with learned KNN adjacency |
|
|
81
|
+
| `SemiSupervisedOPF` | Learning from labeled and unlabeled samples |
|
|
82
|
+
| `UnsupervisedOPF` | Density-based clustering and label propagation |
|
|
83
|
+
|
|
84
|
+
The package also includes 47 distance metrics, random generators, OPF
|
|
85
|
+
evaluation measures, dataset loaders and splitters, package logging and
|
|
86
|
+
exception helpers, and converters for LibOPF binary datasets.
|
|
87
|
+
|
|
88
|
+
See [the documentation](https://opfython.readthedocs.io/) and the
|
|
89
|
+
[`examples/applications`](examples/applications) directory for complete
|
|
90
|
+
workflows.
|
|
91
|
+
|
|
92
|
+
## Development
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
uv sync --all-groups
|
|
96
|
+
uv run pytest
|
|
97
|
+
uv run pre-commit run --all-files
|
|
98
|
+
uv run --group docs sphinx-build -W -b html docs docs/_build/html
|
|
99
|
+
uv build --no-sources
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Citation
|
|
103
|
+
|
|
104
|
+
```bibtex
|
|
105
|
+
@article{rosa2021simpa,
|
|
106
|
+
title = {OPFython: A Python implementation for Optimum-Path Forest},
|
|
107
|
+
author = {Gustavo H. {de Rosa} and Joao P. Papa},
|
|
108
|
+
journal = {Software Impacts},
|
|
109
|
+
pages = {100113},
|
|
110
|
+
year = {2021},
|
|
111
|
+
issn = {2665-9638},
|
|
112
|
+
doi = {https://doi.org/10.1016/j.simpa.2021.100113}
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
```bibtex
|
|
117
|
+
@misc{rosa2021speedup,
|
|
118
|
+
title = {Speeding Up OPFython with Numba},
|
|
119
|
+
author = {Gustavo H. de Rosa and Joao Paulo Papa},
|
|
120
|
+
year = {2021},
|
|
121
|
+
eprint = {2106.11828},
|
|
122
|
+
archivePrefix = {arXiv},
|
|
123
|
+
primaryClass = {cs.LG}
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
OPFython is licensed under the Apache License 2.0.
|
|
128
|
+
|
opfython-2.0.0/README.md
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# OPFython
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/opfython/)
|
|
4
|
+
[](https://github.com/gugarosa/opfython/actions/workflows/ci.yml)
|
|
5
|
+
[](https://opfython.readthedocs.io/)
|
|
6
|
+
[](https://doi.org/10.1016/j.simpa.2021.100113)
|
|
7
|
+
[](LICENSE)
|
|
8
|
+
|
|
9
|
+
OPFython is a Python implementation of the Optimum-Path Forest family of
|
|
10
|
+
classifiers. It provides supervised, semi-supervised, unsupervised, and
|
|
11
|
+
KNN-supervised models backed by NumPy and Numba.
|
|
12
|
+
|
|
13
|
+
This implementation follows [LibOPF](https://github.com/jppbsi/LibOPF).
|
|
14
|
+
Please cite the original LibOPF authors as well as OPFython when using it in
|
|
15
|
+
research.
|
|
16
|
+
|
|
17
|
+
OPFython requires Python 3.11 or newer.
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
uv add opfython
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Quick start
|
|
26
|
+
|
|
27
|
+
```python
|
|
28
|
+
import numpy as np
|
|
29
|
+
|
|
30
|
+
from opfython.models import SupervisedOPF
|
|
31
|
+
|
|
32
|
+
X_train = np.asarray([[0.0, 0.0], [0.1, 0.2], [1.0, 1.0], [1.1, 0.9]])
|
|
33
|
+
Y_train = np.asarray([0, 0, 1, 1])
|
|
34
|
+
X_test = np.asarray([[0.05, 0.1], [1.05, 1.0]])
|
|
35
|
+
|
|
36
|
+
classifier = SupervisedOPF()
|
|
37
|
+
classifier.fit(X_train, Y_train)
|
|
38
|
+
predictions = classifier.predict(X_test)
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Labels must be zero-based and sequential. Pre-computed distance matrices can
|
|
42
|
+
be supplied through each classifier's `pre_computed_distance` constructor
|
|
43
|
+
argument.
|
|
44
|
+
|
|
45
|
+
## Classifiers
|
|
46
|
+
|
|
47
|
+
| Class | Purpose |
|
|
48
|
+
|---|---|
|
|
49
|
+
| `SupervisedOPF` | Complete-graph supervised classification |
|
|
50
|
+
| `KNNSupervisedOPF` | Supervised classification with learned KNN adjacency |
|
|
51
|
+
| `SemiSupervisedOPF` | Learning from labeled and unlabeled samples |
|
|
52
|
+
| `UnsupervisedOPF` | Density-based clustering and label propagation |
|
|
53
|
+
|
|
54
|
+
The package also includes 47 distance metrics, random generators, OPF
|
|
55
|
+
evaluation measures, dataset loaders and splitters, package logging and
|
|
56
|
+
exception helpers, and converters for LibOPF binary datasets.
|
|
57
|
+
|
|
58
|
+
See [the documentation](https://opfython.readthedocs.io/) and the
|
|
59
|
+
[`examples/applications`](examples/applications) directory for complete
|
|
60
|
+
workflows.
|
|
61
|
+
|
|
62
|
+
## Development
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
uv sync --all-groups
|
|
66
|
+
uv run pytest
|
|
67
|
+
uv run pre-commit run --all-files
|
|
68
|
+
uv run --group docs sphinx-build -W -b html docs docs/_build/html
|
|
69
|
+
uv build --no-sources
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Citation
|
|
73
|
+
|
|
74
|
+
```bibtex
|
|
75
|
+
@article{rosa2021simpa,
|
|
76
|
+
title = {OPFython: A Python implementation for Optimum-Path Forest},
|
|
77
|
+
author = {Gustavo H. {de Rosa} and Joao P. Papa},
|
|
78
|
+
journal = {Software Impacts},
|
|
79
|
+
pages = {100113},
|
|
80
|
+
year = {2021},
|
|
81
|
+
issn = {2665-9638},
|
|
82
|
+
doi = {https://doi.org/10.1016/j.simpa.2021.100113}
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
```bibtex
|
|
87
|
+
@misc{rosa2021speedup,
|
|
88
|
+
title = {Speeding Up OPFython with Numba},
|
|
89
|
+
author = {Gustavo H. de Rosa and Joao Paulo Papa},
|
|
90
|
+
year = {2021},
|
|
91
|
+
eprint = {2106.11828},
|
|
92
|
+
archivePrefix = {arXiv},
|
|
93
|
+
primaryClass = {cs.LG}
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
OPFython is licensed under the Apache License 2.0.
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
"""A core package for all common opfython modules.
|
|
2
|
-
"""
|
|
1
|
+
"""A core package for all common opfython modules."""
|
|
3
2
|
|
|
4
3
|
from opfython.core.heap import Heap
|
|
5
4
|
from opfython.core.node import Node
|
|
6
|
-
from opfython.core.subgraph import Subgraph
|
|
7
5
|
from opfython.core.opf import OPF
|
|
6
|
+
from opfython.core.subgraph import Subgraph
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
"""Priority queue used by OPF algorithms."""
|
|
2
|
+
|
|
3
|
+
from typing import List
|
|
4
|
+
|
|
5
|
+
import opfython.utils.constants as c
|
|
6
|
+
import opfython.utils.exception as e
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Heap:
|
|
10
|
+
"""Fixed-size mutable min or max heap."""
|
|
11
|
+
|
|
12
|
+
def __init__(self, size: int = 1, policy: str = "min") -> None:
|
|
13
|
+
self.size = size
|
|
14
|
+
self.policy = policy
|
|
15
|
+
self.cost = [c.FLOAT_MAX] * size
|
|
16
|
+
self.color = [c.WHITE] * size
|
|
17
|
+
self.p = [-1] * size
|
|
18
|
+
self.pos = [-1] * size
|
|
19
|
+
self.last = -1
|
|
20
|
+
|
|
21
|
+
@property
|
|
22
|
+
def size(self) -> int:
|
|
23
|
+
"""Maximum heap size."""
|
|
24
|
+
|
|
25
|
+
return self._size
|
|
26
|
+
|
|
27
|
+
@size.setter
|
|
28
|
+
def size(self, size: int) -> None:
|
|
29
|
+
if not isinstance(size, int):
|
|
30
|
+
raise e.TypeError("`size` should be an integer")
|
|
31
|
+
if size < 1:
|
|
32
|
+
raise e.ValueError("`size` should be > 0")
|
|
33
|
+
self._size = size
|
|
34
|
+
|
|
35
|
+
@property
|
|
36
|
+
def policy(self) -> str:
|
|
37
|
+
"""Heap ordering policy."""
|
|
38
|
+
|
|
39
|
+
return self._policy
|
|
40
|
+
|
|
41
|
+
@policy.setter
|
|
42
|
+
def policy(self, policy: str) -> None:
|
|
43
|
+
if policy not in ["min", "max"]:
|
|
44
|
+
raise e.ValueError("`policy` should be `min` or `max`")
|
|
45
|
+
self._policy = policy
|
|
46
|
+
|
|
47
|
+
@property
|
|
48
|
+
def cost(self) -> List[float]:
|
|
49
|
+
"""Node costs."""
|
|
50
|
+
|
|
51
|
+
return self._cost
|
|
52
|
+
|
|
53
|
+
@cost.setter
|
|
54
|
+
def cost(self, cost: List[float]) -> None:
|
|
55
|
+
if not isinstance(cost, list):
|
|
56
|
+
raise e.TypeError("`cost` should be a list")
|
|
57
|
+
self._cost = cost
|
|
58
|
+
|
|
59
|
+
@property
|
|
60
|
+
def color(self) -> List[int]:
|
|
61
|
+
"""Node colors."""
|
|
62
|
+
|
|
63
|
+
return self._color
|
|
64
|
+
|
|
65
|
+
@color.setter
|
|
66
|
+
def color(self, color: List[int]) -> None:
|
|
67
|
+
if not isinstance(color, list):
|
|
68
|
+
raise e.TypeError("`color` should be a list")
|
|
69
|
+
self._color = color
|
|
70
|
+
|
|
71
|
+
@property
|
|
72
|
+
def p(self) -> List[int]:
|
|
73
|
+
"""Heap-position to node mapping."""
|
|
74
|
+
|
|
75
|
+
return self._p
|
|
76
|
+
|
|
77
|
+
@p.setter
|
|
78
|
+
def p(self, p: List[int]) -> None:
|
|
79
|
+
if not isinstance(p, list):
|
|
80
|
+
raise e.TypeError("`p` should be a list")
|
|
81
|
+
self._p = p
|
|
82
|
+
|
|
83
|
+
@property
|
|
84
|
+
def pos(self) -> List[int]:
|
|
85
|
+
"""Node to heap-position mapping."""
|
|
86
|
+
|
|
87
|
+
return self._pos
|
|
88
|
+
|
|
89
|
+
@pos.setter
|
|
90
|
+
def pos(self, pos: List[int]) -> None:
|
|
91
|
+
if not isinstance(pos, list):
|
|
92
|
+
raise e.TypeError("`pos` should be a list")
|
|
93
|
+
self._pos = pos
|
|
94
|
+
|
|
95
|
+
@property
|
|
96
|
+
def last(self) -> int:
|
|
97
|
+
"""Last occupied heap position."""
|
|
98
|
+
|
|
99
|
+
return self._last
|
|
100
|
+
|
|
101
|
+
@last.setter
|
|
102
|
+
def last(self, last: int) -> None:
|
|
103
|
+
if not isinstance(last, int):
|
|
104
|
+
raise e.TypeError("`last` should be an integer")
|
|
105
|
+
if last < -1:
|
|
106
|
+
raise e.ValueError("`last` should be > -1")
|
|
107
|
+
self._last = last
|
|
108
|
+
|
|
109
|
+
def is_full(self) -> bool:
|
|
110
|
+
"""Return whether every heap slot is occupied."""
|
|
111
|
+
|
|
112
|
+
return self.last == self.size - 1
|
|
113
|
+
|
|
114
|
+
def is_empty(self) -> bool:
|
|
115
|
+
"""Return whether the heap has no nodes."""
|
|
116
|
+
|
|
117
|
+
return self.last == -1
|
|
118
|
+
|
|
119
|
+
def dad(self, i: int) -> int:
|
|
120
|
+
"""Return a node's parent position."""
|
|
121
|
+
|
|
122
|
+
return int((i - 1) / 2)
|
|
123
|
+
|
|
124
|
+
def left_son(self, i: int) -> int:
|
|
125
|
+
"""Return a node's left-child position."""
|
|
126
|
+
|
|
127
|
+
return 2 * i + 1
|
|
128
|
+
|
|
129
|
+
def right_son(self, i: int) -> int:
|
|
130
|
+
"""Return a node's right-child position."""
|
|
131
|
+
|
|
132
|
+
return 2 * i + 2
|
|
133
|
+
|
|
134
|
+
def _precedes(self, left: int, right: int) -> bool:
|
|
135
|
+
if self.policy == "min":
|
|
136
|
+
return self.cost[left] < self.cost[right]
|
|
137
|
+
return self.cost[left] > self.cost[right]
|
|
138
|
+
|
|
139
|
+
def _swap(self, left: int, right: int) -> None:
|
|
140
|
+
self.p[left], self.p[right] = self.p[right], self.p[left]
|
|
141
|
+
self.pos[self.p[left]] = left
|
|
142
|
+
self.pos[self.p[right]] = right
|
|
143
|
+
|
|
144
|
+
def go_up(self, i: int) -> None:
|
|
145
|
+
"""Move a node toward the heap root."""
|
|
146
|
+
|
|
147
|
+
parent = self.dad(i)
|
|
148
|
+
while i > 0 and self._precedes(self.p[i], self.p[parent]):
|
|
149
|
+
self._swap(i, parent)
|
|
150
|
+
i = parent
|
|
151
|
+
parent = self.dad(i)
|
|
152
|
+
|
|
153
|
+
def go_down(self, i: int) -> None:
|
|
154
|
+
"""Move a node toward the heap leaves."""
|
|
155
|
+
|
|
156
|
+
left = self.left_son(i)
|
|
157
|
+
right = self.right_son(i)
|
|
158
|
+
target = i
|
|
159
|
+
|
|
160
|
+
if left <= self.last and self._precedes(self.p[left], self.p[target]):
|
|
161
|
+
target = left
|
|
162
|
+
if right <= self.last and self._precedes(self.p[right], self.p[target]):
|
|
163
|
+
target = right
|
|
164
|
+
|
|
165
|
+
if target != i:
|
|
166
|
+
self._swap(i, target)
|
|
167
|
+
self.go_down(target)
|
|
168
|
+
|
|
169
|
+
def insert(self, p: int) -> bool:
|
|
170
|
+
"""Insert a node if capacity is available."""
|
|
171
|
+
|
|
172
|
+
if self.is_full():
|
|
173
|
+
return False
|
|
174
|
+
|
|
175
|
+
self.last += 1
|
|
176
|
+
self.p[self.last] = p
|
|
177
|
+
self.color[p] = c.GRAY
|
|
178
|
+
self.pos[p] = self.last
|
|
179
|
+
self.go_up(self.last)
|
|
180
|
+
return True
|
|
181
|
+
|
|
182
|
+
def remove(self) -> int:
|
|
183
|
+
"""Remove and return the next node, or ``False`` when empty."""
|
|
184
|
+
|
|
185
|
+
if self.is_empty():
|
|
186
|
+
return False
|
|
187
|
+
|
|
188
|
+
node = self.p[0]
|
|
189
|
+
self.pos[node] = -1
|
|
190
|
+
self.color[node] = c.BLACK
|
|
191
|
+
self.p[0] = self.p[self.last]
|
|
192
|
+
self.pos[self.p[0]] = 0
|
|
193
|
+
self.p[self.last] = -1
|
|
194
|
+
self.last -= 1
|
|
195
|
+
self.go_down(0)
|
|
196
|
+
return node
|
|
197
|
+
|
|
198
|
+
def update(self, p: int, cost: float) -> None:
|
|
199
|
+
"""Update a node's cost, inserting it when unseen."""
|
|
200
|
+
|
|
201
|
+
self.cost[p] = cost
|
|
202
|
+
if self.color[p] == c.BLACK:
|
|
203
|
+
pass
|
|
204
|
+
|
|
205
|
+
if self.color[p] == c.WHITE:
|
|
206
|
+
self.insert(p)
|
|
207
|
+
else:
|
|
208
|
+
self.go_up(self.pos[p])
|