parx 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.
- parx-0.1.0/.envrc +3 -0
- parx-0.1.0/.github/workflows/ci.yml +55 -0
- parx-0.1.0/.github/workflows/docs.yml +72 -0
- parx-0.1.0/.github/workflows/release.yml +56 -0
- parx-0.1.0/.gitignore +61 -0
- parx-0.1.0/CHANGELOG.md +35 -0
- parx-0.1.0/CITATION.cff +31 -0
- parx-0.1.0/CONTRIBUTING.md +217 -0
- parx-0.1.0/LICENSE +21 -0
- parx-0.1.0/OVERVIEW.md +305 -0
- parx-0.1.0/PKG-INFO +376 -0
- parx-0.1.0/README.md +325 -0
- parx-0.1.0/TODO.md +442 -0
- parx-0.1.0/docs/concepts.md +189 -0
- parx-0.1.0/docs/examples.md +39 -0
- parx-0.1.0/docs/index.md +74 -0
- parx-0.1.0/docs/notebooks/demo_plotly.html +308 -0
- parx-0.1.0/docs/notebooks/demo_plt.html +308 -0
- parx-0.1.0/docs/notebooks/feature_embedding.html +308 -0
- parx-0.1.0/docs/notebooks/index.md +31 -0
- parx-0.1.0/docs/reference.md +57 -0
- parx-0.1.0/docs/usage.md +126 -0
- parx-0.1.0/examples/01_identity_network.py +94 -0
- parx-0.1.0/examples/02_random_mlp.py +124 -0
- parx-0.1.0/examples/03_epochs.py +151 -0
- parx-0.1.0/examples/04_feature_embedding.py +77 -0
- parx-0.1.0/mkdocs.yml +52 -0
- parx-0.1.0/notebooks/demo_plotly.py +760 -0
- parx-0.1.0/notebooks/demo_plt.py +700 -0
- parx-0.1.0/notebooks/feature_embedding.py +375 -0
- parx-0.1.0/pyproject.toml +109 -0
- parx-0.1.0/setup.cfg +4 -0
- parx-0.1.0/src/parx/__init__.py +111 -0
- parx-0.1.0/src/parx/_check.py +14 -0
- parx-0.1.0/src/parx/_julia_init.py +106 -0
- parx-0.1.0/src/parx/_lp.py +59 -0
- parx-0.1.0/src/parx/analysis.py +441 -0
- parx-0.1.0/src/parx/diagnostics.py +67 -0
- parx-0.1.0/src/parx/io.py +120 -0
- parx-0.1.0/src/parx/io_partition.py +152 -0
- parx-0.1.0/src/parx/julia/Manifest.toml +325 -0
- parx-0.1.0/src/parx/julia/Project.toml +14 -0
- parx-0.1.0/src/parx/julia/src/LinearRegions.jl +13 -0
- parx-0.1.0/src/parx/julia/src/bridge.jl +24 -0
- parx-0.1.0/src/parx/julia/src/exact.jl +177 -0
- parx-0.1.0/src/parx/julia/src/lp.jl +91 -0
- parx-0.1.0/src/parx/julia/src/self_test.jl +29 -0
- parx-0.1.0/src/parx/julia/src/sparse.jl +75 -0
- parx-0.1.0/src/parx/juliapkg.json +13 -0
- parx-0.1.0/src/parx/methods/__init__.py +87 -0
- parx-0.1.0/src/parx/methods/exact_julia.py +50 -0
- parx-0.1.0/src/parx/methods/exact_julia_fast.py +56 -0
- parx-0.1.0/src/parx/methods/exact_python.py +230 -0
- parx-0.1.0/src/parx/methods/sparse_julia.py +43 -0
- parx-0.1.0/src/parx/methods/sparse_python.py +79 -0
- parx-0.1.0/src/parx/network.py +159 -0
- parx-0.1.0/src/parx/partition.py +210 -0
- parx-0.1.0/src/parx/precompile.py +65 -0
- parx-0.1.0/src/parx/region.py +37 -0
- parx-0.1.0/src/parx/verify.py +179 -0
- parx-0.1.0/src/parx/viz.py +2009 -0
- parx-0.1.0/src/parx.egg-info/PKG-INFO +376 -0
- parx-0.1.0/src/parx.egg-info/SOURCES.txt +80 -0
- parx-0.1.0/src/parx.egg-info/dependency_links.txt +1 -0
- parx-0.1.0/src/parx.egg-info/requires.txt +33 -0
- parx-0.1.0/src/parx.egg-info/scm_file_list.json +77 -0
- parx-0.1.0/src/parx.egg-info/scm_version.json +8 -0
- parx-0.1.0/src/parx.egg-info/top_level.txt +1 -0
- parx-0.1.0/tests/conftest.py +13 -0
- parx-0.1.0/tests/test_analysis.py +447 -0
- parx-0.1.0/tests/test_diagnostics.py +38 -0
- parx-0.1.0/tests/test_io.py +116 -0
- parx-0.1.0/tests/test_io_partition.py +175 -0
- parx-0.1.0/tests/test_julia_bridge.py +326 -0
- parx-0.1.0/tests/test_julia_init.py +102 -0
- parx-0.1.0/tests/test_methods.py +215 -0
- parx-0.1.0/tests/test_mlp.py +217 -0
- parx-0.1.0/tests/test_network.py +128 -0
- parx-0.1.0/tests/test_partition.py +147 -0
- parx-0.1.0/tests/test_verify.py +180 -0
- parx-0.1.0/tests/test_viz.py +738 -0
- parx-0.1.0/uv.lock +3055 -0
parx-0.1.0/.envrc
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
name: Python ${{ matrix.python-version }} / Julia ${{ matrix.julia-version }}
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
|
|
14
|
+
strategy:
|
|
15
|
+
fail-fast: false
|
|
16
|
+
matrix:
|
|
17
|
+
python-version: ["3.10", "3.11", "3.12"]
|
|
18
|
+
julia-version: ["1.10"]
|
|
19
|
+
|
|
20
|
+
steps:
|
|
21
|
+
- uses: actions/checkout@v4
|
|
22
|
+
|
|
23
|
+
- name: Set up Julia
|
|
24
|
+
uses: julia-actions/setup-julia@v2
|
|
25
|
+
with:
|
|
26
|
+
version: ${{ matrix.julia-version }}
|
|
27
|
+
|
|
28
|
+
- name: Install uv
|
|
29
|
+
uses: astral-sh/setup-uv@v4
|
|
30
|
+
|
|
31
|
+
- name: Set up Python
|
|
32
|
+
run: uv python install ${{ matrix.python-version }}
|
|
33
|
+
|
|
34
|
+
- name: Create virtual environment
|
|
35
|
+
run: uv venv --python ${{ matrix.python-version }}
|
|
36
|
+
|
|
37
|
+
- name: Install Python dependencies
|
|
38
|
+
run: uv pip install -e ".[dev]"
|
|
39
|
+
|
|
40
|
+
- name: Instantiate Julia environment
|
|
41
|
+
run: julia --project=src/parx/julia -e "using Pkg; Pkg.instantiate()"
|
|
42
|
+
|
|
43
|
+
- name: Lint with ruff
|
|
44
|
+
run: uv run ruff check src/ tests/
|
|
45
|
+
|
|
46
|
+
- name: Run tests
|
|
47
|
+
run: uv run pytest --cov=parx --cov-report=xml
|
|
48
|
+
env:
|
|
49
|
+
JULIA_NUM_THREADS: "2"
|
|
50
|
+
|
|
51
|
+
- name: Upload coverage
|
|
52
|
+
uses: codecov/codecov-action@v4
|
|
53
|
+
with:
|
|
54
|
+
file: coverage.xml
|
|
55
|
+
continue-on-error: true
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
name: Docs
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
paths:
|
|
7
|
+
- "docs/**"
|
|
8
|
+
- "mkdocs.yml"
|
|
9
|
+
- "src/**"
|
|
10
|
+
- ".github/workflows/docs.yml"
|
|
11
|
+
pull_request:
|
|
12
|
+
branches: [main]
|
|
13
|
+
paths:
|
|
14
|
+
- "docs/**"
|
|
15
|
+
- "mkdocs.yml"
|
|
16
|
+
- "src/**"
|
|
17
|
+
- ".github/workflows/docs.yml"
|
|
18
|
+
workflow_dispatch:
|
|
19
|
+
|
|
20
|
+
permissions:
|
|
21
|
+
contents: read
|
|
22
|
+
pages: write
|
|
23
|
+
id-token: write
|
|
24
|
+
|
|
25
|
+
concurrency:
|
|
26
|
+
group: pages
|
|
27
|
+
cancel-in-progress: false
|
|
28
|
+
|
|
29
|
+
jobs:
|
|
30
|
+
build:
|
|
31
|
+
name: Build MkDocs site
|
|
32
|
+
runs-on: ubuntu-latest
|
|
33
|
+
steps:
|
|
34
|
+
- uses: actions/checkout@v4
|
|
35
|
+
|
|
36
|
+
- name: Configure GitHub Pages
|
|
37
|
+
if: github.event_name != 'pull_request'
|
|
38
|
+
uses: actions/configure-pages@v5
|
|
39
|
+
|
|
40
|
+
- name: Install uv
|
|
41
|
+
uses: astral-sh/setup-uv@v4
|
|
42
|
+
|
|
43
|
+
- name: Set up Python
|
|
44
|
+
run: uv python install 3.12
|
|
45
|
+
|
|
46
|
+
- name: Create virtual environment
|
|
47
|
+
run: uv venv --python 3.12
|
|
48
|
+
|
|
49
|
+
- name: Install docs dependencies
|
|
50
|
+
run: uv pip install "mkdocs>=1.6" "mkdocs-material>=9.5" "mkdocstrings[python]>=0.26"
|
|
51
|
+
|
|
52
|
+
- name: Build site
|
|
53
|
+
run: uv run mkdocs build --strict
|
|
54
|
+
|
|
55
|
+
- name: Upload Pages artifact
|
|
56
|
+
if: github.event_name != 'pull_request'
|
|
57
|
+
uses: actions/upload-pages-artifact@v4
|
|
58
|
+
with:
|
|
59
|
+
path: site
|
|
60
|
+
|
|
61
|
+
deploy:
|
|
62
|
+
name: Deploy to GitHub Pages
|
|
63
|
+
needs: build
|
|
64
|
+
if: github.event_name != 'pull_request'
|
|
65
|
+
runs-on: ubuntu-latest
|
|
66
|
+
environment:
|
|
67
|
+
name: github-pages
|
|
68
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
69
|
+
steps:
|
|
70
|
+
- name: Deploy to GitHub Pages
|
|
71
|
+
id: deployment
|
|
72
|
+
uses: actions/deploy-pages@v4
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
contents: read
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
build:
|
|
12
|
+
name: Build distribution
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
with:
|
|
17
|
+
# setuptools-scm needs full history + tags to compute the version;
|
|
18
|
+
# a shallow checkout would silently produce a 0.0.devN version.
|
|
19
|
+
fetch-depth: 0
|
|
20
|
+
|
|
21
|
+
- name: Install uv
|
|
22
|
+
uses: astral-sh/setup-uv@v4
|
|
23
|
+
|
|
24
|
+
- name: Set up Python
|
|
25
|
+
run: uv python install 3.12
|
|
26
|
+
|
|
27
|
+
- name: Build sdist and wheel
|
|
28
|
+
run: uv build
|
|
29
|
+
|
|
30
|
+
- name: Upload build artifacts
|
|
31
|
+
uses: actions/upload-artifact@v4
|
|
32
|
+
with:
|
|
33
|
+
name: dist
|
|
34
|
+
path: dist/
|
|
35
|
+
|
|
36
|
+
publish:
|
|
37
|
+
name: Publish to PyPI
|
|
38
|
+
needs: build
|
|
39
|
+
runs-on: ubuntu-latest
|
|
40
|
+
environment:
|
|
41
|
+
name: pypi
|
|
42
|
+
url: https://pypi.org/project/parx/
|
|
43
|
+
permissions:
|
|
44
|
+
id-token: write # required for Trusted Publishing (OIDC) — no API token stored
|
|
45
|
+
steps:
|
|
46
|
+
- name: Download build artifacts
|
|
47
|
+
uses: actions/download-artifact@v4
|
|
48
|
+
with:
|
|
49
|
+
name: dist
|
|
50
|
+
path: dist/
|
|
51
|
+
|
|
52
|
+
- name: Install uv
|
|
53
|
+
uses: astral-sh/setup-uv@v4
|
|
54
|
+
|
|
55
|
+
- name: Publish to PyPI
|
|
56
|
+
run: uv publish --trusted-publishing always
|
parx-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
dist/
|
|
6
|
+
build/
|
|
7
|
+
.eggs/
|
|
8
|
+
*.egg
|
|
9
|
+
.venv/
|
|
10
|
+
venv/
|
|
11
|
+
.env
|
|
12
|
+
|
|
13
|
+
# uv
|
|
14
|
+
.uv/
|
|
15
|
+
uv.lock # commit this if you want locked Python deps, otherwise ignore
|
|
16
|
+
|
|
17
|
+
# Pytest / coverage
|
|
18
|
+
.pytest_cache/
|
|
19
|
+
.coverage
|
|
20
|
+
htmlcov/
|
|
21
|
+
|
|
22
|
+
# Ruff / mypy
|
|
23
|
+
.ruff_cache/
|
|
24
|
+
.mypy_cache/
|
|
25
|
+
|
|
26
|
+
# Julia
|
|
27
|
+
# Do NOT ignore Manifest.toml — it should be committed for reproducibility
|
|
28
|
+
*.jl.cov
|
|
29
|
+
*.jl.mem
|
|
30
|
+
/depot/
|
|
31
|
+
|
|
32
|
+
# Local packaging/release working notes — not meant for the public repo
|
|
33
|
+
/road2publish.md
|
|
34
|
+
|
|
35
|
+
# Claude Code
|
|
36
|
+
.claude/
|
|
37
|
+
CLAUDE.md
|
|
38
|
+
memory/
|
|
39
|
+
|
|
40
|
+
# Examples output
|
|
41
|
+
examples/output/
|
|
42
|
+
|
|
43
|
+
# Editors
|
|
44
|
+
.vscode/
|
|
45
|
+
.idea/
|
|
46
|
+
*.swp
|
|
47
|
+
*.swo
|
|
48
|
+
.DS_Store
|
|
49
|
+
|
|
50
|
+
# Jupyter (if you add notebooks later)
|
|
51
|
+
.ipynb_checkpoints/
|
|
52
|
+
*.ipynb
|
|
53
|
+
|
|
54
|
+
# marimo
|
|
55
|
+
__marimo__/
|
|
56
|
+
.marimo_agent_state.json
|
|
57
|
+
layouts/
|
|
58
|
+
project_overview.py
|
|
59
|
+
|
|
60
|
+
# MkDocs
|
|
61
|
+
site/
|
parx-0.1.0/CHANGELOG.md
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html)
|
|
7
|
+
once it reaches 1.0.
|
|
8
|
+
|
|
9
|
+
## [Unreleased]
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
- `pyproject.toml` referenced a nonexistent `LICENSE` file while the tracked
|
|
13
|
+
`LICENCE` file was empty; replaced with a real MIT `LICENSE` and the modern
|
|
14
|
+
SPDX `license`/`license-files` metadata form.
|
|
15
|
+
- The built sdist/wheel silently omitted all Julia source files
|
|
16
|
+
(`src/parx/julia/src/*.jl`) because `package-data`'s `julia/*` glob does not
|
|
17
|
+
recurse into the nested `julia/src/` directory. Anyone installing from a
|
|
18
|
+
built artifact (rather than an editable/source checkout) would have hit a
|
|
19
|
+
hard failure on first import. Fixed by adding `julia/src/*` to
|
|
20
|
+
`package-data`.
|
|
21
|
+
- README referenced a `parx[analysis]` extra that did not exist in
|
|
22
|
+
`pyproject.toml`; added it (`scikit-learn`, used by `plot_partition_pca`).
|
|
23
|
+
- Stale/placeholder content in `CONTRIBUTING.md` (clone URL, test file
|
|
24
|
+
listing) and a broken `LICENCE` link in `README.md`.
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
- `LICENSE` (MIT).
|
|
28
|
+
- `CITATION.cff` for GitHub's citation button and future Zenodo archival.
|
|
29
|
+
- CI/docs/license/Python-version badges in `README.md`.
|
|
30
|
+
- `road2publish.md`, tracking the path to a PyPI release and Zenodo DOI.
|
|
31
|
+
|
|
32
|
+
### Changed
|
|
33
|
+
- Untracked marimo/layout tool artifacts (`.marimo_agent_state.json`,
|
|
34
|
+
`__marimo__/session/*.json`, `layouts/*.json`, `project_overview.py`) that
|
|
35
|
+
don't belong in the package; they remain on disk but are now gitignored.
|
parx-0.1.0/CITATION.cff
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
cff-version: 1.2.0
|
|
2
|
+
message: "If you use this software, please cite it as below."
|
|
3
|
+
title: "parx: Polyhedral Affine Region eXplorer"
|
|
4
|
+
abstract: >-
|
|
5
|
+
Exact enumeration and analysis of the linear (polyhedral activation)
|
|
6
|
+
regions of ReLU neural networks. A Python API layered over a Julia
|
|
7
|
+
backend for the combinatorially intensive region-finding algorithms.
|
|
8
|
+
type: software
|
|
9
|
+
version: 0.1.0
|
|
10
|
+
date-released: "2026-07-03"
|
|
11
|
+
license: MIT
|
|
12
|
+
repository-code: "https://github.com/Johanmkr/parx"
|
|
13
|
+
url: "https://github.com/Johanmkr/parx"
|
|
14
|
+
authors:
|
|
15
|
+
- family-names: Mylius-Kroken
|
|
16
|
+
given-names: Johan
|
|
17
|
+
email: johan.m.kroken@gmail.com
|
|
18
|
+
orcid: https://orcid.org/0009-0005-8580-372X
|
|
19
|
+
keywords:
|
|
20
|
+
- neural networks
|
|
21
|
+
- linear regions
|
|
22
|
+
- polyhedral
|
|
23
|
+
- ReLU
|
|
24
|
+
- piecewise linear
|
|
25
|
+
- affine
|
|
26
|
+
- interpretability
|
|
27
|
+
- geometry
|
|
28
|
+
# identifiers:
|
|
29
|
+
# - type: doi
|
|
30
|
+
# value: 10.5281/zenodo.XXXXXXX
|
|
31
|
+
# description: Zenodo archive of this repository (added once a DOI exists — see road2publish.md Phase 4)
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
# Contributing to parx
|
|
2
|
+
|
|
3
|
+
## Prerequisites
|
|
4
|
+
|
|
5
|
+
- Python ≥ 3.10
|
|
6
|
+
- Julia ≥ 1.10 (install via [juliaup](https://github.com/JuliaLang/juliaup))
|
|
7
|
+
- [uv](https://github.com/astral-sh/uv) (recommended) or pip
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Setting Up the Development Environment
|
|
12
|
+
|
|
13
|
+
### 1. Clone the repository
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
git clone https://github.com/Johanmkr/parx
|
|
17
|
+
cd parx
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
### 2. Create a virtual environment and install dependencies
|
|
21
|
+
|
|
22
|
+
**With uv (recommended):**
|
|
23
|
+
```bash
|
|
24
|
+
uv venv # creates .venv/
|
|
25
|
+
source .venv/bin/activate # Windows: .venv\Scripts\activate
|
|
26
|
+
uv pip install -e ".[dev]"
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
**With pip:**
|
|
30
|
+
```bash
|
|
31
|
+
python -m venv .venv
|
|
32
|
+
source .venv/bin/activate
|
|
33
|
+
pip install -e ".[dev]"
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### 3. Verify the setup
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pytest
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
All tests should pass.
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Project Structure
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
parx/
|
|
50
|
+
├── pyproject.toml # build config, dependencies, tool config
|
|
51
|
+
├── README.md
|
|
52
|
+
├── CONTRIBUTING.md
|
|
53
|
+
├── LICENSE
|
|
54
|
+
├── .github/
|
|
55
|
+
│ └── workflows/
|
|
56
|
+
│ ├── ci.yml # GitHub Actions CI
|
|
57
|
+
│ └── docs.yml # builds & deploys the docs site to GitHub Pages
|
|
58
|
+
├── mkdocs.yml # docs site config
|
|
59
|
+
├── docs/ # docs site content (see "Docs site" below)
|
|
60
|
+
├── tests/
|
|
61
|
+
│ ├── conftest.py
|
|
62
|
+
│ ├── test_analysis.py
|
|
63
|
+
│ ├── test_diagnostics.py
|
|
64
|
+
│ ├── test_io.py
|
|
65
|
+
│ ├── test_io_partition.py
|
|
66
|
+
│ ├── test_julia_bridge.py
|
|
67
|
+
│ ├── test_methods.py
|
|
68
|
+
│ ├── test_mlp.py
|
|
69
|
+
│ ├── test_network.py
|
|
70
|
+
│ ├── test_partition.py
|
|
71
|
+
│ ├── test_verify.py
|
|
72
|
+
│ └── test_viz.py
|
|
73
|
+
└── src/
|
|
74
|
+
└── parx/
|
|
75
|
+
├── __init__.py # public API surface
|
|
76
|
+
├── _check.py # startup checks (Julia on PATH, etc.)
|
|
77
|
+
├── _julia_init.py # Julia runtime initialization
|
|
78
|
+
├── _lp.py # Chebyshev center LP helper
|
|
79
|
+
├── network.py # network loading (.pth, .h5)
|
|
80
|
+
├── region.py # Region dataclass
|
|
81
|
+
├── partition.py # Partition object + halfspaces/route/filter
|
|
82
|
+
├── methods/ # region-finding backends
|
|
83
|
+
│ ├── __init__.py
|
|
84
|
+
│ ├── sparse_julia.py
|
|
85
|
+
│ ├── exact_julia.py
|
|
86
|
+
│ ├── exact_julia_fast.py
|
|
87
|
+
│ ├── sparse_python.py
|
|
88
|
+
│ └── exact_python.py
|
|
89
|
+
├── io.py # iter_state_dicts helper
|
|
90
|
+
├── verify.py # overlap/coverage checks
|
|
91
|
+
├── viz.py # Plotly (default) / matplotlib visualizations
|
|
92
|
+
├── juliapkg.json # Julia runtime dependencies (for juliacall/juliapkg)
|
|
93
|
+
└── julia/
|
|
94
|
+
├── LinearRegions.jl
|
|
95
|
+
├── bridge.jl
|
|
96
|
+
├── sparse.jl
|
|
97
|
+
├── exact.jl
|
|
98
|
+
├── lp.jl
|
|
99
|
+
├── Project.toml # standalone Julia environment (for direct Julia testing)
|
|
100
|
+
└── Manifest.toml # locked deps for standalone environment
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## Development Workflow
|
|
106
|
+
|
|
107
|
+
### Running tests
|
|
108
|
+
```bash
|
|
109
|
+
pytest # run all tests
|
|
110
|
+
pytest tests/test_methods.py # run a specific file
|
|
111
|
+
pytest -x # stop on first failure
|
|
112
|
+
pytest --cov=parx # with coverage
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Linting and formatting
|
|
116
|
+
```bash
|
|
117
|
+
ruff check src/ tests/ # lint
|
|
118
|
+
ruff format src/ tests/ # format
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Testing the Julia code independently
|
|
122
|
+
|
|
123
|
+
You can test Julia code directly without going through Python:
|
|
124
|
+
```bash
|
|
125
|
+
cd src/parx/julia
|
|
126
|
+
julia --project=. -e "using LinearRegions; LinearRegions.run_tests()"
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## Docs site
|
|
132
|
+
|
|
133
|
+
The docs site (published at [johanmkr.github.io/parx](https://johanmkr.github.io/parx/)) is built with MkDocs from `docs/` and deployed by `.github/workflows/docs.yml` whenever `docs/` or `mkdocs.yml` changes on `main`.
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
uv pip install -e ".[docs]"
|
|
137
|
+
mkdocs serve # live preview at http://127.0.0.1:8000
|
|
138
|
+
mkdocs build --strict # what CI runs; fails on broken nav/links
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
The three demo notebooks are embedded on the Notebooks page as pre-exported static HTML (`docs/notebooks/demo_plt.html`, `docs/notebooks/demo_plotly.html`, `docs/notebooks/feature_embedding.html`) — the docs build itself has no Julia/PyTorch dependency, so this export step is manual. **After editing any of `notebooks/demo_plt.py`, `notebooks/demo_plotly.py`, or `notebooks/feature_embedding.py`, re-export before committing:**
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
marimo export html notebooks/demo_plt.py -o docs/notebooks/demo_plt.html
|
|
145
|
+
marimo export html notebooks/demo_plotly.py -o docs/notebooks/demo_plotly.html
|
|
146
|
+
marimo export html notebooks/feature_embedding.py -o docs/notebooks/feature_embedding.html
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## Adding Julia Dependencies
|
|
152
|
+
|
|
153
|
+
parx uses two separate Julia environments:
|
|
154
|
+
|
|
155
|
+
- **Runtime environment** — managed by juliacall/juliapkg, stored in `.venv/julia_env`.
|
|
156
|
+
Declare new packages in `src/parx/juliapkg.json`. juliacall resolves and installs them
|
|
157
|
+
automatically on the next `import parx`. No manual `Pkg.add` needed.
|
|
158
|
+
|
|
159
|
+
- **Standalone testing environment** — `src/parx/julia/Project.toml`. Used only for
|
|
160
|
+
running Julia code directly (see "Testing the Julia code independently" above).
|
|
161
|
+
Add packages here with:
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
cd src/parx/julia
|
|
165
|
+
julia --project=.
|
|
166
|
+
```
|
|
167
|
+
```julia
|
|
168
|
+
using Pkg
|
|
169
|
+
Pkg.add("SomePackage") # updates Project.toml and Manifest.toml
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Commit both `Project.toml` and `Manifest.toml` when changing the standalone environment.
|
|
173
|
+
|
|
174
|
+
For packages needed at runtime, edit `src/parx/juliapkg.json` and commit that file.
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## Environment Variables
|
|
179
|
+
|
|
180
|
+
| Variable | Default | Description |
|
|
181
|
+
|---|---|---|
|
|
182
|
+
| `JULIA_NUM_THREADS` | `"auto"` | Number of Julia threads |
|
|
183
|
+
| `PYTHON_JULIACALL_HANDLE_SIGNALS` | `"yes"` | Makes the harmless segfault at exit (when Julia threads are active) happen cleanly after results are already reported, instead of during teardown. It does not prevent the segfault itself — if you see one, check the run's actual output/exit status first; a `"N passed"` line means the run succeeded despite it. |
|
|
184
|
+
| `PYTHON_JULIAPKG_EXE` | auto-detected on juliaup (see below) | Force juliacall to use a specific Julia binary instead of auto-resolving one. |
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## Troubleshooting
|
|
189
|
+
|
|
190
|
+
### `ERROR: could not load library ".../juliaup/bin/../lib/julia/sys.so": ... No such file or directory`
|
|
191
|
+
|
|
192
|
+
This used to happen on the **first** call that touches Julia (`ensure_julia()`, any `*_julia`/`exact_julia*` method, or the `julia_session` pytest fixture) on a machine where Julia was installed via [juliaup](https://github.com/JuliaLang/juliaup) — i.e. exactly the install method this repo recommends. **`parx` now works around it automatically** (`_julia_init.py::_resolve_juliaup_shim`) — read on for what it does and what to do if you still hit this.
|
|
193
|
+
|
|
194
|
+
**Cause:** `julia` on `PATH` is juliaup's launcher shim (`~/.juliaup/bin/julia`), not a real Julia install — juliaup keeps the actual per-version binaries elsewhere (`~/.julia/juliaup/julia-<version>+.../`). `juliapkg` tries to auto-upgrade to the newest Julia release on every resolve; when that opportunistic install fails (network hiccup, a not-yet-fully-available release, etc.) it silently falls back to the raw shim path instead of one of your already-installed, perfectly good Julia versions, and juliacall then derives the system-image path relative to the shim's directory — which never has a `lib/julia/sys.so` next to it. This is a `juliapkg` bug, not a `parx` one; it's invisible in CI because CI installs Julia directly (`julia-actions/setup-julia@v2`), never through juliaup.
|
|
195
|
+
|
|
196
|
+
**The automatic fix:** before `juliacall` is ever imported, `ensure_julia()` checks whether `julia` on `PATH` resolves to a juliaup launcher shim (`os.path.realpath` ends in `julialauncher`). If so, it reads `~/.julia/juliaup/juliaup.json` directly to find the real binary behind juliaup's *default* channel, and sets `PYTHON_JULIAPKG_EXE` to that real path (via `os.environ.setdefault`, so it never overrides an explicit value you've already set) — juliapkg then uses that binary directly instead of falling back to the broken shim path. Confirmed end-to-end: a from-scratch clone + `uv venv` + `pip install -e ".[dev]"` + `pytest`, with **zero manual env vars**, now passes cleanly (`219 passed`).
|
|
197
|
+
|
|
198
|
+
**If it still happens anyway** (e.g. juliaup's on-disk metadata format has changed since this was written, or you're using something other than juliaup — a container image with a hand-rolled Julia install, `asdf`, etc.), the detection silently no-ops and you're back to the original failure. Fall back to pointing juliacall at a known-good binary yourself:
|
|
199
|
+
|
|
200
|
+
```bash
|
|
201
|
+
juliaup list # see what's installed, e.g. 1.10.11+0.x64.linux.gnu
|
|
202
|
+
export PYTHON_JULIAPKG_EXE="$HOME/.julia/juliaup/julia-1.10.11+0.x64.linux.gnu/bin/julia"
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
(matching the Julia 1.10 that CI and `src/parx/julia/Manifest.toml` are pinned to is the safest bet — a newer version may work but isn't what's tested). Then delete any stale resolution and retry: `rm -rf .venv/julia_env`. If you had to do this, please open an issue — it means the auto-detection missed a case and should be taught about it.
|
|
206
|
+
|
|
207
|
+
If you'd rather not hardcode a version path, `export PYTHON_JULIAPKG_OFFLINE=yes` also avoids the buggy auto-upgrade path (it makes juliapkg reuse the newest **already-installed** juliaup version instead of trying to fetch a new one) — simpler, but it may still land you on a newer, less-tested Julia than pinning to 1.10 would.
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
## Pull Request Checklist
|
|
212
|
+
|
|
213
|
+
- [ ] Tests pass (`pytest`)
|
|
214
|
+
- [ ] Code is formatted (`ruff format`)
|
|
215
|
+
- [ ] No lint errors (`ruff check`)
|
|
216
|
+
- [ ] New functionality has tests
|
|
217
|
+
- [ ] `Manifest.toml` is committed if Julia deps changed
|
parx-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Johan Mylius-Kroken
|
|
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.
|