l1tv 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.
Files changed (54) hide show
  1. l1tv-0.1.0/.github/workflows/ci.yml +70 -0
  2. l1tv-0.1.0/.github/workflows/release.yml +111 -0
  3. l1tv-0.1.0/.gitignore +50 -0
  4. l1tv-0.1.0/Auxiliary/deltaSNR.m +14 -0
  5. l1tv-0.1.0/Auxiliary/distAngle.m +14 -0
  6. l1tv-0.1.0/Auxiliary/distTransformL1.m +15 -0
  7. l1tv-0.1.0/Auxiliary/randCP.m +13 -0
  8. l1tv-0.1.0/Auxiliary/randl.m +10 -0
  9. l1tv-0.1.0/Auxiliary/wrapAngle.m +7 -0
  10. l1tv-0.1.0/CHANGELOG.md +59 -0
  11. l1tv-0.1.0/CITATION.cff +41 -0
  12. l1tv-0.1.0/Cargo.lock +230 -0
  13. l1tv-0.1.0/Cargo.toml +41 -0
  14. l1tv-0.1.0/Demos/demo_L1TV_Circ.m +40 -0
  15. l1tv-0.1.0/Demos/demo_L1TV_Real.m +37 -0
  16. l1tv-0.1.0/Docs/L1TV_Circ.png +0 -0
  17. l1tv-0.1.0/Docs/L1TV_Real.png +0 -0
  18. l1tv-0.1.0/L1TV_Circ.m +80 -0
  19. l1tv-0.1.0/L1TV_Real.m +62 -0
  20. l1tv-0.1.0/LICENSE +21 -0
  21. l1tv-0.1.0/PKG-INFO +147 -0
  22. l1tv-0.1.0/PORTED_BY.md +58 -0
  23. l1tv-0.1.0/README.md +23 -0
  24. l1tv-0.1.0/README_PYTHON.md +113 -0
  25. l1tv-0.1.0/demos_python/demo_circ.py +99 -0
  26. l1tv-0.1.0/demos_python/demo_real.py +95 -0
  27. l1tv-0.1.0/l1tv/__init__.py +161 -0
  28. l1tv-0.1.0/pyproject.toml +62 -0
  29. l1tv-0.1.0/setPath.m +8 -0
  30. l1tv-0.1.0/src/circ.rs +359 -0
  31. l1tv-0.1.0/src/dist_transform.rs +201 -0
  32. l1tv-0.1.0/src/lib.rs +61 -0
  33. l1tv-0.1.0/src/real.rs +282 -0
  34. l1tv-0.1.0/tests/conftest.py +45 -0
  35. l1tv-0.1.0/tests/make_fixtures.m +89 -0
  36. l1tv-0.1.0/tests/matlab_fixtures/circ_alpha0.mat +0 -0
  37. l1tv-0.1.0/tests/matlab_fixtures/circ_branchcut.mat +0 -0
  38. l1tv-0.1.0/tests/matlab_fixtures/circ_corners.mat +0 -0
  39. l1tv-0.1.0/tests/matlab_fixtures/circ_huge.mat +0 -0
  40. l1tv-0.1.0/tests/matlab_fixtures/circ_n100.mat +0 -0
  41. l1tv-0.1.0/tests/matlab_fixtures/circ_n100_w.mat +0 -0
  42. l1tv-0.1.0/tests/matlab_fixtures/circ_n2000.mat +0 -0
  43. l1tv-0.1.0/tests/matlab_fixtures/circ_tiny.mat +0 -0
  44. l1tv-0.1.0/tests/matlab_fixtures/real_alpha0.mat +0 -0
  45. l1tv-0.1.0/tests/matlab_fixtures/real_huge.mat +0 -0
  46. l1tv-0.1.0/tests/matlab_fixtures/real_int.mat +0 -0
  47. l1tv-0.1.0/tests/matlab_fixtures/real_n100.mat +0 -0
  48. l1tv-0.1.0/tests/matlab_fixtures/real_n100_w.mat +0 -0
  49. l1tv-0.1.0/tests/matlab_fixtures/real_n2000.mat +0 -0
  50. l1tv-0.1.0/tests/matlab_fixtures/real_small.mat +0 -0
  51. l1tv-0.1.0/tests/matlab_fixtures/real_tiny.mat +0 -0
  52. l1tv-0.1.0/tests/test_circ.py +99 -0
  53. l1tv-0.1.0/tests/test_matlab_parity.py +105 -0
  54. l1tv-0.1.0/tests/test_real.py +138 -0
@@ -0,0 +1,70 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [master]
6
+ pull_request:
7
+
8
+ jobs:
9
+ rust:
10
+ name: Rust ${{ matrix.os }}
11
+ runs-on: ${{ matrix.os }}
12
+ strategy:
13
+ fail-fast: false
14
+ matrix:
15
+ os: [ubuntu-latest, macos-latest, windows-latest]
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+ # Pin Python so pyo3-ffi's build script doesn't pick up the runner's
19
+ # default Python (now 3.14 on GHA's latest images), which is newer than
20
+ # pyo3 0.24.2's supported range.
21
+ - uses: actions/setup-python@v5
22
+ with:
23
+ python-version: "3.12"
24
+ - uses: dtolnay/rust-toolchain@stable
25
+ with:
26
+ components: clippy, rustfmt
27
+ - uses: Swatinem/rust-cache@v2
28
+ - name: cargo fmt
29
+ run: cargo fmt --all -- --check
30
+ # clippy is platform-agnostic in intent; running it on one platform is
31
+ # enough and avoids macOS/Windows-specific lint flakes.
32
+ - name: cargo clippy
33
+ if: matrix.os == 'ubuntu-latest'
34
+ run: cargo clippy --all-targets -- -D warnings
35
+ - name: cargo test
36
+ run: cargo test --release
37
+
38
+ python:
39
+ name: Python ${{ matrix.python-version }} ${{ matrix.os }}
40
+ runs-on: ${{ matrix.os }}
41
+ strategy:
42
+ fail-fast: false
43
+ matrix:
44
+ os: [ubuntu-latest, macos-latest, windows-latest]
45
+ python-version: ["3.9", "3.12"]
46
+ steps:
47
+ - uses: actions/checkout@v4
48
+ - uses: actions/setup-python@v5
49
+ with:
50
+ python-version: ${{ matrix.python-version }}
51
+ - uses: dtolnay/rust-toolchain@stable
52
+ - uses: Swatinem/rust-cache@v2
53
+ # PEP 517 install — pulls maturin from build-system.requires, builds the
54
+ # Rust extension via maturin, and installs the package + test extras
55
+ # (pytest, scipy, matplotlib). Avoids the venv-required `maturin develop`
56
+ # path that fails on stock GitHub-hosted runners.
57
+ - name: install l1tv (PEP 517)
58
+ run: pip install ".[test]"
59
+ # Run pytest and demos from a directory that does NOT contain `l1tv/`,
60
+ # otherwise Python's cwd-on-sys.path rule shadows the installed package
61
+ # with the source-tree copy (which lacks the compiled `_core` extension).
62
+ - name: pytest
63
+ working-directory: ${{ runner.temp }}
64
+ run: pytest -v ${{ github.workspace }}/tests
65
+ - name: smoke demos
66
+ working-directory: ${{ runner.temp }}
67
+ shell: bash
68
+ run: |
69
+ python "${GITHUB_WORKSPACE}/demos_python/demo_real.py"
70
+ python "${GITHUB_WORKSPACE}/demos_python/demo_circ.py"
@@ -0,0 +1,111 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+ workflow_dispatch:
8
+
9
+ permissions:
10
+ contents: read
11
+ id-token: write # for trusted publishing to PyPI
12
+
13
+ jobs:
14
+ linux:
15
+ runs-on: ubuntu-latest
16
+ strategy:
17
+ fail-fast: false
18
+ matrix:
19
+ target: [x86_64, aarch64]
20
+ steps:
21
+ - uses: actions/checkout@v4
22
+ - uses: actions/setup-python@v5
23
+ with: { python-version: "3.12" }
24
+ - uses: PyO3/maturin-action@v1
25
+ with:
26
+ target: ${{ matrix.target }}
27
+ # Drop --zig and rely on `manylinux: auto`. With --zig, maturin
28
+ # cross-links on the host and needs an aarch64-targeted Python on
29
+ # PATH (which there isn't), causing the aarch64 build to fail with
30
+ # "Couldn't find any python interpreters". `manylinux: auto` runs
31
+ # the build inside the official quay.io/pypa/manylinux*_<arch>
32
+ # container, which ships /opt/python/cp3X-cp3X/bin/python for
33
+ # every supported version; --find-interpreter picks them up so we
34
+ # get a wheel per Python per arch.
35
+ args: --release --out dist --find-interpreter
36
+ manylinux: auto
37
+ - uses: actions/upload-artifact@v4
38
+ with:
39
+ name: wheels-linux-${{ matrix.target }}
40
+ path: dist
41
+
42
+ macos:
43
+ # macos-latest is Apple Silicon (arm64). Phase 4 cross-cutting decision:
44
+ # drop the macos-13 (x86) runner family-wide due to 24-hour queue hangs.
45
+ # Intel-mac users fall back to source builds.
46
+ runs-on: macos-latest
47
+ strategy:
48
+ fail-fast: false
49
+ matrix:
50
+ target: [x86_64, aarch64]
51
+ steps:
52
+ - uses: actions/checkout@v4
53
+ - uses: actions/setup-python@v5
54
+ with: { python-version: "3.12" }
55
+ - uses: PyO3/maturin-action@v1
56
+ with:
57
+ target: ${{ matrix.target }}
58
+ args: --release --out dist
59
+ - uses: actions/upload-artifact@v4
60
+ with:
61
+ name: wheels-macos-${{ matrix.target }}
62
+ path: dist
63
+
64
+ windows:
65
+ runs-on: windows-latest
66
+ strategy:
67
+ fail-fast: false
68
+ matrix:
69
+ target: [x64]
70
+ steps:
71
+ - uses: actions/checkout@v4
72
+ - uses: actions/setup-python@v5
73
+ with: { python-version: "3.12" }
74
+ - uses: PyO3/maturin-action@v1
75
+ with:
76
+ target: ${{ matrix.target }}
77
+ args: --release --out dist
78
+ - uses: actions/upload-artifact@v4
79
+ with:
80
+ name: wheels-windows-${{ matrix.target }}
81
+ path: dist
82
+
83
+ sdist:
84
+ runs-on: ubuntu-latest
85
+ steps:
86
+ - uses: actions/checkout@v4
87
+ - uses: PyO3/maturin-action@v1
88
+ with:
89
+ command: sdist
90
+ args: --out dist
91
+ - uses: actions/upload-artifact@v4
92
+ with:
93
+ name: wheels-sdist
94
+ path: dist
95
+
96
+ publish:
97
+ name: Publish to PyPI
98
+ runs-on: ubuntu-latest
99
+ needs: [linux, macos, windows, sdist]
100
+ if: startsWith(github.ref, 'refs/tags/v')
101
+ # Tied to the `pypi` GitHub Actions environment — must match the
102
+ # "Environment name" in PyPI's trusted-publisher config for `l1tv`.
103
+ # Required reviewers / branch rules configured at the env level act as
104
+ # the gate before this job mints an OIDC token and uploads.
105
+ environment: pypi
106
+ steps:
107
+ - uses: actions/download-artifact@v4
108
+ with:
109
+ path: dist
110
+ merge-multiple: true
111
+ - uses: pypa/gh-action-pypi-publish@release/v1
l1tv-0.1.0/.gitignore ADDED
@@ -0,0 +1,50 @@
1
+
2
+ unitTests/swUnitTestDistTransformL1.m
3
+
4
+ Docs/create_images.m
5
+
6
+ unitTests/swDistTransformL1FH.m
7
+
8
+ unitTests/swUnitTestDistAngles.m
9
+
10
+ unitTests/swUnitTestDistTransformNE.m
11
+
12
+ unitTests/swUnitTestMinL1TVCirc.m
13
+
14
+ # Rust build artifacts
15
+ target/
16
+ Cargo.lock.bak
17
+
18
+ # Compiled Python extension (platform-specific; built locally or by CI)
19
+ l1tv/_core*.so
20
+ l1tv/_core*.pyd
21
+
22
+ # Python cache
23
+ __pycache__/
24
+ *.pyc
25
+ *.pyo
26
+ .pytest_cache/
27
+
28
+ # macOS
29
+ .DS_Store
30
+
31
+ # Distribution / packaging
32
+ dist/
33
+ build/
34
+ *.egg-info/
35
+ .eggs/
36
+
37
+ # Editors / IDEs
38
+ .vscode/
39
+ .idea/
40
+ *.swp
41
+
42
+ # Jupyter
43
+ .ipynb_checkpoints/
44
+
45
+ # Demo plot artifacts (regenerated on demand)
46
+ demos_python/*.png
47
+
48
+ # Virtualenvs
49
+ .venv/
50
+ venv/
@@ -0,0 +1,14 @@
1
+ function dsnr = deltaSNR( groundTruth, data, estimate, dataSpace)
2
+ %deltaSNR Computes the signal to noise ratio improvement of a restoration
3
+
4
+ switch dataSpace
5
+ case 'circ'
6
+ dist = @(x,y) distAngle(x,y);
7
+ case 'real'
8
+ dist = @(x,y) abs(x - y);
9
+ end
10
+
11
+ dsnr = 10 * log10(sum( dist(groundTruth, data).^2 ) / sum( dist(groundTruth, estimate).^2 ));
12
+
13
+ end
14
+
@@ -0,0 +1,14 @@
1
+ function d = distAngle( phi, psi )
2
+ %distAngle Distance between two angles phi and psi
3
+
4
+ if (all(abs(phi) <= pi)) && (all(abs(psi) <= pi))
5
+ % cheap computation for angles in the interval [-pi, pi]
6
+ aux = phi - psi;
7
+ d = min(abs([aux(:) + 2 * pi, aux(:), aux(:) - 2 * pi]), [], 2);
8
+ d = reshape(d, size(aux));
9
+ else
10
+ % more expensive calculation for angles outside the interval [-pi, pi]
11
+ aux = abs(angle(exp(1i * (phi - psi))));
12
+ d = min(aux, 2*pi-aux);
13
+ end
14
+
@@ -0,0 +1,15 @@
1
+ function B = distTransformL1( B, R, alpha )
2
+ %distTransformL1 Fast computation of L1 distance transform
3
+
4
+ K = numel(B);
5
+ % forward pass
6
+ for k=2:K
7
+ B(k) = min( B(k), B(k-1) + alpha * (R(k) - R(k-1)));
8
+ end
9
+ % backward pass
10
+ for k=K-1:-1:1
11
+ B(k) = min( B(k), B(k+1) + alpha * (R(k+1) - R(k)));
12
+ end
13
+
14
+ end
15
+
@@ -0,0 +1,13 @@
1
+ function r = randCP( r, lambda)
2
+ %randCP Bernoulli-thinned innovations approximating a compound Poisson.
3
+ % Each entry of r is independently zeroed with probability exp(-lambda);
4
+ % otherwise it is left at the supplied value. This matches the marginal
5
+ % P(N = 0) of a Poisson(lambda) point process and is used in the demos as
6
+ % a cheap surrogate for compound-Poisson innovations (good for small
7
+ % lambda; not the actual compound Poisson distribution).
8
+
9
+ x = rand(size(r)); % uniform distr. vector
10
+ r(x(:) <= exp(-lambda)) = 0;
11
+
12
+ end
13
+
@@ -0,0 +1,10 @@
1
+ function y = randl( varargin )
2
+ %randl Random normal Laplacian noise, mean = 0, variance sigma^2 = 1
3
+ % pdf: $\frac{1}{\sqrt{2}} e^{- \sqrt{2} |x|}$
4
+ % Same input arguments as function rand
5
+
6
+ x = rand( varargin{:} ) - 0.5;
7
+ y = -sign(x) .* log(1 - 2 * abs(x)) / sqrt(2);
8
+
9
+ end
10
+
@@ -0,0 +1,7 @@
1
+ function x = wrapAngle( y )
2
+ %wrapAngle Wraps the angle y to the interval [-pi, pi]
3
+
4
+ x = angle(exp(1i * y));
5
+
6
+ end
7
+
@@ -0,0 +1,59 @@
1
+ # Changelog
2
+
3
+ All notable changes to the `l1tv` Python package are documented in this file.
4
+ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this
5
+ project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ The MATLAB reference implementation is tracked separately in this same repo's
8
+ git history; algorithmic semantics are unchanged across the port.
9
+
10
+ ## [0.1.0] — Unreleased
11
+
12
+ Initial Python/Rust port of the MATLAB L1TV library by Storath, Weinmann
13
+ (2016).
14
+
15
+ ### Added
16
+
17
+ - Rust core (`_l1tv_core` cdylib, pyo3 0.24.1, numpy 0.24, ndarray 0.16):
18
+ - `solve_l1_tv_real(y, alpha, weights, use_dist_transform)` — exact
19
+ L1-TV solver for real-valued 1-D signals.
20
+ - `solve_l1_tv_circ(y, alpha, weights, use_dist_transform)` — exact
21
+ L1-TV solver for circle-valued 1-D signals with shortest-arc distance.
22
+ - `dist_transform.l1_dt_with_argmin` — forward + backward L1 distance
23
+ transform with argmin tracking (used by both solvers).
24
+ - Python facade (`l1tv` package):
25
+ - `min_l1_tv` and `min_l1_tv_circ` public functions.
26
+ - Boundary handling: any 1-D shape (list, tuple, row, column) is
27
+ accepted via `np.ascontiguousarray + .ravel()`. Validates finite y,
28
+ non-negative alpha, shape-matched non-negative weights.
29
+ - Tests:
30
+ - 25 Rust unit tests covering DT correctness, candidate-set theorem,
31
+ edge cases, and naive-vs-DT objective parity for both solvers.
32
+ - 28 Python tests covering the public API, input validation, and shape
33
+ handling.
34
+ - 16 MATLAB-vs-Rust objective parity tests (parametrised) using
35
+ pre-generated `.mat` fixtures; relerr < 1e-10 on every fixture.
36
+ - Demos: `demos_python/demo_real.py` and `demo_circ.py` mirror the
37
+ original MATLAB demos and run end-to-end with optional matplotlib
38
+ plotting.
39
+
40
+ ### Memory layout
41
+
42
+ The Rust port uses a rolling pair of K-cost columns plus a `u32` argmin-
43
+ pointer table for O(N) backtracking, replacing MATLAB's full `(K, N)`
44
+ double-precision cost table. Net memory roughly halves at typical sizes
45
+ and can drop further if argmin pointers shrink to `u16` (when `K < 65536`).
46
+
47
+ ### Differences from the MATLAB reference
48
+
49
+ - Input shape: silently produced wrong results for row-vector input in
50
+ `L1TV_Circ.m` (the antipodal vertical concat became a 2×K matrix). The
51
+ Python facade rules this out by construction.
52
+ - Numerical type: `f64` only. Cast at the boundary if you need `f32`.
53
+ - `setPath.m`-style invasive path setup is not relevant; just
54
+ `pip install l1tv`.
55
+
56
+ These differences do not affect algorithmic semantics for column-vector
57
+ MATLAB inputs, which is the only shape the MATLAB demos used.
58
+
59
+ [0.1.0]: https://github.com/mstorath/L1TV/releases/tag/v0.1.0
@@ -0,0 +1,41 @@
1
+ cff-version: 1.2.0
2
+ message: "If you use this software, please cite both the software and the associated paper below."
3
+ title: "L1TV: Exact L1-TV regularization of real- or circle-valued signals"
4
+ abstract: "Reference MATLAB implementation of exact, non-iterative algorithms for L1-TV regularization of real-valued and circle-valued (phase / orientation) signals. Solves the discrete L1-TV minimisation problem to global optimum via dynamic programming with a distance-transform speedup."
5
+ type: software
6
+ url: "https://github.com/mstorath/L1TV"
7
+ repository-code: "https://github.com/mstorath/L1TV"
8
+ license: MIT
9
+ authors:
10
+ - family-names: Storath
11
+ given-names: Martin
12
+ email: martin.storath@thws.de
13
+ orcid: "https://orcid.org/0000-0003-1427-0776"
14
+ affiliation: "Lab for Mathematical Methods in Computer Vision and Machine Learning, Technische Hochschule Würzburg-Schweinfurt"
15
+ - family-names: Weinmann
16
+ given-names: Andreas
17
+ email: andreas.weinmann@thws.de
18
+ orcid: "https://orcid.org/0000-0002-4969-7609"
19
+ affiliation: "Department of Mathematics and Natural Sciences, Hochschule Darmstadt"
20
+ preferred-citation:
21
+ type: article
22
+ title: "Exact algorithms for L1-TV regularization of real-valued or circle-valued signals"
23
+ authors:
24
+ - family-names: Storath
25
+ given-names: Martin
26
+ orcid: "https://orcid.org/0000-0003-1427-0776"
27
+ - family-names: Weinmann
28
+ given-names: Andreas
29
+ orcid: "https://orcid.org/0000-0002-4969-7609"
30
+ - family-names: Unser
31
+ given-names: Michael
32
+ orcid: "https://orcid.org/0000-0003-1248-2513"
33
+ journal: "SIAM Journal on Scientific Computing"
34
+ volume: 38
35
+ issue: 1
36
+ start: "A614"
37
+ end: "A630"
38
+ year: 2016
39
+ publisher:
40
+ name: "Society for Industrial and Applied Mathematics"
41
+ doi: "10.1137/15M101796X"
l1tv-0.1.0/Cargo.lock ADDED
@@ -0,0 +1,230 @@
1
+ # This file is automatically @generated by Cargo.
2
+ # It is not intended for manual editing.
3
+ version = 4
4
+
5
+ [[package]]
6
+ name = "_l1tv_core"
7
+ version = "0.1.0"
8
+ dependencies = [
9
+ "ndarray",
10
+ "numpy",
11
+ "pyo3",
12
+ ]
13
+
14
+ [[package]]
15
+ name = "autocfg"
16
+ version = "1.5.0"
17
+ source = "registry+https://github.com/rust-lang/crates.io-index"
18
+ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
19
+
20
+ [[package]]
21
+ name = "heck"
22
+ version = "0.5.0"
23
+ source = "registry+https://github.com/rust-lang/crates.io-index"
24
+ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
25
+
26
+ [[package]]
27
+ name = "libc"
28
+ version = "0.2.186"
29
+ source = "registry+https://github.com/rust-lang/crates.io-index"
30
+ checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
31
+
32
+ [[package]]
33
+ name = "matrixmultiply"
34
+ version = "0.3.10"
35
+ source = "registry+https://github.com/rust-lang/crates.io-index"
36
+ checksum = "a06de3016e9fae57a36fd14dba131fccf49f74b40b7fbdb472f96e361ec71a08"
37
+ dependencies = [
38
+ "autocfg",
39
+ "rawpointer",
40
+ ]
41
+
42
+ [[package]]
43
+ name = "ndarray"
44
+ version = "0.16.1"
45
+ source = "registry+https://github.com/rust-lang/crates.io-index"
46
+ checksum = "882ed72dce9365842bf196bdeedf5055305f11fc8c03dee7bb0194a6cad34841"
47
+ dependencies = [
48
+ "matrixmultiply",
49
+ "num-complex",
50
+ "num-integer",
51
+ "num-traits",
52
+ "portable-atomic",
53
+ "portable-atomic-util",
54
+ "rawpointer",
55
+ ]
56
+
57
+ [[package]]
58
+ name = "num-complex"
59
+ version = "0.4.6"
60
+ source = "registry+https://github.com/rust-lang/crates.io-index"
61
+ checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495"
62
+ dependencies = [
63
+ "num-traits",
64
+ ]
65
+
66
+ [[package]]
67
+ name = "num-integer"
68
+ version = "0.1.46"
69
+ source = "registry+https://github.com/rust-lang/crates.io-index"
70
+ checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
71
+ dependencies = [
72
+ "num-traits",
73
+ ]
74
+
75
+ [[package]]
76
+ name = "num-traits"
77
+ version = "0.2.19"
78
+ source = "registry+https://github.com/rust-lang/crates.io-index"
79
+ checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
80
+ dependencies = [
81
+ "autocfg",
82
+ ]
83
+
84
+ [[package]]
85
+ name = "numpy"
86
+ version = "0.28.0"
87
+ source = "registry+https://github.com/rust-lang/crates.io-index"
88
+ checksum = "778da78c64ddc928ebf5ad9df5edf0789410ff3bdbf3619aed51cd789a6af1e2"
89
+ dependencies = [
90
+ "libc",
91
+ "ndarray",
92
+ "num-complex",
93
+ "num-integer",
94
+ "num-traits",
95
+ "pyo3",
96
+ "pyo3-build-config",
97
+ "rustc-hash",
98
+ ]
99
+
100
+ [[package]]
101
+ name = "once_cell"
102
+ version = "1.21.4"
103
+ source = "registry+https://github.com/rust-lang/crates.io-index"
104
+ checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
105
+
106
+ [[package]]
107
+ name = "portable-atomic"
108
+ version = "1.13.1"
109
+ source = "registry+https://github.com/rust-lang/crates.io-index"
110
+ checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49"
111
+
112
+ [[package]]
113
+ name = "portable-atomic-util"
114
+ version = "0.2.7"
115
+ source = "registry+https://github.com/rust-lang/crates.io-index"
116
+ checksum = "c2a106d1259c23fac8e543272398ae0e3c0b8d33c88ed73d0cc71b0f1d902618"
117
+ dependencies = [
118
+ "portable-atomic",
119
+ ]
120
+
121
+ [[package]]
122
+ name = "proc-macro2"
123
+ version = "1.0.106"
124
+ source = "registry+https://github.com/rust-lang/crates.io-index"
125
+ checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
126
+ dependencies = [
127
+ "unicode-ident",
128
+ ]
129
+
130
+ [[package]]
131
+ name = "pyo3"
132
+ version = "0.28.3"
133
+ source = "registry+https://github.com/rust-lang/crates.io-index"
134
+ checksum = "91fd8e38a3b50ed1167fb981cd6fd60147e091784c427b8f7183a7ee32c31c12"
135
+ dependencies = [
136
+ "libc",
137
+ "once_cell",
138
+ "portable-atomic",
139
+ "pyo3-build-config",
140
+ "pyo3-ffi",
141
+ "pyo3-macros",
142
+ ]
143
+
144
+ [[package]]
145
+ name = "pyo3-build-config"
146
+ version = "0.28.3"
147
+ source = "registry+https://github.com/rust-lang/crates.io-index"
148
+ checksum = "e368e7ddfdeb98c9bca7f8383be1648fd84ab466bf2bc015e94008db6d35611e"
149
+ dependencies = [
150
+ "target-lexicon",
151
+ ]
152
+
153
+ [[package]]
154
+ name = "pyo3-ffi"
155
+ version = "0.28.3"
156
+ source = "registry+https://github.com/rust-lang/crates.io-index"
157
+ checksum = "7f29e10af80b1f7ccaf7f69eace800a03ecd13e883acfacc1e5d0988605f651e"
158
+ dependencies = [
159
+ "libc",
160
+ "pyo3-build-config",
161
+ ]
162
+
163
+ [[package]]
164
+ name = "pyo3-macros"
165
+ version = "0.28.3"
166
+ source = "registry+https://github.com/rust-lang/crates.io-index"
167
+ checksum = "df6e520eff47c45997d2fc7dd8214b25dd1310918bbb2642156ef66a67f29813"
168
+ dependencies = [
169
+ "proc-macro2",
170
+ "pyo3-macros-backend",
171
+ "quote",
172
+ "syn",
173
+ ]
174
+
175
+ [[package]]
176
+ name = "pyo3-macros-backend"
177
+ version = "0.28.3"
178
+ source = "registry+https://github.com/rust-lang/crates.io-index"
179
+ checksum = "c4cdc218d835738f81c2338f822078af45b4afdf8b2e33cbb5916f108b813acb"
180
+ dependencies = [
181
+ "heck",
182
+ "proc-macro2",
183
+ "pyo3-build-config",
184
+ "quote",
185
+ "syn",
186
+ ]
187
+
188
+ [[package]]
189
+ name = "quote"
190
+ version = "1.0.45"
191
+ source = "registry+https://github.com/rust-lang/crates.io-index"
192
+ checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
193
+ dependencies = [
194
+ "proc-macro2",
195
+ ]
196
+
197
+ [[package]]
198
+ name = "rawpointer"
199
+ version = "0.2.1"
200
+ source = "registry+https://github.com/rust-lang/crates.io-index"
201
+ checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3"
202
+
203
+ [[package]]
204
+ name = "rustc-hash"
205
+ version = "2.1.2"
206
+ source = "registry+https://github.com/rust-lang/crates.io-index"
207
+ checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe"
208
+
209
+ [[package]]
210
+ name = "syn"
211
+ version = "2.0.117"
212
+ source = "registry+https://github.com/rust-lang/crates.io-index"
213
+ checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
214
+ dependencies = [
215
+ "proc-macro2",
216
+ "quote",
217
+ "unicode-ident",
218
+ ]
219
+
220
+ [[package]]
221
+ name = "target-lexicon"
222
+ version = "0.13.5"
223
+ source = "registry+https://github.com/rust-lang/crates.io-index"
224
+ checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca"
225
+
226
+ [[package]]
227
+ name = "unicode-ident"
228
+ version = "1.0.24"
229
+ source = "registry+https://github.com/rust-lang/crates.io-index"
230
+ checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
l1tv-0.1.0/Cargo.toml ADDED
@@ -0,0 +1,41 @@
1
+ [package]
2
+ name = "_l1tv_core"
3
+ version = "0.1.0"
4
+ edition = "2021"
5
+ description = "Rust core for the l1tv Python package — exact L1-TV regularisation of real- or circle-valued 1-D signals (Storath, Weinmann, Unser 2016). Automated port of the original MATLAB by Claude Opus coding agent (Anthropic, 2026)."
6
+ authors = [
7
+ "Martin Storath <storath@gmail.com> (original MATLAB)",
8
+ "Andreas Weinmann (original MATLAB)",
9
+ "Claude Opus coding agent, Anthropic (Rust port, 2026)"
10
+ ]
11
+ license = "MIT"
12
+ repository = "https://github.com/mstorath/L1TV"
13
+ readme = "README_PYTHON.md"
14
+
15
+ [lib]
16
+ name = "_l1tv_core"
17
+ crate-type = ["cdylib"]
18
+
19
+ [dependencies]
20
+ # pyo3 0.28 (June 2025+) is the first family of versions that supports
21
+ # CPython 3.14. The previous family-wide pin at 0.24.1 (a security-fix pin
22
+ # for GHSA-pph8-gcv7-4qj5) does not support 3.14 and broke the linux release
23
+ # build with `--find-interpreter` after the manylinux containers added 3.14.
24
+ # numpy crate version tracks pyo3 minor; ndarray version tracks numpy's.
25
+ pyo3 = { version = "0.28" }
26
+ numpy = "0.28"
27
+ ndarray = "0.16"
28
+
29
+ # `extension-module` is gated behind a Cargo feature instead of being on
30
+ # unconditionally. Reason: with the feature ON, pyo3 instructs the linker to
31
+ # leave Python symbols unresolved (resolved at runtime by the Python
32
+ # interpreter that loads the .so). That works for the wheel target but breaks
33
+ # `cargo test`, whose test binary has no Python interpreter to satisfy the
34
+ # undefined symbols at runtime — macOS's strict linker rejects the binary.
35
+ # Maturin enables the feature via [tool.maturin].features in pyproject.toml.
36
+ [features]
37
+ extension-module = ["pyo3/extension-module"]
38
+
39
+ [profile.release]
40
+ opt-level = 3
41
+ lto = true