locpick 0.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.
- locpick-0.0.0/.coveragerc +8 -0
- locpick-0.0.0/.github/release.yml +22 -0
- locpick-0.0.0/.github/workflows/build_docs.yml +106 -0
- locpick-0.0.0/.github/workflows/lint.yml +22 -0
- locpick-0.0.0/.github/workflows/spec1.yml +126 -0
- locpick-0.0.0/.github/workflows/unittests.yml +82 -0
- locpick-0.0.0/.github/workflows/upload_package.yml +51 -0
- locpick-0.0.0/.gitignore +95 -0
- locpick-0.0.0/.pre-commit-config.yaml +13 -0
- locpick-0.0.0/LICENSE +27 -0
- locpick-0.0.0/PKG-INFO +105 -0
- locpick-0.0.0/README.md +30 -0
- locpick-0.0.0/ci/312-latest.yml +44 -0
- locpick-0.0.0/ci/312-oldest.yml +34 -0
- locpick-0.0.0/ci/313-latest.yml +44 -0
- locpick-0.0.0/ci/314-latest.yml +33 -0
- locpick-0.0.0/codecov.yml +4 -0
- locpick-0.0.0/docs/.gitignore +1 -0
- locpick-0.0.0/docs/Makefile +22 -0
- locpick-0.0.0/docs/docs_env.yml +49 -0
- locpick-0.0.0/docs/source/_static/2ed978e7d8105199423735a8254a30a7.svg +47 -0
- locpick-0.0.0/docs/source/_static/5291332ea831bd1aeca31c499f8025dc.svg +32 -0
- locpick-0.0.0/docs/source/_static/custom.css +52 -0
- locpick-0.0.0/docs/source/_static/lokey.png +0 -0
- locpick-0.0.0/docs/source/_static/lokey2.png +0 -0
- locpick-0.0.0/docs/source/_static/lokey3.png +0 -0
- locpick-0.0.0/docs/source/_static/references.bib +444 -0
- locpick-0.0.0/docs/source/_static/roundabout-black.svg +6 -0
- locpick-0.0.0/docs/source/_static/roundabout-white.svg +6 -0
- locpick-0.0.0/docs/source/_static/roundabout.png +0 -0
- locpick-0.0.0/docs/source/_static/roundabout.svg +29 -0
- locpick-0.0.0/docs/source/api.rst +149 -0
- locpick-0.0.0/docs/source/conf.py +164 -0
- locpick-0.0.0/docs/source/index.md +47 -0
- locpick-0.0.0/docs/source/installation.md +42 -0
- locpick-0.0.0/docs/source/references.md +5 -0
- locpick-0.0.0/docs/source/user-guide/choicetable.md +57 -0
- locpick-0.0.0/docs/source/user-guide/distance.md +15 -0
- locpick-0.0.0/docs/source/user-guide/inference.md +96 -0
- locpick-0.0.0/docs/source/user-guide/livelike_locpick_household_tract_demo.ipynb +570 -0
- locpick-0.0.0/docs/source/user-guide/mixed.md +26 -0
- locpick-0.0.0/docs/source/user-guide/mnl.md +34 -0
- locpick-0.0.0/docs/source/user-guide/modelspec.md +67 -0
- locpick-0.0.0/docs/source/user-guide/nested.md +29 -0
- locpick-0.0.0/docs/source/user-guide/prediction.md +30 -0
- locpick-0.0.0/docs/source/user-guide/sampling.md +23 -0
- locpick-0.0.0/docs/source/user-guide/sar_mnl.md +186 -0
- locpick-0.0.0/docs/source/user-guide/sar_mnl_demo.ipynb +434 -0
- locpick-0.0.0/docs/source/user-guide/spatial_mixed.md +112 -0
- locpick-0.0.0/docs/source/user-guide/spatial_mixed_nested.md +110 -0
- locpick-0.0.0/docs/source/user-guide/spatial_models_demo.ipynb +414 -0
- locpick-0.0.0/docs/source/user-guide/spatial_models_lag_demo.ipynb +524 -0
- locpick-0.0.0/docs/source/user-guide/spatial_nested.md +69 -0
- locpick-0.0.0/docs/update_version_json.py +40 -0
- locpick-0.0.0/docs/versions.json +3 -0
- locpick-0.0.0/environment.yml +48 -0
- locpick-0.0.0/locpick/__init__.py +27 -0
- locpick-0.0.0/locpick/__init__.pyi +153 -0
- locpick-0.0.0/locpick/_jax/__init__.py +55 -0
- locpick-0.0.0/locpick/_jax/__init__.pyi +62 -0
- locpick-0.0.0/locpick/_jax/builders.py +1607 -0
- locpick-0.0.0/locpick/_jax/data.py +361 -0
- locpick-0.0.0/locpick/_jax/diag_precompute.py +516 -0
- locpick-0.0.0/locpick/_jax/kernels.py +905 -0
- locpick-0.0.0/locpick/_jax/objective.py +296 -0
- locpick-0.0.0/locpick/_jax/sar_kernels.py +1008 -0
- locpick-0.0.0/locpick/_jax/sparse_backends.py +232 -0
- locpick-0.0.0/locpick/_jax/sparse_solve.py +429 -0
- locpick-0.0.0/locpick/_jax/transforms.py +275 -0
- locpick-0.0.0/locpick/_kernels/__init__.py +14 -0
- locpick-0.0.0/locpick/_kernels/__init__.pyi +20 -0
- locpick-0.0.0/locpick/_kernels/constants.py +31 -0
- locpick-0.0.0/locpick/_kernels/mnl_numpy.py +232 -0
- locpick-0.0.0/locpick/_kernels/sar_mnl_numpy.py +259 -0
- locpick-0.0.0/locpick/_sampling/__init__.py +9 -0
- locpick-0.0.0/locpick/_sampling/__init__.pyi +33 -0
- locpick-0.0.0/locpick/_sampling/correction.py +75 -0
- locpick-0.0.0/locpick/_sampling/inclusion.py +130 -0
- locpick-0.0.0/locpick/_sampling/kernels.py +441 -0
- locpick-0.0.0/locpick/_solvers/__init__.py +12 -0
- locpick-0.0.0/locpick/_solvers/__init__.pyi +39 -0
- locpick-0.0.0/locpick/_solvers/lbfgs.py +165 -0
- locpick-0.0.0/locpick/_solvers/optax.py +255 -0
- locpick-0.0.0/locpick/_solvers/optimagic.py +164 -0
- locpick-0.0.0/locpick/_solvers/optimistix.py +479 -0
- locpick-0.0.0/locpick/_solvers/protocol.py +171 -0
- locpick-0.0.0/locpick/_solvers/trust_ncg.py +151 -0
- locpick-0.0.0/locpick/data/__init__.py +45 -0
- locpick-0.0.0/locpick/data/__init__.pyi +60 -0
- locpick-0.0.0/locpick/data/arrays.py +128 -0
- locpick-0.0.0/locpick/data/choicetable.py +1118 -0
- locpick-0.0.0/locpick/data/dataset.py +330 -0
- locpick-0.0.0/locpick/data/distance.py +260 -0
- locpick-0.0.0/locpick/data/format.py +302 -0
- locpick-0.0.0/locpick/data/problem.py +265 -0
- locpick-0.0.0/locpick/dgp.py +2055 -0
- locpick-0.0.0/locpick/models/__init__.py +17 -0
- locpick-0.0.0/locpick/models/__init__.pyi +36 -0
- locpick-0.0.0/locpick/models/_spatial.py +185 -0
- locpick-0.0.0/locpick/models/_spatial_weights.py +126 -0
- locpick-0.0.0/locpick/models/base.py +858 -0
- locpick-0.0.0/locpick/models/choice_model.py +2258 -0
- locpick-0.0.0/locpick/models/mixed.py +621 -0
- locpick-0.0.0/locpick/models/nested.py +357 -0
- locpick-0.0.0/locpick/models/scl.py +276 -0
- locpick-0.0.0/locpick/py.typed +0 -0
- locpick-0.0.0/locpick/results/__init__.py +14 -0
- locpick-0.0.0/locpick/results/__init__.pyi +35 -0
- locpick-0.0.0/locpick/results/diagnostics.py +713 -0
- locpick-0.0.0/locpick/results/fit_result.py +268 -0
- locpick-0.0.0/locpick/spec/__init__.py +9 -0
- locpick-0.0.0/locpick/spec/__init__.pyi +14 -0
- locpick-0.0.0/locpick/spec/model_spec.py +330 -0
- locpick-0.0.0/locpick/spec/terms.py +107 -0
- locpick-0.0.0/locpick.egg-info/PKG-INFO +105 -0
- locpick-0.0.0/locpick.egg-info/SOURCES.txt +142 -0
- locpick-0.0.0/locpick.egg-info/dependency_links.txt +1 -0
- locpick-0.0.0/locpick.egg-info/requires.txt +60 -0
- locpick-0.0.0/locpick.egg-info/top_level.txt +1 -0
- locpick-0.0.0/pyproject.toml +131 -0
- locpick-0.0.0/setup.cfg +4 -0
- locpick-0.0.0/tests/README.md +1 -0
- locpick-0.0.0/tests/test_backend_kernel_interfaces.py +188 -0
- locpick-0.0.0/tests/test_choicetable_xarray.py +548 -0
- locpick-0.0.0/tests/test_diag_precompute.py +105 -0
- locpick-0.0.0/tests/test_diagnostics.py +306 -0
- locpick-0.0.0/tests/test_estimation_problem.py +308 -0
- locpick-0.0.0/tests/test_inference.py +351 -0
- locpick-0.0.0/tests/test_marginal_effects_wtp.py +372 -0
- locpick-0.0.0/tests/test_mixed_nested.py +207 -0
- locpick-0.0.0/tests/test_mnl.py +1928 -0
- locpick-0.0.0/tests/test_modelspec_v2.py +214 -0
- locpick-0.0.0/tests/test_nested_and_mixed.py +1150 -0
- locpick-0.0.0/tests/test_param_recovery.py +531 -0
- locpick-0.0.0/tests/test_prediction_all_models.py +502 -0
- locpick-0.0.0/tests/test_prediction_simulation.py +323 -0
- locpick-0.0.0/tests/test_qmc_draws.py +155 -0
- locpick-0.0.0/tests/test_sampling.py +718 -0
- locpick-0.0.0/tests/test_sar_mnl.py +747 -0
- locpick-0.0.0/tests/test_solvers.py +284 -0
- locpick-0.0.0/tests/test_sparse_backends.py +156 -0
- locpick-0.0.0/tests/test_sparse_solve_vjp.py +181 -0
- locpick-0.0.0/tests/test_spatial_diagnostics.py +319 -0
- locpick-0.0.0/tests/test_spatial_models.py +1523 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
changelog:
|
|
2
|
+
exclude:
|
|
3
|
+
labels:
|
|
4
|
+
- ignore-for-release
|
|
5
|
+
authors:
|
|
6
|
+
- dependabot
|
|
7
|
+
categories:
|
|
8
|
+
- title: Breaking Changes
|
|
9
|
+
labels:
|
|
10
|
+
- breaking
|
|
11
|
+
- title: New Features
|
|
12
|
+
labels:
|
|
13
|
+
- enhancement
|
|
14
|
+
- title: Bug Fixes
|
|
15
|
+
labels:
|
|
16
|
+
- bug
|
|
17
|
+
- title: Documentation
|
|
18
|
+
labels:
|
|
19
|
+
- documentation
|
|
20
|
+
- title: Other Changes
|
|
21
|
+
labels:
|
|
22
|
+
- "*"
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Build Docs
|
|
3
|
+
|
|
4
|
+
on:
|
|
5
|
+
push:
|
|
6
|
+
branches: [dev]
|
|
7
|
+
tags:
|
|
8
|
+
- "v*"
|
|
9
|
+
pull_request:
|
|
10
|
+
branches:
|
|
11
|
+
- "*"
|
|
12
|
+
workflow_dispatch:
|
|
13
|
+
inputs:
|
|
14
|
+
version:
|
|
15
|
+
description: Manual Doc Build
|
|
16
|
+
default: run-doc-build
|
|
17
|
+
required: false
|
|
18
|
+
|
|
19
|
+
concurrency:
|
|
20
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
21
|
+
cancel-in-progress: true
|
|
22
|
+
|
|
23
|
+
jobs:
|
|
24
|
+
docs:
|
|
25
|
+
name: Build & push docs
|
|
26
|
+
runs-on: ${{ matrix.os }}
|
|
27
|
+
timeout-minutes: 90
|
|
28
|
+
strategy:
|
|
29
|
+
matrix:
|
|
30
|
+
os: ["ubuntu-latest"]
|
|
31
|
+
environment-file: [docs/docs_env.yml]
|
|
32
|
+
experimental: [false]
|
|
33
|
+
defaults:
|
|
34
|
+
run:
|
|
35
|
+
shell: bash -l {0}
|
|
36
|
+
|
|
37
|
+
steps:
|
|
38
|
+
- name: Checkout repo
|
|
39
|
+
uses: actions/checkout@v4
|
|
40
|
+
with:
|
|
41
|
+
fetch-depth: 0
|
|
42
|
+
|
|
43
|
+
- name: Setup micromamba
|
|
44
|
+
uses: mamba-org/setup-micromamba@v2
|
|
45
|
+
with:
|
|
46
|
+
environment-file: ${{ matrix.environment-file }}
|
|
47
|
+
micromamba-version: "latest"
|
|
48
|
+
|
|
49
|
+
- name: Install package
|
|
50
|
+
run: pip install . --no-deps
|
|
51
|
+
|
|
52
|
+
- name: Make docs
|
|
53
|
+
run: cd docs; make html
|
|
54
|
+
env:
|
|
55
|
+
CENSUS: ${{ secrets.CENSUS }}
|
|
56
|
+
|
|
57
|
+
- name: Zip documentation
|
|
58
|
+
if: ${{ github.event_name == 'pull_request' }}
|
|
59
|
+
run: zip docs_artifact.zip docs/build/html -r
|
|
60
|
+
|
|
61
|
+
- name: Upload artifact
|
|
62
|
+
if: ${{ github.event_name == 'pull_request' }}
|
|
63
|
+
uses: actions/upload-artifact@v4
|
|
64
|
+
with:
|
|
65
|
+
name: docs_artifact
|
|
66
|
+
path: docs_artifact.zip
|
|
67
|
+
|
|
68
|
+
- name: Publish to Github Pages on dev (dev)
|
|
69
|
+
if: ${{ github.ref == 'refs/heads/dev' }}
|
|
70
|
+
uses: peaceiris/actions-gh-pages@v4
|
|
71
|
+
with:
|
|
72
|
+
github_token: ${{ secrets.GITHUB_TOKEN }}
|
|
73
|
+
publish_dir: docs/build/html/
|
|
74
|
+
destination_dir: dev
|
|
75
|
+
keep_files: false
|
|
76
|
+
|
|
77
|
+
- name: Publish to Github Pages on release (versioned)
|
|
78
|
+
if: ${{ github.ref_type == 'tag' }}
|
|
79
|
+
uses: peaceiris/actions-gh-pages@v4
|
|
80
|
+
with:
|
|
81
|
+
github_token: ${{ secrets.GITHUB_TOKEN }}
|
|
82
|
+
publish_dir: docs/build/html/
|
|
83
|
+
destination_dir: ${{ github.ref_name }}
|
|
84
|
+
|
|
85
|
+
- name: Publish to Github Pages on release (stable)
|
|
86
|
+
if: ${{ github.ref_type == 'tag' }}
|
|
87
|
+
uses: peaceiris/actions-gh-pages@v4
|
|
88
|
+
with:
|
|
89
|
+
github_token: ${{ secrets.GITHUB_TOKEN }}
|
|
90
|
+
publish_dir: docs/build/html/
|
|
91
|
+
destination_dir: stable
|
|
92
|
+
keep_files: false
|
|
93
|
+
|
|
94
|
+
- name: Update the versions switcher
|
|
95
|
+
if: ${{ github.ref_type == 'tag' }}
|
|
96
|
+
run: |
|
|
97
|
+
git checkout gh-pages
|
|
98
|
+
git fetch
|
|
99
|
+
git pull
|
|
100
|
+
git config --global user.name 'github-actions[bot]'
|
|
101
|
+
git config --global user.email '41898282+github-actions[bot]@users.noreply.github.com'
|
|
102
|
+
python docs/update_version_json.py --version ${{ github.ref_name }}
|
|
103
|
+
git add versions.json
|
|
104
|
+
git commit -m "Update versions switcher"
|
|
105
|
+
git push
|
|
106
|
+
git checkout dev
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Lint
|
|
3
|
+
|
|
4
|
+
on:
|
|
5
|
+
push:
|
|
6
|
+
branches: [dev]
|
|
7
|
+
pull_request:
|
|
8
|
+
branches:
|
|
9
|
+
- "*"
|
|
10
|
+
workflow_dispatch:
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
ruff:
|
|
14
|
+
name: Ruff (lint + format check)
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
- uses: astral-sh/ruff-action@v3
|
|
19
|
+
with:
|
|
20
|
+
version: "0.15.11"
|
|
21
|
+
- run: ruff check .
|
|
22
|
+
- run: ruff format --check .
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
name: SPEC 1 Compliance
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [dev]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches:
|
|
8
|
+
- "*"
|
|
9
|
+
workflow_dispatch:
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
spec1:
|
|
13
|
+
name: Check SPEC 1 (lazy loading)
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
|
|
18
|
+
- uses: actions/setup-python@v5
|
|
19
|
+
with:
|
|
20
|
+
python-version: "3.x"
|
|
21
|
+
|
|
22
|
+
- name: Check SPEC 1 compliance
|
|
23
|
+
run: |
|
|
24
|
+
python - <<'SCRIPT'
|
|
25
|
+
import re
|
|
26
|
+
import sys
|
|
27
|
+
from pathlib import Path
|
|
28
|
+
|
|
29
|
+
# ── Auto-detect package name from pyproject.toml ────────────────
|
|
30
|
+
pyproject = Path("pyproject.toml")
|
|
31
|
+
if not pyproject.is_file():
|
|
32
|
+
sys.exit("❌ No pyproject.toml found in repo root")
|
|
33
|
+
pyproject_text = pyproject.read_text()
|
|
34
|
+
m = re.search(r'^name\s*=\s*"([^"]+)"', pyproject_text, re.MULTILINE)
|
|
35
|
+
if not m:
|
|
36
|
+
sys.exit("❌ Could not find [project] name in pyproject.toml")
|
|
37
|
+
pkg_name = m.group(1).replace("-", "_")
|
|
38
|
+
|
|
39
|
+
# ── Auto-detect package directory (flat or src layout) ─────────
|
|
40
|
+
PKG = None
|
|
41
|
+
for candidate in [Path(pkg_name), Path("src") / pkg_name]:
|
|
42
|
+
if candidate.is_dir() and (candidate / "__init__.py").is_file():
|
|
43
|
+
PKG = candidate
|
|
44
|
+
break
|
|
45
|
+
if PKG is None:
|
|
46
|
+
sys.exit(f"❌ Could not find package directory for '{pkg_name}'")
|
|
47
|
+
|
|
48
|
+
print(f"Package: {pkg_name} → {PKG}/\n")
|
|
49
|
+
errors = []
|
|
50
|
+
warnings = []
|
|
51
|
+
|
|
52
|
+
# ── 1. lazy_loader is a dependency ──────────────────────────────
|
|
53
|
+
if "lazy_loader" not in pyproject_text:
|
|
54
|
+
errors.append("lazy_loader not found in pyproject.toml dependencies")
|
|
55
|
+
|
|
56
|
+
# ── 2. Scan every __init__.py under the package ─────────────────
|
|
57
|
+
has_any_pyi = False
|
|
58
|
+
|
|
59
|
+
for init in sorted(PKG.rglob("__init__.py")):
|
|
60
|
+
rel = init.relative_to(PKG.parent)
|
|
61
|
+
src = init.read_text()
|
|
62
|
+
|
|
63
|
+
# Skip test directories
|
|
64
|
+
if any(part == "tests" or part.startswith("test_") for part in init.parts):
|
|
65
|
+
continue
|
|
66
|
+
|
|
67
|
+
uses_stub = "attach_stub" in src
|
|
68
|
+
uses_attach = bool(re.search(r"\.attach\s*\(", src)) and not uses_stub
|
|
69
|
+
uses_lazy = uses_stub or uses_attach
|
|
70
|
+
|
|
71
|
+
# 2a. If using lazy_loader, must set __getattr__, __dir__, __all__
|
|
72
|
+
if uses_lazy:
|
|
73
|
+
for attr in ("__getattr__", "__dir__", "__all__"):
|
|
74
|
+
if attr not in src:
|
|
75
|
+
errors.append(f"{rel}: lazy_loader used but {attr} not assigned")
|
|
76
|
+
|
|
77
|
+
# 2b. If using attach_stub, must have a .pyi sibling
|
|
78
|
+
if uses_stub:
|
|
79
|
+
pyi = init.with_suffix(".pyi")
|
|
80
|
+
has_any_pyi = True
|
|
81
|
+
if not pyi.is_file():
|
|
82
|
+
errors.append(f"{rel}: uses attach_stub but {pyi.name} is missing")
|
|
83
|
+
|
|
84
|
+
# 2c. No eager submodule imports alongside lazy loading
|
|
85
|
+
if uses_lazy:
|
|
86
|
+
for line in src.splitlines():
|
|
87
|
+
stripped = line.strip()
|
|
88
|
+
if stripped.startswith("#") or not stripped:
|
|
89
|
+
continue
|
|
90
|
+
if re.match(r"^from\s+\.\w+\s+import\s+", stripped):
|
|
91
|
+
if any(allow in stripped for allow in
|
|
92
|
+
["lazy_loader", "contextlib", "importlib"]):
|
|
93
|
+
continue
|
|
94
|
+
errors.append(
|
|
95
|
+
f"{rel}: eager import alongside lazy_loader: {stripped}"
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
# ── 3. If .pyi stubs exist, check py.typed and package-data ────
|
|
99
|
+
if has_any_pyi:
|
|
100
|
+
py_typed = PKG / "py.typed"
|
|
101
|
+
if not py_typed.is_file():
|
|
102
|
+
warnings.append(
|
|
103
|
+
f"{PKG}/py.typed is missing — required by PEP 561 when "
|
|
104
|
+
"shipping .pyi stubs"
|
|
105
|
+
)
|
|
106
|
+
if "*.pyi" not in pyproject_text:
|
|
107
|
+
warnings.append(
|
|
108
|
+
"pyproject.toml [tool.setuptools.package-data] should "
|
|
109
|
+
"include '*.pyi' and '**/*.pyi' for .pyi stubs to ship"
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
# ── Report ─────────────────────────────────────────────────────
|
|
113
|
+
if warnings:
|
|
114
|
+
print("⚠️ Warnings:")
|
|
115
|
+
for w in warnings:
|
|
116
|
+
print(f" • {w}")
|
|
117
|
+
print()
|
|
118
|
+
|
|
119
|
+
if errors:
|
|
120
|
+
print("❌ SPEC 1 compliance check FAILED:\n")
|
|
121
|
+
for e in errors:
|
|
122
|
+
print(f" • {e}")
|
|
123
|
+
sys.exit(1)
|
|
124
|
+
else:
|
|
125
|
+
print("✅ SPEC 1 compliance check passed")
|
|
126
|
+
SCRIPT
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Continuous Integration
|
|
3
|
+
|
|
4
|
+
on:
|
|
5
|
+
push:
|
|
6
|
+
branches: [dev]
|
|
7
|
+
pull_request:
|
|
8
|
+
branches:
|
|
9
|
+
- "*"
|
|
10
|
+
schedule:
|
|
11
|
+
- cron: "59 23 * * *"
|
|
12
|
+
workflow_dispatch:
|
|
13
|
+
inputs:
|
|
14
|
+
version:
|
|
15
|
+
description: Manual CI Run
|
|
16
|
+
default: test
|
|
17
|
+
required: false
|
|
18
|
+
|
|
19
|
+
jobs:
|
|
20
|
+
tests:
|
|
21
|
+
name: ${{ matrix.os }}, ${{ matrix.environment-file }}
|
|
22
|
+
runs-on: ${{ matrix.os }}
|
|
23
|
+
timeout-minutes: 90
|
|
24
|
+
strategy:
|
|
25
|
+
matrix:
|
|
26
|
+
os: [ubuntu-latest]
|
|
27
|
+
environment-file: [
|
|
28
|
+
ci/312-oldest.yml,
|
|
29
|
+
ci/312-latest.yml,
|
|
30
|
+
ci/313-latest.yml,
|
|
31
|
+
ci/314-latest.yml,
|
|
32
|
+
]
|
|
33
|
+
include:
|
|
34
|
+
- environment-file: ci/314-latest.yml
|
|
35
|
+
os: macos-13 # Intel
|
|
36
|
+
- environment-file: ci/314-latest.yml
|
|
37
|
+
os: macos-latest # Apple Silicon
|
|
38
|
+
- environment-file: ci/314-latest.yml
|
|
39
|
+
os: windows-latest
|
|
40
|
+
fail-fast: false
|
|
41
|
+
|
|
42
|
+
defaults:
|
|
43
|
+
run:
|
|
44
|
+
shell: bash -l {0}
|
|
45
|
+
|
|
46
|
+
steps:
|
|
47
|
+
- name: checkout repo
|
|
48
|
+
uses: actions/checkout@v4
|
|
49
|
+
with:
|
|
50
|
+
fetch-depth: 0
|
|
51
|
+
|
|
52
|
+
- name: setup micromamba
|
|
53
|
+
uses: mamba-org/setup-micromamba@v2
|
|
54
|
+
with:
|
|
55
|
+
environment-file: ${{ matrix.environment-file }}
|
|
56
|
+
micromamba-version: "latest"
|
|
57
|
+
|
|
58
|
+
- name: environment info
|
|
59
|
+
run: |
|
|
60
|
+
micromamba info
|
|
61
|
+
micromamba list
|
|
62
|
+
|
|
63
|
+
- name: install package
|
|
64
|
+
run: pip install -e . --no-deps
|
|
65
|
+
|
|
66
|
+
- name: run tests
|
|
67
|
+
run: |
|
|
68
|
+
pytest tests/ -m '' \
|
|
69
|
+
-n auto \
|
|
70
|
+
-v \
|
|
71
|
+
-r a \
|
|
72
|
+
--color yes \
|
|
73
|
+
--cov locpick \
|
|
74
|
+
--cov-append \
|
|
75
|
+
--cov-report term-missing \
|
|
76
|
+
--cov-report=xml .
|
|
77
|
+
|
|
78
|
+
- name: codecov
|
|
79
|
+
uses: codecov/codecov-action@v5
|
|
80
|
+
with:
|
|
81
|
+
token: ${{ secrets.CODECOV_TOKEN }}
|
|
82
|
+
files: ./coverage.xml
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Release & Publish
|
|
3
|
+
|
|
4
|
+
on:
|
|
5
|
+
push:
|
|
6
|
+
# Sequence of patterns matched against refs/tags
|
|
7
|
+
tags:
|
|
8
|
+
- "v*"
|
|
9
|
+
workflow_dispatch:
|
|
10
|
+
inputs:
|
|
11
|
+
version:
|
|
12
|
+
description: Manual Release
|
|
13
|
+
default: test
|
|
14
|
+
required: false
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
build:
|
|
18
|
+
name: Create release & publish to PyPI
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
permissions:
|
|
21
|
+
id-token: write # MANDATORY for trusted publishing to PyPI
|
|
22
|
+
contents: write # MANDATORY for the Github release action
|
|
23
|
+
|
|
24
|
+
steps:
|
|
25
|
+
- name: Checkout repo
|
|
26
|
+
uses: actions/checkout@v4
|
|
27
|
+
|
|
28
|
+
- name: Set up python
|
|
29
|
+
uses: actions/setup-python@v5
|
|
30
|
+
with:
|
|
31
|
+
python-version: "3.x"
|
|
32
|
+
|
|
33
|
+
- name: Install Dependencies
|
|
34
|
+
run: |
|
|
35
|
+
python -m pip install --upgrade pip
|
|
36
|
+
python -m pip install --upgrade build twine
|
|
37
|
+
python -m build
|
|
38
|
+
twine check --strict dist/*
|
|
39
|
+
|
|
40
|
+
- name: Create Release Notes
|
|
41
|
+
uses: actions/github-script@v7
|
|
42
|
+
with:
|
|
43
|
+
github-token: ${{secrets.GITHUB_TOKEN}}
|
|
44
|
+
script: |
|
|
45
|
+
await github.request(`POST /repos/${{ github.repository }}/releases`, {
|
|
46
|
+
tag_name: "${{ github.ref }}",
|
|
47
|
+
generate_release_notes: true
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
- name: Publish distribution 📦 to PyPI
|
|
51
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
locpick-0.0.0/.gitignore
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
benchmarks/
|
|
2
|
+
ref/
|
|
3
|
+
|
|
4
|
+
.copilot
|
|
5
|
+
.serena/
|
|
6
|
+
.cache
|
|
7
|
+
.ruff_cache
|
|
8
|
+
.pytest_cache
|
|
9
|
+
.vscode
|
|
10
|
+
refpy
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# Jupyter checkpoints
|
|
14
|
+
**/.ipynb_checkpoints
|
|
15
|
+
.pytest_cache/*
|
|
16
|
+
|
|
17
|
+
# Byte-compiled / optimized / DLL files
|
|
18
|
+
__pycache__/
|
|
19
|
+
*.py[cod]
|
|
20
|
+
|
|
21
|
+
# C extensions
|
|
22
|
+
*.so
|
|
23
|
+
|
|
24
|
+
# Distribution / packaging
|
|
25
|
+
.Python
|
|
26
|
+
env/
|
|
27
|
+
bin/
|
|
28
|
+
build/
|
|
29
|
+
develop-eggs/
|
|
30
|
+
dist/
|
|
31
|
+
eggs/
|
|
32
|
+
lib/
|
|
33
|
+
lib64/
|
|
34
|
+
parts/
|
|
35
|
+
sdist/
|
|
36
|
+
var/
|
|
37
|
+
*.egg-info/
|
|
38
|
+
.installed.cfg
|
|
39
|
+
*.egg
|
|
40
|
+
|
|
41
|
+
# Installer logs
|
|
42
|
+
pip-log.txt
|
|
43
|
+
pip-delete-this-directory.txt
|
|
44
|
+
|
|
45
|
+
# Unit test / coverage reports
|
|
46
|
+
htmlcov/
|
|
47
|
+
.tox/
|
|
48
|
+
.coverage
|
|
49
|
+
.cache
|
|
50
|
+
.idea/
|
|
51
|
+
nosetests.xml
|
|
52
|
+
coverage.xml
|
|
53
|
+
|
|
54
|
+
# Translations
|
|
55
|
+
*.mo
|
|
56
|
+
|
|
57
|
+
# Mr Developer
|
|
58
|
+
.mr.developer.cfg
|
|
59
|
+
.project
|
|
60
|
+
.pydevproject
|
|
61
|
+
|
|
62
|
+
# Rope
|
|
63
|
+
.ropeproject
|
|
64
|
+
|
|
65
|
+
# Django stuff
|
|
66
|
+
*.log
|
|
67
|
+
*.pot
|
|
68
|
+
|
|
69
|
+
# Sphinx documentation
|
|
70
|
+
docs/_build/
|
|
71
|
+
docs/build/
|
|
72
|
+
docs/source/generated/
|
|
73
|
+
# Added by code-review-graph
|
|
74
|
+
.code-review-graph/
|
|
75
|
+
|
|
76
|
+
# macOS
|
|
77
|
+
.DS_Store
|
|
78
|
+
|
|
79
|
+
# AI assistant configs and scratch (local, per-developer)
|
|
80
|
+
.claude/
|
|
81
|
+
.codebuddy/
|
|
82
|
+
.gemini/
|
|
83
|
+
.kiro/
|
|
84
|
+
.qoder/
|
|
85
|
+
.serena/
|
|
86
|
+
.cursorrules
|
|
87
|
+
.windsurfrules
|
|
88
|
+
.mcp.json
|
|
89
|
+
opencode.jsonc
|
|
90
|
+
AGENTS.md
|
|
91
|
+
CLAUDE.md
|
|
92
|
+
CODEBUDDY.md
|
|
93
|
+
GEMINI.md
|
|
94
|
+
QODER.md
|
|
95
|
+
.github/code-review-graph.instruction.md
|
locpick-0.0.0/LICENSE
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
Copyright (c) 2026, eli knaap
|
|
2
|
+
All rights reserved.
|
|
3
|
+
|
|
4
|
+
Redistribution and use in source and binary forms, with or without
|
|
5
|
+
modification, are permitted provided that the following conditions are met:
|
|
6
|
+
|
|
7
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
8
|
+
list of conditions and the following disclaimer.
|
|
9
|
+
|
|
10
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
11
|
+
this list of conditions and the following disclaimer in the documentation
|
|
12
|
+
and/or other materials provided with the distribution.
|
|
13
|
+
|
|
14
|
+
3. Neither the name of the copyright holder nor the names of its contributors
|
|
15
|
+
may be used to endorse or promote products derived from this software without
|
|
16
|
+
specific prior written permission.
|
|
17
|
+
|
|
18
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
19
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
20
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
21
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
22
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
23
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
24
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
25
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
26
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
27
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
locpick-0.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: locpick
|
|
3
|
+
Version: 0.0.0
|
|
4
|
+
Summary: Modern location and destination choice modeling
|
|
5
|
+
Author: eli knaap
|
|
6
|
+
License: BSD-3-Clause
|
|
7
|
+
Project-URL: Homepage, https://knaaptime.github.io/locpick
|
|
8
|
+
Project-URL: Documentation, https://knaaptime.github.io/locpick
|
|
9
|
+
Project-URL: Source, https://github.com/knaaptime/locpick
|
|
10
|
+
Project-URL: Tracker, https://github.com/knaaptime/locpick/issues
|
|
11
|
+
Project-URL: Changelog, https://github.com/knaaptime/locpick/releases
|
|
12
|
+
Classifier: Intended Audience :: Science/Research
|
|
13
|
+
Classifier: Topic :: Scientific/Engineering :: Information Analysis
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
18
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
19
|
+
Classifier: Operating System :: OS Independent
|
|
20
|
+
Requires-Python: >=3.12
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Requires-Dist: numpy>=1.23
|
|
24
|
+
Requires-Dist: pandas>=1.5
|
|
25
|
+
Requires-Dist: xarray>=2022.06
|
|
26
|
+
Requires-Dist: formulaic>=0.5
|
|
27
|
+
Requires-Dist: scipy>=1.9
|
|
28
|
+
Requires-Dist: numba>=0.59
|
|
29
|
+
Requires-Dist: lazy_loader>=0.4
|
|
30
|
+
Requires-Dist: jax>=0.4
|
|
31
|
+
Provides-Extra: optax
|
|
32
|
+
Requires-Dist: optax>=0.1; extra == "optax"
|
|
33
|
+
Provides-Extra: optimagic
|
|
34
|
+
Requires-Dist: optimagic>=0.5; extra == "optimagic"
|
|
35
|
+
Provides-Extra: optimistix
|
|
36
|
+
Requires-Dist: optimistix>=0.0.6; extra == "optimistix"
|
|
37
|
+
Provides-Extra: spatial
|
|
38
|
+
Requires-Dist: libpysal>=4.0; extra == "spatial"
|
|
39
|
+
Requires-Dist: geopandas>=0.13; extra == "spatial"
|
|
40
|
+
Requires-Dist: scikit-learn>=1.2; extra == "spatial"
|
|
41
|
+
Provides-Extra: sparse
|
|
42
|
+
Requires-Dist: sparsax>=0.7; extra == "sparse"
|
|
43
|
+
Requires-Dist: scikit-sparse>=0.4; extra == "sparse"
|
|
44
|
+
Provides-Extra: test
|
|
45
|
+
Requires-Dist: coverage; extra == "test"
|
|
46
|
+
Requires-Dist: pytest>=7; extra == "test"
|
|
47
|
+
Requires-Dist: pytest-cov; extra == "test"
|
|
48
|
+
Requires-Dist: pytest-xdist; extra == "test"
|
|
49
|
+
Requires-Dist: formulaic>=0.5; extra == "test"
|
|
50
|
+
Provides-Extra: docs
|
|
51
|
+
Requires-Dist: numpydoc; extra == "docs"
|
|
52
|
+
Requires-Dist: nbsphinx; extra == "docs"
|
|
53
|
+
Requires-Dist: myst-parser; extra == "docs"
|
|
54
|
+
Requires-Dist: myst-nb; extra == "docs"
|
|
55
|
+
Requires-Dist: sphinx>=7; extra == "docs"
|
|
56
|
+
Requires-Dist: sphinx-autodoc2; extra == "docs"
|
|
57
|
+
Requires-Dist: sphinxcontrib-bibtex; extra == "docs"
|
|
58
|
+
Requires-Dist: sphinx-copybutton; extra == "docs"
|
|
59
|
+
Requires-Dist: sphinx-immaterial; extra == "docs"
|
|
60
|
+
Requires-Dist: watermark; extra == "docs"
|
|
61
|
+
Provides-Extra: all
|
|
62
|
+
Requires-Dist: locpick[optax]; extra == "all"
|
|
63
|
+
Requires-Dist: locpick[optimagic]; extra == "all"
|
|
64
|
+
Requires-Dist: locpick[optimistix]; extra == "all"
|
|
65
|
+
Requires-Dist: locpick[spatial]; extra == "all"
|
|
66
|
+
Requires-Dist: locpick[sparse]; extra == "all"
|
|
67
|
+
Provides-Extra: dev
|
|
68
|
+
Requires-Dist: locpick[all]; extra == "dev"
|
|
69
|
+
Requires-Dist: locpick[test]; extra == "dev"
|
|
70
|
+
Requires-Dist: locpick[docs]; extra == "dev"
|
|
71
|
+
Requires-Dist: ruff; extra == "dev"
|
|
72
|
+
Requires-Dist: ipykernel; extra == "dev"
|
|
73
|
+
Requires-Dist: pre-commit; extra == "dev"
|
|
74
|
+
Dynamic: license-file
|
|
75
|
+
|
|
76
|
+
# LocPick
|
|
77
|
+
|
|
78
|
+
`locpick` is a Python library for estimating **discrete choice models of location decisions** — where individuals, households, or firms choose among *spatially-defined* alternatives (neighborhoods, jobs, housing units, transit stops). These methods have broad applicability but are common in regional land-use and housing market modeling. The package is designed for:
|
|
79
|
+
|
|
80
|
+
- **Large-scale urban models**: 100K+ choosers, 1K+ alternatives
|
|
81
|
+
- **Sampling-based estimation**: Most alternatives are irrelevant; only a sampled subset is evaluated per chooser
|
|
82
|
+
- **Spatial dependence**: Nearby alternatives can affect each other or share unobserved attributes (via `graph=` on any model)
|
|
83
|
+
- **Heterogeneous preferences**: Mixed logit for random taste variation
|
|
84
|
+
- **Nested structure**: Nested logit for hierarchical choice (e.g., county → tract → block)
|
|
85
|
+
- **JAX-native computation**: JIT-compiled kernels, GPU acceleration, automatic differentiation
|
|
86
|
+
|
|
87
|
+
The package is **not** a general-purpose ML library. It is specifically for structural econometric models of choice where the likelihood has a closed form (or simulated approximation) and parameters have behavioral interpretations. For more transportation-oriented problems, see [larch](https://github.com/driftlesslabs/larch)
|
|
88
|
+
|
|
89
|
+
## Features
|
|
90
|
+
|
|
91
|
+
LocPick can automate the creation of choice tables for estimation or simulation, using census choice sets, uniform or weighted random sampling of alternatives, generated interaction terms, and cartesian merges. A unique feature is the implementation of *spatial* choice models, which take one of two forms.
|
|
92
|
+
|
|
93
|
+
- The [Bhat et al](https://linkinghub.elsevier.com/retrieve/pii/S0191261503000055) approach is similar to a spatial error model, assuming that nearby alternatives are more similar (closer substitutes).
|
|
94
|
+
- The SAR style approach assumes that the structural utility of each alternative $V$ has a simultaneous autoregressive structure, and is estimated with either [PML](http://dx.doi.org/10.1016/j.regsciurbeco.2009.09.004) or [GMM](https://www.sciencedirect.com/science/article/pii/S0166046217300625)
|
|
95
|
+
|
|
96
|
+
It also provides tools for Monte Carlo simulation of choices given probability distributions from fitted models, with fast algorithms for independent or capacity-constrained choices.
|
|
97
|
+
|
|
98
|
+
LocPick includes estimators for classic, spatially-correlated, and simultaneous autoregressive:
|
|
99
|
+
|
|
100
|
+
- Multinomial Logit
|
|
101
|
+
- Nested Logit
|
|
102
|
+
- Mixed Logit and
|
|
103
|
+
- Mixed/Nested
|
|
104
|
+
|
|
105
|
+
models, and an internal data pipeline designed around pandas inputs, xarray-backed alignment, and NumPy/JAX-ready arrays.
|