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.
Files changed (144) hide show
  1. locpick-0.0.0/.coveragerc +8 -0
  2. locpick-0.0.0/.github/release.yml +22 -0
  3. locpick-0.0.0/.github/workflows/build_docs.yml +106 -0
  4. locpick-0.0.0/.github/workflows/lint.yml +22 -0
  5. locpick-0.0.0/.github/workflows/spec1.yml +126 -0
  6. locpick-0.0.0/.github/workflows/unittests.yml +82 -0
  7. locpick-0.0.0/.github/workflows/upload_package.yml +51 -0
  8. locpick-0.0.0/.gitignore +95 -0
  9. locpick-0.0.0/.pre-commit-config.yaml +13 -0
  10. locpick-0.0.0/LICENSE +27 -0
  11. locpick-0.0.0/PKG-INFO +105 -0
  12. locpick-0.0.0/README.md +30 -0
  13. locpick-0.0.0/ci/312-latest.yml +44 -0
  14. locpick-0.0.0/ci/312-oldest.yml +34 -0
  15. locpick-0.0.0/ci/313-latest.yml +44 -0
  16. locpick-0.0.0/ci/314-latest.yml +33 -0
  17. locpick-0.0.0/codecov.yml +4 -0
  18. locpick-0.0.0/docs/.gitignore +1 -0
  19. locpick-0.0.0/docs/Makefile +22 -0
  20. locpick-0.0.0/docs/docs_env.yml +49 -0
  21. locpick-0.0.0/docs/source/_static/2ed978e7d8105199423735a8254a30a7.svg +47 -0
  22. locpick-0.0.0/docs/source/_static/5291332ea831bd1aeca31c499f8025dc.svg +32 -0
  23. locpick-0.0.0/docs/source/_static/custom.css +52 -0
  24. locpick-0.0.0/docs/source/_static/lokey.png +0 -0
  25. locpick-0.0.0/docs/source/_static/lokey2.png +0 -0
  26. locpick-0.0.0/docs/source/_static/lokey3.png +0 -0
  27. locpick-0.0.0/docs/source/_static/references.bib +444 -0
  28. locpick-0.0.0/docs/source/_static/roundabout-black.svg +6 -0
  29. locpick-0.0.0/docs/source/_static/roundabout-white.svg +6 -0
  30. locpick-0.0.0/docs/source/_static/roundabout.png +0 -0
  31. locpick-0.0.0/docs/source/_static/roundabout.svg +29 -0
  32. locpick-0.0.0/docs/source/api.rst +149 -0
  33. locpick-0.0.0/docs/source/conf.py +164 -0
  34. locpick-0.0.0/docs/source/index.md +47 -0
  35. locpick-0.0.0/docs/source/installation.md +42 -0
  36. locpick-0.0.0/docs/source/references.md +5 -0
  37. locpick-0.0.0/docs/source/user-guide/choicetable.md +57 -0
  38. locpick-0.0.0/docs/source/user-guide/distance.md +15 -0
  39. locpick-0.0.0/docs/source/user-guide/inference.md +96 -0
  40. locpick-0.0.0/docs/source/user-guide/livelike_locpick_household_tract_demo.ipynb +570 -0
  41. locpick-0.0.0/docs/source/user-guide/mixed.md +26 -0
  42. locpick-0.0.0/docs/source/user-guide/mnl.md +34 -0
  43. locpick-0.0.0/docs/source/user-guide/modelspec.md +67 -0
  44. locpick-0.0.0/docs/source/user-guide/nested.md +29 -0
  45. locpick-0.0.0/docs/source/user-guide/prediction.md +30 -0
  46. locpick-0.0.0/docs/source/user-guide/sampling.md +23 -0
  47. locpick-0.0.0/docs/source/user-guide/sar_mnl.md +186 -0
  48. locpick-0.0.0/docs/source/user-guide/sar_mnl_demo.ipynb +434 -0
  49. locpick-0.0.0/docs/source/user-guide/spatial_mixed.md +112 -0
  50. locpick-0.0.0/docs/source/user-guide/spatial_mixed_nested.md +110 -0
  51. locpick-0.0.0/docs/source/user-guide/spatial_models_demo.ipynb +414 -0
  52. locpick-0.0.0/docs/source/user-guide/spatial_models_lag_demo.ipynb +524 -0
  53. locpick-0.0.0/docs/source/user-guide/spatial_nested.md +69 -0
  54. locpick-0.0.0/docs/update_version_json.py +40 -0
  55. locpick-0.0.0/docs/versions.json +3 -0
  56. locpick-0.0.0/environment.yml +48 -0
  57. locpick-0.0.0/locpick/__init__.py +27 -0
  58. locpick-0.0.0/locpick/__init__.pyi +153 -0
  59. locpick-0.0.0/locpick/_jax/__init__.py +55 -0
  60. locpick-0.0.0/locpick/_jax/__init__.pyi +62 -0
  61. locpick-0.0.0/locpick/_jax/builders.py +1607 -0
  62. locpick-0.0.0/locpick/_jax/data.py +361 -0
  63. locpick-0.0.0/locpick/_jax/diag_precompute.py +516 -0
  64. locpick-0.0.0/locpick/_jax/kernels.py +905 -0
  65. locpick-0.0.0/locpick/_jax/objective.py +296 -0
  66. locpick-0.0.0/locpick/_jax/sar_kernels.py +1008 -0
  67. locpick-0.0.0/locpick/_jax/sparse_backends.py +232 -0
  68. locpick-0.0.0/locpick/_jax/sparse_solve.py +429 -0
  69. locpick-0.0.0/locpick/_jax/transforms.py +275 -0
  70. locpick-0.0.0/locpick/_kernels/__init__.py +14 -0
  71. locpick-0.0.0/locpick/_kernels/__init__.pyi +20 -0
  72. locpick-0.0.0/locpick/_kernels/constants.py +31 -0
  73. locpick-0.0.0/locpick/_kernels/mnl_numpy.py +232 -0
  74. locpick-0.0.0/locpick/_kernels/sar_mnl_numpy.py +259 -0
  75. locpick-0.0.0/locpick/_sampling/__init__.py +9 -0
  76. locpick-0.0.0/locpick/_sampling/__init__.pyi +33 -0
  77. locpick-0.0.0/locpick/_sampling/correction.py +75 -0
  78. locpick-0.0.0/locpick/_sampling/inclusion.py +130 -0
  79. locpick-0.0.0/locpick/_sampling/kernels.py +441 -0
  80. locpick-0.0.0/locpick/_solvers/__init__.py +12 -0
  81. locpick-0.0.0/locpick/_solvers/__init__.pyi +39 -0
  82. locpick-0.0.0/locpick/_solvers/lbfgs.py +165 -0
  83. locpick-0.0.0/locpick/_solvers/optax.py +255 -0
  84. locpick-0.0.0/locpick/_solvers/optimagic.py +164 -0
  85. locpick-0.0.0/locpick/_solvers/optimistix.py +479 -0
  86. locpick-0.0.0/locpick/_solvers/protocol.py +171 -0
  87. locpick-0.0.0/locpick/_solvers/trust_ncg.py +151 -0
  88. locpick-0.0.0/locpick/data/__init__.py +45 -0
  89. locpick-0.0.0/locpick/data/__init__.pyi +60 -0
  90. locpick-0.0.0/locpick/data/arrays.py +128 -0
  91. locpick-0.0.0/locpick/data/choicetable.py +1118 -0
  92. locpick-0.0.0/locpick/data/dataset.py +330 -0
  93. locpick-0.0.0/locpick/data/distance.py +260 -0
  94. locpick-0.0.0/locpick/data/format.py +302 -0
  95. locpick-0.0.0/locpick/data/problem.py +265 -0
  96. locpick-0.0.0/locpick/dgp.py +2055 -0
  97. locpick-0.0.0/locpick/models/__init__.py +17 -0
  98. locpick-0.0.0/locpick/models/__init__.pyi +36 -0
  99. locpick-0.0.0/locpick/models/_spatial.py +185 -0
  100. locpick-0.0.0/locpick/models/_spatial_weights.py +126 -0
  101. locpick-0.0.0/locpick/models/base.py +858 -0
  102. locpick-0.0.0/locpick/models/choice_model.py +2258 -0
  103. locpick-0.0.0/locpick/models/mixed.py +621 -0
  104. locpick-0.0.0/locpick/models/nested.py +357 -0
  105. locpick-0.0.0/locpick/models/scl.py +276 -0
  106. locpick-0.0.0/locpick/py.typed +0 -0
  107. locpick-0.0.0/locpick/results/__init__.py +14 -0
  108. locpick-0.0.0/locpick/results/__init__.pyi +35 -0
  109. locpick-0.0.0/locpick/results/diagnostics.py +713 -0
  110. locpick-0.0.0/locpick/results/fit_result.py +268 -0
  111. locpick-0.0.0/locpick/spec/__init__.py +9 -0
  112. locpick-0.0.0/locpick/spec/__init__.pyi +14 -0
  113. locpick-0.0.0/locpick/spec/model_spec.py +330 -0
  114. locpick-0.0.0/locpick/spec/terms.py +107 -0
  115. locpick-0.0.0/locpick.egg-info/PKG-INFO +105 -0
  116. locpick-0.0.0/locpick.egg-info/SOURCES.txt +142 -0
  117. locpick-0.0.0/locpick.egg-info/dependency_links.txt +1 -0
  118. locpick-0.0.0/locpick.egg-info/requires.txt +60 -0
  119. locpick-0.0.0/locpick.egg-info/top_level.txt +1 -0
  120. locpick-0.0.0/pyproject.toml +131 -0
  121. locpick-0.0.0/setup.cfg +4 -0
  122. locpick-0.0.0/tests/README.md +1 -0
  123. locpick-0.0.0/tests/test_backend_kernel_interfaces.py +188 -0
  124. locpick-0.0.0/tests/test_choicetable_xarray.py +548 -0
  125. locpick-0.0.0/tests/test_diag_precompute.py +105 -0
  126. locpick-0.0.0/tests/test_diagnostics.py +306 -0
  127. locpick-0.0.0/tests/test_estimation_problem.py +308 -0
  128. locpick-0.0.0/tests/test_inference.py +351 -0
  129. locpick-0.0.0/tests/test_marginal_effects_wtp.py +372 -0
  130. locpick-0.0.0/tests/test_mixed_nested.py +207 -0
  131. locpick-0.0.0/tests/test_mnl.py +1928 -0
  132. locpick-0.0.0/tests/test_modelspec_v2.py +214 -0
  133. locpick-0.0.0/tests/test_nested_and_mixed.py +1150 -0
  134. locpick-0.0.0/tests/test_param_recovery.py +531 -0
  135. locpick-0.0.0/tests/test_prediction_all_models.py +502 -0
  136. locpick-0.0.0/tests/test_prediction_simulation.py +323 -0
  137. locpick-0.0.0/tests/test_qmc_draws.py +155 -0
  138. locpick-0.0.0/tests/test_sampling.py +718 -0
  139. locpick-0.0.0/tests/test_sar_mnl.py +747 -0
  140. locpick-0.0.0/tests/test_solvers.py +284 -0
  141. locpick-0.0.0/tests/test_sparse_backends.py +156 -0
  142. locpick-0.0.0/tests/test_sparse_solve_vjp.py +181 -0
  143. locpick-0.0.0/tests/test_spatial_diagnostics.py +319 -0
  144. locpick-0.0.0/tests/test_spatial_models.py +1523 -0
@@ -0,0 +1,8 @@
1
+ # .coveragerc
2
+ [run]
3
+ source = locpick
4
+ concurrency = multiprocessing
5
+ parallel = true
6
+ omit =
7
+ tests/*
8
+ */tests/*
@@ -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
@@ -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
@@ -0,0 +1,13 @@
1
+ repos:
2
+ - repo: local
3
+ hooks:
4
+ - id: ruff-check
5
+ name: ruff check
6
+ entry: ruff check --fix
7
+ language: system
8
+ types: [python]
9
+ - id: ruff-format
10
+ name: ruff format
11
+ entry: ruff format
12
+ language: system
13
+ types: [python]
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.