lapcpp 1.0.0__cp313-cp313-win_arm64.whl
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.
- lapcpp/__init__.py +21 -0
- lapcpp/__init__.pyi +3 -0
- lapcpp/_lapjv.cp313-win_arm64.pyd +0 -0
- lapcpp/_lapjv.pyi +214 -0
- lapcpp/py.typed +3 -0
- lapcpp-1.0.0.dist-info/DELVEWHEEL +2 -0
- lapcpp-1.0.0.dist-info/METADATA +206 -0
- lapcpp-1.0.0.dist-info/RECORD +15 -0
- lapcpp-1.0.0.dist-info/WHEEL +5 -0
- lapcpp-1.0.0.dist-info/licenses/LICENSE +21 -0
- lapcpp-1.0.0.dist-info/licenses/NOTICE +10 -0
- lapcpp-1.0.0.dist-info/licenses/src/_lapjv/LICENSE +22 -0
- lapcpp-1.0.0.dist-info/licenses/tests/LICENSE +25 -0
- lapcpp-1.0.0.dist-info/top_level.txt +1 -0
- lapcpp.libs/msvcp140-167ceac85c2d726c4cd9e39b8881fafd.dll +0 -0
lapcpp/__init__.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""Native C++ linear assignment solvers with implicit rejection and batch solving."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
# start delvewheel patch
|
|
5
|
+
def _delvewheel_patch_1_13_2():
|
|
6
|
+
import os
|
|
7
|
+
if os.path.isdir(libs_dir := os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir, 'lapcpp.libs'))):
|
|
8
|
+
os.add_dll_directory(libs_dir)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
_delvewheel_patch_1_13_2()
|
|
12
|
+
del _delvewheel_patch_1_13_2
|
|
13
|
+
# end delvewheel patch
|
|
14
|
+
|
|
15
|
+
from ._lapjv import lapjv, lapjv_batch
|
|
16
|
+
|
|
17
|
+
__version__ = "1.0.0"
|
|
18
|
+
__author__ = 'Ratha SIV'
|
|
19
|
+
__description__ = 'Native C++ linear assignment solvers with implicit rejection and batch solving.'
|
|
20
|
+
__homepage__ = 'https://github.com/rathaROG/lapcpp'
|
|
21
|
+
__all__ = ["lapjv", "lapjv_batch"]
|
lapcpp/__init__.pyi
ADDED
|
Binary file
|
lapcpp/_lapjv.pyi
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
# Copyright (c) 2026 Ratha SIV | MIT License
|
|
2
|
+
|
|
3
|
+
"""Static signatures and documentation for the lapcpp entry points."""
|
|
4
|
+
|
|
5
|
+
from typing import Any, List, Optional, Tuple, Union, overload
|
|
6
|
+
from typing import Literal, Protocol
|
|
7
|
+
import numpy as np
|
|
8
|
+
|
|
9
|
+
_Precision = Literal["native", "float32", "float64"]
|
|
10
|
+
_Output = Literal["indices", "pairs"]
|
|
11
|
+
|
|
12
|
+
class _SingleSolver(Protocol):
|
|
13
|
+
@overload
|
|
14
|
+
def __call__(
|
|
15
|
+
self, cost: Any,
|
|
16
|
+
extend_cost: bool = False,
|
|
17
|
+
cost_limit: float = ...,
|
|
18
|
+
return_cost: Literal[True] = True,
|
|
19
|
+
*,
|
|
20
|
+
precision: _Precision = "native",
|
|
21
|
+
output: Literal["indices"] = "indices",
|
|
22
|
+
) -> Tuple[float, np.ndarray, np.ndarray]: ...
|
|
23
|
+
|
|
24
|
+
@overload
|
|
25
|
+
def __call__(
|
|
26
|
+
self, cost: Any,
|
|
27
|
+
extend_cost: bool,
|
|
28
|
+
cost_limit: float,
|
|
29
|
+
return_cost: Literal[False],
|
|
30
|
+
*,
|
|
31
|
+
precision: _Precision = "native",
|
|
32
|
+
output: Literal["indices"] = "indices",
|
|
33
|
+
) -> Tuple[np.ndarray, np.ndarray]: ...
|
|
34
|
+
|
|
35
|
+
@overload
|
|
36
|
+
def __call__(
|
|
37
|
+
self, cost: Any,
|
|
38
|
+
extend_cost: bool = False,
|
|
39
|
+
cost_limit: float = ...,
|
|
40
|
+
*,
|
|
41
|
+
return_cost: Literal[False],
|
|
42
|
+
precision: _Precision = "native",
|
|
43
|
+
output: Literal["indices"] = "indices",
|
|
44
|
+
) -> Tuple[np.ndarray, np.ndarray]: ...
|
|
45
|
+
|
|
46
|
+
@overload
|
|
47
|
+
def __call__(
|
|
48
|
+
self, cost: Any,
|
|
49
|
+
extend_cost: bool = False,
|
|
50
|
+
cost_limit: float = ...,
|
|
51
|
+
return_cost: bool = True,
|
|
52
|
+
*,
|
|
53
|
+
precision: _Precision = "native",
|
|
54
|
+
output: Literal["indices"] = "indices",
|
|
55
|
+
) -> Union[Tuple[float, np.ndarray, np.ndarray], Tuple[np.ndarray, np.ndarray]]: ...
|
|
56
|
+
|
|
57
|
+
@overload
|
|
58
|
+
def __call__(
|
|
59
|
+
self, cost: Any,
|
|
60
|
+
extend_cost: bool = False,
|
|
61
|
+
cost_limit: float = ...,
|
|
62
|
+
return_cost: Literal[True] = True,
|
|
63
|
+
*,
|
|
64
|
+
precision: _Precision = "native",
|
|
65
|
+
output: Literal["pairs"],
|
|
66
|
+
) -> Tuple[float, np.ndarray]: ...
|
|
67
|
+
|
|
68
|
+
@overload
|
|
69
|
+
def __call__(
|
|
70
|
+
self, cost: Any,
|
|
71
|
+
extend_cost: bool,
|
|
72
|
+
cost_limit: float,
|
|
73
|
+
return_cost: Literal[False],
|
|
74
|
+
*,
|
|
75
|
+
precision: _Precision = "native",
|
|
76
|
+
output: Literal["pairs"],
|
|
77
|
+
) -> np.ndarray: ...
|
|
78
|
+
|
|
79
|
+
@overload
|
|
80
|
+
def __call__(
|
|
81
|
+
self, cost: Any,
|
|
82
|
+
extend_cost: bool = False,
|
|
83
|
+
cost_limit: float = ...,
|
|
84
|
+
*,
|
|
85
|
+
return_cost: Literal[False],
|
|
86
|
+
precision: _Precision = "native",
|
|
87
|
+
output: Literal["pairs"],
|
|
88
|
+
) -> np.ndarray: ...
|
|
89
|
+
|
|
90
|
+
@overload
|
|
91
|
+
def __call__(
|
|
92
|
+
self, cost: Any,
|
|
93
|
+
extend_cost: bool = False,
|
|
94
|
+
cost_limit: float = ...,
|
|
95
|
+
return_cost: bool = True,
|
|
96
|
+
*,
|
|
97
|
+
precision: _Precision = "native",
|
|
98
|
+
output: Literal["pairs"],
|
|
99
|
+
) -> Union[Tuple[float, np.ndarray], np.ndarray]: ...
|
|
100
|
+
|
|
101
|
+
@overload
|
|
102
|
+
def __call__(
|
|
103
|
+
self, cost: Any, extend_cost: bool = False,
|
|
104
|
+
cost_limit: float = ..., return_cost: bool = True,
|
|
105
|
+
*, precision: _Precision = "native", output: _Output = "indices",
|
|
106
|
+
) -> Union[Tuple[float, np.ndarray, np.ndarray], Tuple[np.ndarray, np.ndarray], Tuple[float, np.ndarray], np.ndarray]: ...
|
|
107
|
+
|
|
108
|
+
class _BatchSolver(Protocol):
|
|
109
|
+
@overload
|
|
110
|
+
def __call__(
|
|
111
|
+
self, costs: Any,
|
|
112
|
+
extend_cost: bool = False,
|
|
113
|
+
cost_limit: float = ...,
|
|
114
|
+
return_cost: Literal[True] = True,
|
|
115
|
+
n_threads: Optional[int] = 0,
|
|
116
|
+
*,
|
|
117
|
+
precision: _Precision = "native",
|
|
118
|
+
output: Literal["indices"] = "indices",
|
|
119
|
+
) -> Tuple[np.ndarray, List[np.ndarray], List[np.ndarray]]: ...
|
|
120
|
+
|
|
121
|
+
@overload
|
|
122
|
+
def __call__(
|
|
123
|
+
self, costs: Any,
|
|
124
|
+
extend_cost: bool,
|
|
125
|
+
cost_limit: float,
|
|
126
|
+
return_cost: Literal[False],
|
|
127
|
+
n_threads: Optional[int] = 0,
|
|
128
|
+
*,
|
|
129
|
+
precision: _Precision = "native",
|
|
130
|
+
output: Literal["indices"] = "indices",
|
|
131
|
+
) -> Tuple[List[np.ndarray], List[np.ndarray]]: ...
|
|
132
|
+
|
|
133
|
+
@overload
|
|
134
|
+
def __call__(
|
|
135
|
+
self, costs: Any,
|
|
136
|
+
extend_cost: bool = False,
|
|
137
|
+
cost_limit: float = ...,
|
|
138
|
+
*,
|
|
139
|
+
return_cost: Literal[False],
|
|
140
|
+
n_threads: Optional[int] = 0,
|
|
141
|
+
precision: _Precision = "native",
|
|
142
|
+
output: Literal["indices"] = "indices",
|
|
143
|
+
) -> Tuple[List[np.ndarray], List[np.ndarray]]: ...
|
|
144
|
+
|
|
145
|
+
@overload
|
|
146
|
+
def __call__(
|
|
147
|
+
self, costs: Any,
|
|
148
|
+
extend_cost: bool = False,
|
|
149
|
+
cost_limit: float = ...,
|
|
150
|
+
return_cost: bool = True,
|
|
151
|
+
n_threads: Optional[int] = 0,
|
|
152
|
+
*,
|
|
153
|
+
precision: _Precision = "native",
|
|
154
|
+
output: Literal["indices"] = "indices",
|
|
155
|
+
) -> Union[Tuple[np.ndarray, List[np.ndarray], List[np.ndarray]], Tuple[List[np.ndarray], List[np.ndarray]]]: ...
|
|
156
|
+
|
|
157
|
+
@overload
|
|
158
|
+
def __call__(
|
|
159
|
+
self, costs: Any,
|
|
160
|
+
extend_cost: bool = False,
|
|
161
|
+
cost_limit: float = ...,
|
|
162
|
+
return_cost: Literal[True] = True,
|
|
163
|
+
n_threads: Optional[int] = 0,
|
|
164
|
+
*,
|
|
165
|
+
precision: _Precision = "native",
|
|
166
|
+
output: Literal["pairs"],
|
|
167
|
+
) -> Tuple[np.ndarray, List[np.ndarray]]: ...
|
|
168
|
+
|
|
169
|
+
@overload
|
|
170
|
+
def __call__(
|
|
171
|
+
self, costs: Any,
|
|
172
|
+
extend_cost: bool,
|
|
173
|
+
cost_limit: float,
|
|
174
|
+
return_cost: Literal[False],
|
|
175
|
+
n_threads: Optional[int] = 0,
|
|
176
|
+
*,
|
|
177
|
+
precision: _Precision = "native",
|
|
178
|
+
output: Literal["pairs"],
|
|
179
|
+
) -> List[np.ndarray]: ...
|
|
180
|
+
|
|
181
|
+
@overload
|
|
182
|
+
def __call__(
|
|
183
|
+
self, costs: Any,
|
|
184
|
+
extend_cost: bool = False,
|
|
185
|
+
cost_limit: float = ...,
|
|
186
|
+
*,
|
|
187
|
+
return_cost: Literal[False],
|
|
188
|
+
n_threads: Optional[int] = 0,
|
|
189
|
+
precision: _Precision = "native",
|
|
190
|
+
output: Literal["pairs"],
|
|
191
|
+
) -> List[np.ndarray]: ...
|
|
192
|
+
|
|
193
|
+
@overload
|
|
194
|
+
def __call__(
|
|
195
|
+
self, costs: Any,
|
|
196
|
+
extend_cost: bool = False,
|
|
197
|
+
cost_limit: float = ...,
|
|
198
|
+
return_cost: bool = True,
|
|
199
|
+
n_threads: Optional[int] = 0,
|
|
200
|
+
*,
|
|
201
|
+
precision: _Precision = "native",
|
|
202
|
+
output: Literal["pairs"],
|
|
203
|
+
) -> Union[Tuple[np.ndarray, List[np.ndarray]], List[np.ndarray]]: ...
|
|
204
|
+
|
|
205
|
+
@overload
|
|
206
|
+
def __call__(
|
|
207
|
+
self, costs: Any, extend_cost: bool = False,
|
|
208
|
+
cost_limit: float = ..., return_cost: bool = True,
|
|
209
|
+
n_threads: Optional[int] = 0,
|
|
210
|
+
*, precision: _Precision = "native", output: _Output = "indices",
|
|
211
|
+
) -> Union[Tuple[np.ndarray, List[np.ndarray], List[np.ndarray]], Tuple[List[np.ndarray], List[np.ndarray]], Tuple[np.ndarray, List[np.ndarray]], List[np.ndarray]]: ...
|
|
212
|
+
|
|
213
|
+
lapjv: _SingleSolver
|
|
214
|
+
lapjv_batch: _BatchSolver
|
lapcpp/py.typed
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
Version: 1.13.2
|
|
2
|
+
Arguments: ['C:\\Users\\runneradmin\\AppData\\Local\\Temp\\cibw-run-fzg4ohkk\\cp313-win_arm64\\build\\venv\\Scripts\\delvewheel', 'repair', '--add-path', 'C:\\Program Files\\Microsoft Visual Studio\\18\\Enterprise\\VC\\Redist\\MSVC\\14.51.36231\\arm64\\Microsoft.VC145.CRT', '-w', 'C:\\Users\\runneradmin\\AppData\\Local\\Temp\\cibw-run-fzg4ohkk\\cp313-win_arm64\\repaired_wheel', '-v', 'C:\\Users\\runneradmin\\AppData\\Local\\Temp\\cibw-run-fzg4ohkk\\cp313-win_arm64\\built_wheel\\lapcpp-1.0.0-cp313-cp313-win_arm64.whl']
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: lapcpp
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Native C++ linear assignment solvers with implicit rejection and batch solving
|
|
5
|
+
Home-page: https://github.com/rathaROG/lapcpp
|
|
6
|
+
Author: Ratha SIV
|
|
7
|
+
Maintainer: rathaROG
|
|
8
|
+
License: MIT
|
|
9
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
10
|
+
Classifier: Environment :: Console
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Intended Audience :: Education
|
|
13
|
+
Classifier: Intended Audience :: Science/Research
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
22
|
+
Classifier: Topic :: Education
|
|
23
|
+
Classifier: Topic :: Education :: Testing
|
|
24
|
+
Classifier: Topic :: Scientific/Engineering
|
|
25
|
+
Classifier: Topic :: Scientific/Engineering :: Mathematics
|
|
26
|
+
Classifier: Topic :: Software Development
|
|
27
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
28
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
29
|
+
Classifier: Operating System :: POSIX
|
|
30
|
+
Classifier: Operating System :: Unix
|
|
31
|
+
Classifier: Operating System :: MacOS
|
|
32
|
+
Requires-Python: >=3.9
|
|
33
|
+
Description-Content-Type: text/markdown
|
|
34
|
+
License-File: LICENSE
|
|
35
|
+
License-File: NOTICE
|
|
36
|
+
License-File: tests/LICENSE
|
|
37
|
+
License-File: src/_lapjv/LICENSE
|
|
38
|
+
Requires-Dist: numpy>=1.21.6
|
|
39
|
+
Provides-Extra: test
|
|
40
|
+
Requires-Dist: pytest>=7; extra == "test"
|
|
41
|
+
Requires-Dist: scipy>=1.9; extra == "test"
|
|
42
|
+
Requires-Dist: pytest-timeout; extra == "test"
|
|
43
|
+
Provides-Extra: benchmark
|
|
44
|
+
Requires-Dist: scipy>=1.9; extra == "benchmark"
|
|
45
|
+
Requires-Dist: lapx==0.11.1; extra == "benchmark"
|
|
46
|
+
Provides-Extra: dev
|
|
47
|
+
Requires-Dist: build; extra == "dev"
|
|
48
|
+
Requires-Dist: setuptools>=77; extra == "dev"
|
|
49
|
+
Requires-Dist: wheel; extra == "dev"
|
|
50
|
+
Requires-Dist: pytest>=7; extra == "dev"
|
|
51
|
+
Requires-Dist: scipy>=1.9; extra == "dev"
|
|
52
|
+
Requires-Dist: pytest-timeout; extra == "dev"
|
|
53
|
+
Requires-Dist: pyright; extra == "dev"
|
|
54
|
+
Dynamic: author
|
|
55
|
+
Dynamic: classifier
|
|
56
|
+
Dynamic: description
|
|
57
|
+
Dynamic: description-content-type
|
|
58
|
+
Dynamic: home-page
|
|
59
|
+
Dynamic: license
|
|
60
|
+
Dynamic: license-file
|
|
61
|
+
Dynamic: maintainer
|
|
62
|
+
Dynamic: provides-extra
|
|
63
|
+
Dynamic: requires-dist
|
|
64
|
+
Dynamic: requires-python
|
|
65
|
+
Dynamic: summary
|
|
66
|
+
|
|
67
|
+
# lapcpp
|
|
68
|
+
|
|
69
|
+
**lapcpp** solves dense linear assignment problems with a native
|
|
70
|
+
C++ implementation and a NumPy API. Python **3.9 or newer** is required.
|
|
71
|
+
It exposes two solvers: `lapjv` and `lapjv_batch`.
|
|
72
|
+
|
|
73
|
+
lapcpp 1.0.0 derives from the `lapjvh` solver in LAPX 0.11.1.
|
|
74
|
+
Future solver development is led by lapcpp for C++ and laprust for Rust;
|
|
75
|
+
LAPX will incorporate their fixes and improvements.
|
|
76
|
+
|
|
77
|
+
It solves rectangular problems directly and handles unmatched rows and columns
|
|
78
|
+
implicitly when a finite `cost_limit` is supplied, without allocating an expanded
|
|
79
|
+
square cost matrix.
|
|
80
|
+
The square path uses Jonker–Volgenant initialization, reduction, and shortest
|
|
81
|
+
augmenting paths. This package is independent of LAPX and imports as `lapcpp`.
|
|
82
|
+
|
|
83
|
+
## Installation
|
|
84
|
+
|
|
85
|
+
Once available on PyPI:
|
|
86
|
+
|
|
87
|
+
```sh
|
|
88
|
+
python -m pip install lapcpp
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
A source build requires a C++17 compiler and, for `laprust`, Rust/Cargo 1.83+
|
|
92
|
+
with a compatible target. Build dependencies (NumPy, Cython, setuptools) are
|
|
93
|
+
installed automatically by pip. NumPy is the only Python runtime dependency.
|
|
94
|
+
An installed wheel needs no compiler or Rust toolchain.
|
|
95
|
+
|
|
96
|
+
## Quick start
|
|
97
|
+
|
|
98
|
+
```python
|
|
99
|
+
import numpy as np
|
|
100
|
+
import lapcpp
|
|
101
|
+
|
|
102
|
+
cost = np.array([[3., 1.], [1., 4.]])
|
|
103
|
+
total, rows, cols = lapcpp.lapjv(cost)
|
|
104
|
+
# total = 2.0; rows = [0, 1]; cols = [1, 0]
|
|
105
|
+
|
|
106
|
+
costs = np.stack([cost, cost]) # (batch, rows, columns)
|
|
107
|
+
totals, rows, cols = lapcpp.lapjv_batch(costs, n_threads=2)
|
|
108
|
+
# totals = [2., 2.]; rows and cols are lists of arrays, one per problem
|
|
109
|
+
|
|
110
|
+
rectangular = np.array([[.1, .8, .9], [.9, .7, .8]], dtype=np.float32)
|
|
111
|
+
total, rows, cols = lapcpp.lapjv(rectangular, cost_limit=.5)
|
|
112
|
+
# Only (0, 0) is selected; total is about .1, excluding rejection penalties.
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## API
|
|
116
|
+
|
|
117
|
+
```python
|
|
118
|
+
lapjv(cost, extend_cost=False, cost_limit=np.inf, return_cost=True,
|
|
119
|
+
*, precision="native", output="indices")
|
|
120
|
+
lapjv_batch(costs, extend_cost=False, cost_limit=np.inf, return_cost=True,
|
|
121
|
+
n_threads=0, *, precision="native", output="indices")
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
| Option | Behavior |
|
|
125
|
+
| --- | --- |
|
|
126
|
+
| `cost` / `costs` | Real numeric arrays with shape `(N, M)` / `(B, N, M)`. |
|
|
127
|
+
| `extend_cost=True` | Allows rectangular inputs when the limit is infinite; no square padding is used. |
|
|
128
|
+
| `cost_limit` | A finite value permits unmatched endpoints at a penalty of `cost_limit / 2` each. Finite limits also permit rectangles and apply to square inputs. |
|
|
129
|
+
| `return_cost` | Defaults to `True`; totals sum selected original costs in float64 and exclude rejection penalties. `False` omits totals and skips the sum. |
|
|
130
|
+
| `precision` | `"native"` preserves float32/float64; other real numeric inputs use float64. Explicit `"float32"` / `"float64"` select working costs. |
|
|
131
|
+
| `output` | `"indices"` returns aligned row/column arrays; `"pairs"` returns `(K, 2)` arrays. |
|
|
132
|
+
| `n_threads` | Batch only. `0` / `None` selects automatically; negative values use one worker; positive values are capped by batch size. |
|
|
133
|
+
|
|
134
|
+
Rows are sorted and all returned indices are int64. They identify the selected
|
|
135
|
+
pairs; unmatched items are absent. Batch results contain lists because each
|
|
136
|
+
problem may select a different number of pairs. Equal-cost optima may return
|
|
137
|
+
different assignments across solvers or precisions.
|
|
138
|
+
|
|
139
|
+
With a finite limit `L`, the optimized objective is
|
|
140
|
+
`sum(selected costs) + (N + M - 2*K) * L/2`.
|
|
141
|
+
The reported `total` contains only the first term. Joint rejection is different
|
|
142
|
+
from solving a full assignment and then filtering high-cost pairs.
|
|
143
|
+
See [API details](docs/api.md) for numerical and memory behavior.
|
|
144
|
+
|
|
145
|
+
## Development and checks
|
|
146
|
+
|
|
147
|
+
```sh
|
|
148
|
+
python -m pip install -e ".[dev]"
|
|
149
|
+
cd tests
|
|
150
|
+
python -m pytest -q -m "not cross_package"
|
|
151
|
+
cd ..
|
|
152
|
+
python -m pytest -q .github/tests -m "not editable"
|
|
153
|
+
python -m build
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
The test suite covers SciPy and exhaustive-reference comparisons, rectangular
|
|
157
|
+
and finite-limit cases, float32/float64, precision conversion, forbidden edges,
|
|
158
|
+
empty inputs, invalid arguments, repeated workspaces, batch thread counts,
|
|
159
|
+
concurrent calls, package independence, and type annotations.
|
|
160
|
+
Historical LAPX regressions, cross-package concurrency, and isolated editable
|
|
161
|
+
installs are also covered. See the [solver test inventory](tests/README.md),
|
|
162
|
+
[build and packaging checks](.github/tests/README.md), and
|
|
163
|
+
[CONTRIBUTING.md](CONTRIBUTING.md) for commands and coverage.
|
|
164
|
+
|
|
165
|
+
## Benchmarks
|
|
166
|
+
|
|
167
|
+
All three benchmark scripts compare **SciPy (baseline), lapcpp, laprust,
|
|
168
|
+
LAPX lapjvxa and LAPX lapjvsa**. The LAPX candidates represent the JVX and JVS
|
|
169
|
+
families using pair outputs, including their batch APIs. Once lapcpp and laprust
|
|
170
|
+
are available on PyPI, install the dependencies below. Clone or
|
|
171
|
+
download this repository for the scripts and run them from the repository root.
|
|
172
|
+
|
|
173
|
+
```sh
|
|
174
|
+
python -m pip install --upgrade lapcpp laprust "lapx==0.11.1" scipy
|
|
175
|
+
python benchmarks/benchmark_single.py --quick --repeats 3
|
|
176
|
+
python benchmarks/benchmark_batch.py --quick --threads 4 --repeats 3
|
|
177
|
+
python benchmarks/benchmark_tracking.py --quick --repeats 3
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Use `--json benchmark-results/run.json` to save measurements and environment
|
|
181
|
+
details. Each solver is warmed right before its own sample, and calls repeat
|
|
182
|
+
until a sample lasts at least 10 ms; `--reference-timing` restores the pinned
|
|
183
|
+
LAPX timing. See [benchmark methodology](benchmarks/README.md) for full workloads,
|
|
184
|
+
reference LAPX settings, dedicated solver functions, tracking post-filtering, and
|
|
185
|
+
timing boundaries. Tracking never passes its threshold as solver `cost_limit`.
|
|
186
|
+
|
|
187
|
+
## Paper
|
|
188
|
+
|
|
189
|
+
The solver, its implicit rejection, and its performance against SciPy and other
|
|
190
|
+
Jonker–Volgenant implementations are described in *Native Rectangular Linear
|
|
191
|
+
Assignment with Implicit Rejection: A Jonker–Volgenant Solver without Square
|
|
192
|
+
Expansion*, where lapcpp is labeled JVCPP. Read the
|
|
193
|
+
[PDF](https://github.com/rathaROG/lap-paper/blob/main/output/pdf/native_rectangular_assignment.pdf);
|
|
194
|
+
its scripts, raw results, and reproduction steps are in the
|
|
195
|
+
[lap-paper](https://github.com/rathaROG/lap-paper) repository.
|
|
196
|
+
|
|
197
|
+
## Automation and license
|
|
198
|
+
|
|
199
|
+
GitHub workflows test supported Python versions on Windows, Linux and macOS,
|
|
200
|
+
build and test source archives and wheels, and run the three benchmark modes.
|
|
201
|
+
Wheel builds are also available manually. A version tag triggers the release
|
|
202
|
+
workflow; PyPI publishing uses the repository's `pypi` environment and requires
|
|
203
|
+
its Trusted Publisher configuration. No releases are published by local builds.
|
|
204
|
+
|
|
205
|
+
MIT licensed. See [LICENSE](LICENSE), [NOTICE](NOTICE), and
|
|
206
|
+
[native-source license](src/_lapjv/LICENSE) for attribution.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
lapcpp/py.typed,sha256=oD5aTIMVm1T_JgT9BBWE-NnjwwrBB8obapedLpgBAIw,224
|
|
2
|
+
lapcpp/_lapjv.cp313-win_arm64.pyd,sha256=yNU5GQ7Bzg7VTUV-wlxgDrPaAHCtsfN-BASGpnaKxao,173056
|
|
3
|
+
lapcpp/_lapjv.pyi,sha256=6m3PseIodDLZJOefmPisHILaeHOSGAYTxOR29Qd2tY8,6667
|
|
4
|
+
lapcpp/__init__.py,sha256=bMRp9bHWQyDEBn5thkvxjoEMDsMYik77tpfzM10nzbg,693
|
|
5
|
+
lapcpp/__init__.pyi,sha256=lBZQwPZlnjCne2rE5a85Y9diKHVMqyowgBmuA2C9Jo4,102
|
|
6
|
+
lapcpp-1.0.0.dist-info/DELVEWHEEL,sha256=dEbW2xsPvNhZ5WZUBv-Ld_hD3977--UgqQ_WrZb1bx8,540
|
|
7
|
+
lapcpp-1.0.0.dist-info/METADATA,sha256=4BD09VJzOZb2EGPZxad0-UesMHOkkhaOhRExOVRcZg0,9021
|
|
8
|
+
lapcpp-1.0.0.dist-info/RECORD,,
|
|
9
|
+
lapcpp-1.0.0.dist-info/top_level.txt,sha256=hRzd-3vte45FX1s-c2Ff90stQVMvmtz-8VaealriXfs,7
|
|
10
|
+
lapcpp-1.0.0.dist-info/WHEEL,sha256=ayu21Mpadbv-ezysr7KMvwt_cMNEKGc6DXRKBGsIOVc,101
|
|
11
|
+
lapcpp-1.0.0.dist-info/licenses/LICENSE,sha256=CwiIqXxH7u-IMqGpwv76ZusNV6NTJ6lUxtFY7OA3tfs,1086
|
|
12
|
+
lapcpp-1.0.0.dist-info/licenses/NOTICE,sha256=iS5idwxB3Us51wc7F7uvRCwuThO02Et_p35YW9TOgVc,671
|
|
13
|
+
lapcpp-1.0.0.dist-info/licenses/src/_lapjv/LICENSE,sha256=jX3ViOgZaNCvcXnIYO25Gjz3IuliYGm0SoC1eaHG3jw,1136
|
|
14
|
+
lapcpp-1.0.0.dist-info/licenses/tests/LICENSE,sha256=ywsyoyzHjv5Up3ccXuz6Z8fyRchCjVhSZkYbaJu2nIs,1361
|
|
15
|
+
lapcpp.libs/msvcp140-167ceac85c2d726c4cd9e39b8881fafd.dll,sha256=FnzqyFwtcmxM2eObiIH6_cbeFSDA5nxPaycSNfnSpsU,1588064
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 rathaROG
|
|
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.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
lapcpp 1.0.0 is derived from the lapjvh solver family of LAPX 0.11.1
|
|
2
|
+
(rathaROG/lapx, v0.11.1).
|
|
3
|
+
|
|
4
|
+
The square Jonker-Volgenant implementation derives from source{d}/lapjv
|
|
5
|
+
and carries the copyright of Vadim Markovtsev (2016). Native rectangular
|
|
6
|
+
assignment, implicit rejection handling, adapters and batch execution derive
|
|
7
|
+
from Ratha SIV's LAPX work (2026). See src/_lapjv/LICENSE for the full MIT text.
|
|
8
|
+
The top-level LICENSE covers this package. Neither package requires LAPX.
|
|
9
|
+
|
|
10
|
+
Historical regression matrices and fixture generators are adapted from LAPX/gatagat-lap tests under BSD-2-Clause. See tests/LICENSE. The epsilon dataset was contributed by Michael Lewis.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2016 Vadim Markovtsev (source{d})
|
|
4
|
+
Copyright (c) 2026 Ratha SIV
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
in the Software without restriction, including without limitation the rights
|
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
SOFTWARE.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
BSD 2-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2012-2025, Tomas Kazmar
|
|
4
|
+
Copyright (c) 2026, Ratha SIV
|
|
5
|
+
|
|
6
|
+
Redistribution and use in source and binary forms, with or without
|
|
7
|
+
modification, are permitted provided that the following conditions are met:
|
|
8
|
+
|
|
9
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
10
|
+
list of conditions and the following disclaimer.
|
|
11
|
+
|
|
12
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
13
|
+
this list of conditions and the following disclaimer in the documentation
|
|
14
|
+
and/or other materials provided with the distribution.
|
|
15
|
+
|
|
16
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
17
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
18
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
19
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
20
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
21
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
22
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
23
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
24
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
25
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
lapcpp
|
|
Binary file
|