sft-wick 0.2.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 (150) hide show
  1. sft_wick-0.2.0/.github/workflows/ci.yml +75 -0
  2. sft_wick-0.2.0/.github/workflows/publish.yml +138 -0
  3. sft_wick-0.2.0/.gitignore +21 -0
  4. sft_wick-0.2.0/.readthedocs.yaml +17 -0
  5. sft_wick-0.2.0/.zenodo.json +28 -0
  6. sft_wick-0.2.0/CHANGELOG.md +262 -0
  7. sft_wick-0.2.0/CITATION.cff +47 -0
  8. sft_wick-0.2.0/LICENSE +13 -0
  9. sft_wick-0.2.0/PKG-INFO +603 -0
  10. sft_wick-0.2.0/README.md +551 -0
  11. sft_wick-0.2.0/docs/Makefile +19 -0
  12. sft_wick-0.2.0/docs/_static/diagrams/_generate.py +251 -0
  13. sft_wick-0.2.0/docs/_static/diagrams/custom_labels.png +0 -0
  14. sft_wick-0.2.0/docs/_static/diagrams/example.tex +18 -0
  15. sft_wick-0.2.0/docs/_static/diagrams/manual_positions.png +0 -0
  16. sft_wick-0.2.0/docs/_static/diagrams/order2_grayscale.png +0 -0
  17. sft_wick-0.2.0/docs/_static/diagrams/order2_publication.png +0 -0
  18. sft_wick-0.2.0/docs/_static/diagrams/presets.png +0 -0
  19. sft_wick-0.2.0/docs/api/action.rst +8 -0
  20. sft_wick-0.2.0/docs/api/diagrams.rst +8 -0
  21. sft_wick-0.2.0/docs/api/drawing.rst +8 -0
  22. sft_wick-0.2.0/docs/api/drawing_tikz.rst +8 -0
  23. sft_wick-0.2.0/docs/api/evaluate.rst +8 -0
  24. sft_wick-0.2.0/docs/api/expressions.rst +27 -0
  25. sft_wick-0.2.0/docs/api/fields.rst +8 -0
  26. sft_wick-0.2.0/docs/api/index.rst +38 -0
  27. sft_wick-0.2.0/docs/api/indices.rst +8 -0
  28. sft_wick-0.2.0/docs/api/latex.rst +8 -0
  29. sft_wick-0.2.0/docs/api/perturbation.rst +8 -0
  30. sft_wick-0.2.0/docs/api/propagators.rst +8 -0
  31. sft_wick-0.2.0/docs/api/render_style.rst +28 -0
  32. sft_wick-0.2.0/docs/api/simplify.rst +8 -0
  33. sft_wick-0.2.0/docs/api/util.rst +8 -0
  34. sft_wick-0.2.0/docs/api/vertices.rst +8 -0
  35. sft_wick-0.2.0/docs/api/wick.rst +8 -0
  36. sft_wick-0.2.0/docs/api/workflow.rst +67 -0
  37. sft_wick-0.2.0/docs/changelog.rst +65 -0
  38. sft_wick-0.2.0/docs/conf.py +89 -0
  39. sft_wick-0.2.0/docs/contributing.rst +196 -0
  40. sft_wick-0.2.0/docs/deductive_verification.md +493 -0
  41. sft_wick-0.2.0/docs/examples/advanced.rst +247 -0
  42. sft_wick-0.2.0/docs/examples/first_order.rst +90 -0
  43. sft_wick-0.2.0/docs/examples/index.rst +15 -0
  44. sft_wick-0.2.0/docs/examples/multicomponent.rst +75 -0
  45. sft_wick-0.2.0/docs/examples/nonlocal_vertex.rst +80 -0
  46. sft_wick-0.2.0/docs/examples/notebook.rst +9 -0
  47. sft_wick-0.2.0/docs/examples/notebook_rendered.ipynb +280 -0
  48. sft_wick-0.2.0/docs/examples/zeroth_order.rst +99 -0
  49. sft_wick-0.2.0/docs/getting_started.rst +224 -0
  50. sft_wick-0.2.0/docs/index.rst +150 -0
  51. sft_wick-0.2.0/docs/installation.rst +97 -0
  52. sft_wick-0.2.0/docs/make.bat +35 -0
  53. sft_wick-0.2.0/docs/notes/R_contracted_nonlocal_vertex.md +277 -0
  54. sft_wick-0.2.0/docs/notes/equal_time_nonlocal_vertex.md +234 -0
  55. sft_wick-0.2.0/docs/requirements.txt +13 -0
  56. sft_wick-0.2.0/docs/theory/feynman_diagrams.rst +130 -0
  57. sft_wick-0.2.0/docs/theory/index.rst +17 -0
  58. sft_wick-0.2.0/docs/theory/msr_formalism.rst +174 -0
  59. sft_wick-0.2.0/docs/theory/wick_theorem.rst +126 -0
  60. sft_wick-0.2.0/docs/user_guide/diagrams.rst +356 -0
  61. sft_wick-0.2.0/docs/user_guide/discretization.rst +140 -0
  62. sft_wick-0.2.0/docs/user_guide/expressions.rst +175 -0
  63. sft_wick-0.2.0/docs/user_guide/fields.rst +95 -0
  64. sft_wick-0.2.0/docs/user_guide/index.rst +24 -0
  65. sft_wick-0.2.0/docs/user_guide/latex_output.rst +118 -0
  66. sft_wick-0.2.0/docs/user_guide/parallelism.rst +91 -0
  67. sft_wick-0.2.0/docs/user_guide/perturbative_expansion.rst +231 -0
  68. sft_wick-0.2.0/docs/user_guide/simplification.rst +140 -0
  69. sft_wick-0.2.0/docs/user_guide/vertices_and_actions.rst +183 -0
  70. sft_wick-0.2.0/docs/user_guide/workflow.rst +1155 -0
  71. sft_wick-0.2.0/docs/verification/index.rst +339 -0
  72. sft_wick-0.2.0/examples/demo1/L2/config.yaml +122 -0
  73. sft_wick-0.2.0/examples/demo1/L2/reproduce_figures.py +727 -0
  74. sft_wick-0.2.0/examples/demo1/c_closed_form.py +91 -0
  75. sft_wick-0.2.0/examples/demo1/run_simulation.py +269 -0
  76. sft_wick-0.2.0/examples/demo1/validate_R_C_derivation.py +217 -0
  77. sft_wick-0.2.0/examples/demo1/validate_phase5.py +317 -0
  78. sft_wick-0.2.0/examples/demo1/validate_wrapper.py +196 -0
  79. sft_wick-0.2.0/examples/demo1_config.yaml +72 -0
  80. sft_wick-0.2.0/examples/demo2/L2/c_closed_form.py +72 -0
  81. sft_wick-0.2.0/examples/demo2/L2/config_FF.yaml +79 -0
  82. sft_wick-0.2.0/examples/demo2/L2/config_FK.yaml +87 -0
  83. sft_wick-0.2.0/examples/demo2/L2/reproduce_figures.py +433 -0
  84. sft_wick-0.2.0/examples/demo2/k3_coupling.py +98 -0
  85. sft_wick-0.2.0/examples/demo2/run_simulation.py +270 -0
  86. sft_wick-0.2.0/examples/demo2/validate_FK_dynamic.py +200 -0
  87. sft_wick-0.2.0/examples/demo2/validate_wrapper_demo2.py +460 -0
  88. sft_wick-0.2.0/examples/demo2_config.yaml +97 -0
  89. sft_wick-0.2.0/examples/demo_workflow.py +195 -0
  90. sft_wick-0.2.0/pyproject.toml +79 -0
  91. sft_wick-0.2.0/src/sft_wick/__init__.py +191 -0
  92. sft_wick-0.2.0/src/sft_wick/_util.py +17 -0
  93. sft_wick-0.2.0/src/sft_wick/action.py +52 -0
  94. sft_wick-0.2.0/src/sft_wick/diagrams.py +378 -0
  95. sft_wick-0.2.0/src/sft_wick/drawing.py +883 -0
  96. sft_wick-0.2.0/src/sft_wick/drawing_tikz.py +497 -0
  97. sft_wick-0.2.0/src/sft_wick/evaluate.py +3982 -0
  98. sft_wick-0.2.0/src/sft_wick/expressions.py +530 -0
  99. sft_wick-0.2.0/src/sft_wick/fields.py +125 -0
  100. sft_wick-0.2.0/src/sft_wick/indices.py +30 -0
  101. sft_wick-0.2.0/src/sft_wick/latex.py +72 -0
  102. sft_wick-0.2.0/src/sft_wick/perturbation.py +2421 -0
  103. sft_wick-0.2.0/src/sft_wick/propagators.py +74 -0
  104. sft_wick-0.2.0/src/sft_wick/render_labels.py +166 -0
  105. sft_wick-0.2.0/src/sft_wick/render_layout.py +319 -0
  106. sft_wick-0.2.0/src/sft_wick/render_style.py +429 -0
  107. sft_wick-0.2.0/src/sft_wick/simplify.py +1106 -0
  108. sft_wick-0.2.0/src/sft_wick/vertices.py +217 -0
  109. sft_wick-0.2.0/src/sft_wick/wick.py +690 -0
  110. sft_wick-0.2.0/src/sft_wick/workflow/__init__.py +93 -0
  111. sft_wick-0.2.0/src/sft_wick/workflow/cache.py +120 -0
  112. sft_wick-0.2.0/src/sft_wick/workflow/cli.py +187 -0
  113. sft_wick-0.2.0/src/sft_wick/workflow/config.py +964 -0
  114. sft_wick-0.2.0/src/sft_wick/workflow/expansion.py +493 -0
  115. sft_wick-0.2.0/src/sft_wick/workflow/propagators.py +298 -0
  116. sft_wick-0.2.0/src/sft_wick/workflow/r_contracted.py +177 -0
  117. sft_wick-0.2.0/src/sft_wick/workflow/result.py +204 -0
  118. sft_wick-0.2.0/src/sft_wick/workflow/specs.py +768 -0
  119. sft_wick-0.2.0/src/sft_wick/workflow/system.py +495 -0
  120. sft_wick-0.2.0/tests/__init__.py +0 -0
  121. sft_wick-0.2.0/tests/brute_wick.py +397 -0
  122. sft_wick-0.2.0/tests/perf/__init__.py +0 -0
  123. sft_wick-0.2.0/tests/perf/_compare_paths.py +138 -0
  124. sft_wick-0.2.0/tests/perf/test_dynamic_coupling_perf.py +149 -0
  125. sft_wick-0.2.0/tests/test_R_contracted_vertex.py +749 -0
  126. sft_wick-0.2.0/tests/test_c_propagator_gauss_legendre.py +339 -0
  127. sft_wick-0.2.0/tests/test_d_dim_spatial.py +220 -0
  128. sft_wick-0.2.0/tests/test_deductive_expansion.py +1577 -0
  129. sft_wick-0.2.0/tests/test_deductive_numerics.py +2263 -0
  130. sft_wick-0.2.0/tests/test_diag_C_offdiagonal.py +279 -0
  131. sft_wick-0.2.0/tests/test_diagonal_A_time_dependent.py +164 -0
  132. sft_wick-0.2.0/tests/test_diagrams.py +216 -0
  133. sft_wick-0.2.0/tests/test_drawing_style.py +554 -0
  134. sft_wick-0.2.0/tests/test_drawing_tikz.py +258 -0
  135. sft_wick-0.2.0/tests/test_dt_discretization.py +112 -0
  136. sft_wick-0.2.0/tests/test_dynamic_coupling.py +542 -0
  137. sft_wick-0.2.0/tests/test_equal_time_nonlocal.py +453 -0
  138. sft_wick-0.2.0/tests/test_eval_symbolic_batched.py +301 -0
  139. sft_wick-0.2.0/tests/test_evaluate_interpolation_accuracy.py +153 -0
  140. sft_wick-0.2.0/tests/test_evaluate_pipeline.py +169 -0
  141. sft_wick-0.2.0/tests/test_expressions.py +168 -0
  142. sft_wick-0.2.0/tests/test_fields.py +69 -0
  143. sft_wick-0.2.0/tests/test_gauss_legendre_integrator.py +277 -0
  144. sft_wick-0.2.0/tests/test_matrix_r_evaluation.py +46 -0
  145. sft_wick-0.2.0/tests/test_perturbation.py +540 -0
  146. sft_wick-0.2.0/tests/test_propagators.py +119 -0
  147. sft_wick-0.2.0/tests/test_simplify.py +587 -0
  148. sft_wick-0.2.0/tests/test_wick.py +309 -0
  149. sft_wick-0.2.0/tests/test_workflow.py +267 -0
  150. sft_wick-0.2.0/tests/test_workflow_config.py +1241 -0
@@ -0,0 +1,75 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ workflow_dispatch:
8
+
9
+ # Cancel superseded runs on the same ref (e.g. rapid pushes to a PR).
10
+ concurrency:
11
+ group: ci-${{ github.workflow }}-${{ github.ref }}
12
+ cancel-in-progress: true
13
+
14
+ permissions:
15
+ contents: read
16
+
17
+ jobs:
18
+ test:
19
+ name: Tests (Python ${{ matrix.python-version }})
20
+ runs-on: ubuntu-latest
21
+ strategy:
22
+ fail-fast: false
23
+ matrix:
24
+ python-version: ['3.10', '3.11', '3.12']
25
+ steps:
26
+ - uses: actions/checkout@v4
27
+
28
+ - name: Set up Python ${{ matrix.python-version }}
29
+ uses: actions/setup-python@v5
30
+ with:
31
+ python-version: ${{ matrix.python-version }}
32
+ cache: pip
33
+ cache-dependency-path: pyproject.toml
34
+
35
+ - name: Install package with dev extras
36
+ run: |
37
+ python -m pip install --upgrade pip
38
+ pip install -e ".[dev]"
39
+
40
+ - name: Run test suite
41
+ run: pytest tests/ -v
42
+
43
+ build:
44
+ name: Build & check distribution
45
+ runs-on: ubuntu-latest
46
+ steps:
47
+ - uses: actions/checkout@v4
48
+
49
+ - name: Set up Python
50
+ uses: actions/setup-python@v5
51
+ with:
52
+ python-version: '3.12'
53
+
54
+ - name: Install build tooling
55
+ run: python -m pip install --upgrade pip build twine
56
+
57
+ - name: Build sdist and wheel
58
+ run: python -m build
59
+
60
+ - name: Validate distribution metadata
61
+ run: twine check --strict dist/*
62
+
63
+ - name: Smoke-test the built wheel in a clean venv
64
+ run: |
65
+ python -m venv /tmp/smoke
66
+ /tmp/smoke/bin/pip install --upgrade pip
67
+ /tmp/smoke/bin/pip install dist/*.whl
68
+ /tmp/smoke/bin/python -c "from sft_wick import compute_moment; print('import OK')"
69
+ /tmp/smoke/bin/sft-wick --help
70
+
71
+ - name: Upload distribution artifacts
72
+ uses: actions/upload-artifact@v4
73
+ with:
74
+ name: dist-ci
75
+ path: dist/
@@ -0,0 +1,138 @@
1
+ name: Publish
2
+
3
+ # Release pipeline using PyPI Trusted Publishing (OIDC) — no API tokens
4
+ # or stored secrets.
5
+ #
6
+ # * Push a `vX.Y.Z` tag -> run tests + build the distribution
7
+ # (validation dry run; nothing is published).
8
+ # * Publish a GitHub Release -> run tests, build, publish to PyPI, then
9
+ # sign the artifacts with Sigstore and
10
+ # attach them to the release.
11
+ #
12
+ # Recommended flow:
13
+ # 1. git tag v0.2.0 && git push origin v0.2.0 # validates the build
14
+ # 2. once the tag run is green, create a GitHub Release *from the existing
15
+ # tag* # -> publishes to PyPI
16
+ on:
17
+ push:
18
+ tags:
19
+ - 'v*'
20
+ release:
21
+ types: [published]
22
+
23
+ # Serialize runs for a given ref so a tag push and a release of the same
24
+ # version never race. Never cancel an in-flight publish — queue instead.
25
+ concurrency:
26
+ group: publish-${{ github.ref }}
27
+ cancel-in-progress: false
28
+
29
+ permissions:
30
+ contents: read
31
+
32
+ jobs:
33
+ test:
34
+ name: Run tests
35
+ runs-on: ubuntu-latest
36
+ steps:
37
+ - uses: actions/checkout@v4
38
+
39
+ - name: Set up Python
40
+ uses: actions/setup-python@v5
41
+ with:
42
+ python-version: '3.12'
43
+ cache: pip
44
+ cache-dependency-path: pyproject.toml
45
+
46
+ - name: Install package with dev extras
47
+ run: |
48
+ python -m pip install --upgrade pip
49
+ pip install -e ".[dev]"
50
+
51
+ - name: Run test suite
52
+ run: pytest tests/ -v
53
+
54
+ build:
55
+ name: Build & validate distribution
56
+ needs: test
57
+ runs-on: ubuntu-latest
58
+ steps:
59
+ - uses: actions/checkout@v4
60
+
61
+ - name: Set up Python
62
+ uses: actions/setup-python@v5
63
+ with:
64
+ python-version: '3.12'
65
+
66
+ - name: Install build tooling
67
+ run: python -m pip install --upgrade pip build twine
68
+
69
+ - name: Build sdist and wheel
70
+ run: python -m build
71
+
72
+ - name: Validate distribution metadata
73
+ run: twine check --strict dist/*
74
+
75
+ - name: Upload distribution artifacts
76
+ uses: actions/upload-artifact@v4
77
+ with:
78
+ name: dist
79
+ path: dist/
80
+
81
+ publish-pypi:
82
+ name: Publish to PyPI
83
+ # Only when a GitHub Release is published. A plain tag push runs just
84
+ # `test` + `build` above (a validation dry run) and stops here.
85
+ if: github.event_name == 'release'
86
+ needs: build
87
+ runs-on: ubuntu-latest
88
+ environment:
89
+ name: pypi
90
+ url: https://pypi.org/p/sft-wick
91
+ permissions:
92
+ id-token: write # mandatory for Trusted Publishing (OIDC)
93
+ steps:
94
+ - name: Download distribution artifacts
95
+ uses: actions/download-artifact@v4
96
+ with:
97
+ name: dist
98
+ path: dist/
99
+
100
+ - name: Publish to PyPI
101
+ uses: pypa/gh-action-pypi-publish@release/v1
102
+
103
+ github-release:
104
+ name: Sign artifacts and attach to the GitHub Release
105
+ if: github.event_name == 'release'
106
+ needs: publish-pypi
107
+ runs-on: ubuntu-latest
108
+ permissions:
109
+ contents: write # to upload assets to the release
110
+ id-token: write # to mint the Sigstore signing certificate
111
+ steps:
112
+ - name: Download distribution artifacts
113
+ uses: actions/download-artifact@v4
114
+ with:
115
+ name: dist
116
+ path: dist/
117
+
118
+ - name: Sign the distribution with Sigstore
119
+ uses: sigstore/gh-action-sigstore-python@v3.0.0
120
+ with:
121
+ inputs: >-
122
+ ./dist/*.tar.gz
123
+ ./dist/*.whl
124
+
125
+ - name: Upload signed artifacts to the GitHub Release
126
+ # Pass the tag through an env var rather than interpolating it
127
+ # directly into the shell, to avoid any script-injection risk.
128
+ env:
129
+ GITHUB_TOKEN: ${{ github.token }}
130
+ RELEASE_TAG: ${{ github.event.release.tag_name }}
131
+ # `--clobber` keeps the step idempotent if a later re-run re-uploads
132
+ # assets; `dist/*` (not `dist/**`) is an explicit flat-glob of the
133
+ # sdist, wheel, and their Sigstore `*.sigstore.json` bundles.
134
+ run: >-
135
+ gh release upload "$RELEASE_TAG"
136
+ dist/*
137
+ --repo "${{ github.repository }}"
138
+ --clobber
@@ -0,0 +1,21 @@
1
+ .pytest_cache/
2
+ .ruff_cache/
3
+ .mypy_cache/
4
+ *cache/
5
+ .benchmarks/
6
+ __pycache__/
7
+ **/__pycache__/
8
+ *.pyc
9
+ .ipynb_checkpoints/
10
+ .claude/
11
+ CLAUDE.md
12
+ HANDOFF*
13
+ paper/
14
+ archived/
15
+
16
+ # Build / generated artifacts
17
+ .DS_Store
18
+ docs/_build/
19
+ examples/demo_workflow_plot.png
20
+ examples/demo1/__pycache__/
21
+ examples/demo2/__pycache__/
@@ -0,0 +1,17 @@
1
+ version: 2
2
+
3
+ build:
4
+ os: ubuntu-22.04
5
+ tools:
6
+ python: "3.12"
7
+ apt_packages:
8
+ - pandoc
9
+
10
+ sphinx:
11
+ configuration: docs/conf.py
12
+
13
+ python:
14
+ install:
15
+ - method: pip
16
+ path: .
17
+ - requirements: docs/requirements.txt
@@ -0,0 +1,28 @@
1
+ {
2
+ "title": "sft-wick: Feynman diagrammatic expansion and evaluation for statistical field theory",
3
+ "description": "<p><strong>If you use this software, please cite the accompanying paper:</strong> Z. Zhang, <em>sft-wick: A formalism and package for Feynman-diagram expansion and evaluation in stochastic field theories</em>, arXiv:2606.19480 (2026), <a href=\"https://arxiv.org/abs/2606.19480\">https://arxiv.org/abs/2606.19480</a>. This Zenodo record archives the software; the paper is the primary reference.</p> An open-source Python package that constructs diagram tables and computes their integrals numerically for stochastic field dynamics cast into a path-integral (Martin-Siggia-Rose) formulation. Given an action and an observable, it enumerates topologically distinct Feynman diagrams, derives their algebraic coefficients, and evaluates the resulting diagram integrals from user-supplied response and cumulant functions. The core algorithm enumerates spatial topologies before routing component indices, avoiding contraction-by-contraction Wick expansion; response-field constraints (vanishing response-response contractions, the Ito prescription, absence of causal response loops) are enforced during enumeration.",
4
+ "upload_type": "software",
5
+ "license": "BSD-3-Clause",
6
+ "access_right": "open",
7
+ "creators": [
8
+ {
9
+ "name": "Zhang, Zheng",
10
+ "orcid": "0000-0002-9154-2803"
11
+ }
12
+ ],
13
+ "keywords": [
14
+ "statistical field theory",
15
+ "Wick's theorem",
16
+ "Feynman diagrams",
17
+ "Martin-Siggia-Rose formalism",
18
+ "perturbation theory",
19
+ "symbolic computation"
20
+ ],
21
+ "related_identifiers": [
22
+ {
23
+ "identifier": "arXiv:2606.19480",
24
+ "relation": "isSupplementTo",
25
+ "scheme": "arxiv"
26
+ }
27
+ ]
28
+ }
@@ -0,0 +1,262 @@
1
+ # Changelog
2
+
3
+ ## 0.2.0 — 2026-07-25
4
+
5
+ First release on PyPI (`pip install sft-wick`).
6
+
7
+ ### Features
8
+
9
+ - **`NonLocalVertex(equal_time=True)`** — opt-in mode for non-local
10
+ vertices whose `coupling` callable encodes an **equal-shell** /
11
+ **equal-time** connected cumulant (one common cosmology use case:
12
+ `canoes.sachs.compute_kappa3_zeta_table` which returns the
13
+ equal-shell bispectrum `ζ_eq(γ_12, γ_23, γ_31; λ)` for the LSS
14
+ matter / potential 3-point function).
15
+
16
+ When `equal_time=True`, the `m` time legs of that vertex collapse
17
+ into a **single** integration variable while the `m` spatial legs
18
+ remain independent (so the callable still receives `m` distinct
19
+ positions and only one shared time replicated `m` times in
20
+ `t_list`). This matches the action-level relation
21
+ `κ^(m)(z_1,...,z_m) ≈ δ(λ_1−λ_2) … δ(λ_{m−1}−λ_m) · κ_eq(n_1,...,n_m; λ)`
22
+ used in single-shell / Limber-like cosmological approximations.
23
+
24
+ The default `equal_time=False` is unchanged — the full
25
+ cross-spacetime cumulant `κ^(m)(z_1,...,z_m)` with `m` independent
26
+ `(n, λ)` integrations is still the canonical contract.
27
+
28
+ Failure mode this guards against: passing an equal-shell ζ as
29
+ `coupling` without `equal_time=True` lets sft-wick faithfully
30
+ sweep `m` independent times, contributing a spurious `(t_max)^(m−1)`
31
+ factor of integration measure (e.g. `~5.6×10^6` for `m=3` and
32
+ `t_max=2360` Mpc). This was the diagnosis-of-record for the
33
+ STF_lensing path-integral lensing Order-2 FK ⟨κκ⟩ bug.
34
+
35
+ Implementation spans `workflow/specs.py`, `workflow/system.py`,
36
+ `workflow/config.py`, `vertices.py`, `perturbation.py`, and
37
+ `evaluate.py`. Threaded through as
38
+ `Vertex(equal_time=...)` → `VertexInstance.equal_time_aliases` →
39
+ `DiagramTerm.equal_time_aliases` →
40
+ `SpatialStructure.equal_time_aliases`, with time-alias resolution
41
+ in `_times` of the QMC + Gauss-Legendre paths and in
42
+ `DiagramIntegrand.evaluate`. Diagram topology (R / C propagator
43
+ routing, direction groups) is unchanged — only the time-integration
44
+ Jacobian and the callable's `t_list` payload differ.
45
+
46
+ YAML usage::
47
+
48
+ nonlocal_vertices:
49
+ - name: K
50
+ order: 3
51
+ coupling_module: kappa3_callable.py
52
+ coupling_attr: coupling_fn
53
+ equal_time: true
54
+
55
+ See `docs/user_guide/workflow.rst` for the full discussion and
56
+ `docs/notes/equal_time_nonlocal_vertex.md` for the design note.
57
+ Regression coverage: `tests/test_equal_time_nonlocal.py`
58
+ (11 tests covering alias plumbing, static evaluation, dynamic
59
+ callables, and Jacobian ratio).
60
+
61
+ - **`NonLocalVertex(already_R_contracted=True)`** — opt-in mode where
62
+ the user-supplied callable returns the **R-contracted** form
63
+ `κ^(m)_R(γ; z_1', …, z_m') := ∫ ∏ R(z_i', z_i) · κ^(m) dz_1…dz_m`
64
+ evaluated at the **partner** (outer) spacetime points rather than
65
+ the κ's own legs. When set, sft-wick
66
+
67
+ * tags the m R-propagators attached to this vertex's ψ legs as
68
+ **absorbed** — they remain in the diagram graph (so direction
69
+ groups continue to identify the leg with its partner) but
70
+ contribute a factor of 1 instead of the usual `R_time` value;
71
+ * aliases each leg's time onto its Wick partner's time via the
72
+ existing `equal_time_aliases` machinery, so the m leg-time
73
+ integration variables drop out of the simplex;
74
+ * feeds the callable `(n_list, t_list)` with the **partner**
75
+ coordinates instead of the leg's own.
76
+
77
+ Cuts integration dimensionality by `m` per such vertex (e.g. an
78
+ order-2 F+K diagram becomes 1-D after absorbing K's 3 legs). The
79
+ surviving integrand is also **smoother** because the narrow-kernel
80
+ peak has been folded into κ^(m)_R, so Gauss-Legendre converges
81
+ exponentially with far fewer nodes. Motivating use case: canoes'
82
+ squeezed κ³ at `ℓ_max = 5000, z_s = 1100`, where the raw kernel
83
+ has diagonal width `Δχ ~ 2 Mpc/h` and the per-leg χ-integration
84
+ would otherwise demand `~10^11` quadrature points.
85
+
86
+ Mutually exclusive with `equal_time=True` (rejected at
87
+ construction). Default `already_R_contracted=False` is unchanged.
88
+
89
+ Implementation spans `workflow/specs.py`, `workflow/system.py`,
90
+ `workflow/config.py`, `vertices.py`, `perturbation.py`, and
91
+ `evaluate.py`. Threaded through as
92
+ `Vertex(already_R_contracted=...)` →
93
+ `DiagramTerm.r_absorbed_pairs` (collected per-routing in
94
+ `_collect_r_absorbed_pairs`, merged into `equal_time_aliases`) →
95
+ `SpatialStructure.r_absorbed_pairs`, with R-factor skipping
96
+ centralised in `evaluate.py::_kept_r_propagators` (used by all
97
+ five R-product sites: scalar / matrix per-sample, QMC-vectorised,
98
+ GL tensor-product, integrate-over external).
99
+
100
+ YAML usage::
101
+
102
+ nonlocal_vertices:
103
+ - name: K
104
+ order: 3
105
+ coupling_module: kappa3_R_callable.py
106
+ coupling_attr: coupling_fn
107
+ already_R_contracted: true
108
+
109
+ Companion utility `sft_wick.build_R_contracted_callable` wraps a
110
+ raw κ^(m) + R kernel into the R-contracted form via brute-force
111
+ trapezoid quadrature on a user χ-grid — for validation comparand
112
+ use; production callers should supply the analytical / pre-
113
+ tabulated κ^(m)_R directly (e.g. canoes' FFTlog-of-W chain).
114
+
115
+ See `docs/user_guide/workflow.rst` and
116
+ `docs/notes/R_contracted_nonlocal_vertex.md` for full
117
+ documentation. Regression coverage:
118
+ `tests/test_R_contracted_vertex.py` (15 tests including the
119
+ machine-precision constant-κ³ equivalence at `rtol=1e-12` and the
120
+ four-way per-sample × vectorised contract check).
121
+
122
+ ### Performance
123
+
124
+ - **Test-suite wall time cut ~3.5×** (from ~18 min to ~5 min for the
125
+ full 275-test suite on M-series). The profile showed two tests
126
+ accounting for 80 % of runtime — both were building expensive
127
+ C-propagator caches that the test's tolerance did not actually
128
+ require:
129
+ - `test_CF3_full_run_matches_L1_reference` (585 s → 72 s): this is
130
+ an equivalence test (YAML pipeline vs. direct Python — rtol=1e-10
131
+ is bit-identity), so the propagator grid only needs internal
132
+ consistency, not physical precision. `n_grid_t` 30 → 12,
133
+ `t_max` 3.0 → 2.0, `n_samples` 1024 → 256.
134
+ - `test_T4_time_dependent_gamma_end_to_end` (302 s → 24 s): an
135
+ order-0 smoke test whose single C-evaluation did not justify a
136
+ 30² dblquad grid. `n_grid_t` 30 → 10, `n_grid_cache` 120 → 40,
137
+ `t_max` 2.0 → 1.5.
138
+ - `test_W3_white_noise_absorbed_into_translation_spline`
139
+ (97 s → 32 s): lazy-spline cache cost dominated — `n_grid_t`
140
+ 25 → 15 (quadratic win); `n_samples` 2¹³ → 2¹¹ (negligible since
141
+ QMC is not the bottleneck here).
142
+ - `test_spline_matches_split_dblquad` (77 s → 54 s):
143
+ `n_grid` 30 → 25 with worst-case-O(h⁴) tolerance relaxed from
144
+ 2e-3 → 3e-3 (still within 2× the theoretical bound).
145
+ - `test_WF4_end_to_end_matches_validate_phase5` (44 s → 18 s): the
146
+ sweep covered 4 × 2 grid points but the reference values only
147
+ pinned 3 × 1 of them — dropped `y=2.5` and `t_final=1.0`.
148
+
149
+ - **`integrate_diagrams` default `n_jobs` flipped from `1` to `-1`.**
150
+ The guard `len(diagram_terms) <= 2 → sequential fallback` already
151
+ prevents joblib's ~1 s startup from dominating on trivial batches,
152
+ so the new default is a free speedup for any order-2+ batch
153
+ evaluation. Explicit `n_jobs=1` callers (determinism-comparison
154
+ tests like C5) are unaffected.
155
+
156
+ ### Cleanups
157
+
158
+ - Removed **dead `System.explicit_C` field** (declared but never read
159
+ anywhere in the codebase; replaced by the `c_closed_form` kwarg on
160
+ `System.propagators()` and `Propagators.build()`). Docstring for
161
+ `System.explicit_R` now points to `ExplicitR` as the preferred
162
+ structured alternative.
163
+ - Removed 10 unused imports flagged by ruff F401 across
164
+ `diagrams.py`, `drawing.py`, `evaluate.py`, `perturbation.py`,
165
+ `vertices.py`, `workflow/expansion.py`, `workflow/result.py`,
166
+ `workflow/specs.py`.
167
+ - Removed 3 orphaned local assignments flagged by F841
168
+ (`evaluate.py:ids`, `perturbation.py:sum_idx_dims`,
169
+ `simplify.py:ref_flipped`).
170
+
171
+ ### Features
172
+
173
+ - **Time-dependent linear operator** (`workflow/specs.py::DiagonalA`).
174
+ `gamma` may now be a callable ``γ(t) -> np.ndarray(shape=(N,))``;
175
+ the wrapper pre-computes ``Γ_a(t) = ∫_0^t γ_a(τ) dτ`` on a grid
176
+ and caches it as a cubic spline for O(1) R lookups. Diagonal
177
+ case only — full-matrix time-dependent A still requires
178
+ `ExplicitR` with a user-supplied time-ordered matrix exponential.
179
+ New tests in `tests/test_diagonal_A_time_dependent.py`.
180
+
181
+ - **Dynamic coupling evaluation for non-local vertices**
182
+ (`evaluate.py::DynamicCouplingPromise` +
183
+ `perturbation.py::DiagramTerm.build_integrand`). `coupling_values`
184
+ now accepts callables (`fn(n_list, t_list) -> (N,)*m tensor`) for
185
+ spacetime-dependent κ^{(n)}. Demo2's FK channel is now a one-line
186
+ `expansion.evaluate(vertex_types={'FK'})` — no bespoke integrator.
187
+ See `examples/demo2/validate_FK_dynamic.py`.
188
+
189
+ - **Observable convention kwarg** `integrate_over` on
190
+ `integrate_moment` / `Expansion.evaluate` / `.sweep` — per-external
191
+ control over fixed-time (`None`, default, matches ⟨φ(t_f) φ(t_f)⟩
192
+ physics convention) vs time-integrated (`"all"` or a subset).
193
+ The previous default was time-integrated; callers that want that
194
+ now pass `integrate_over="all"` explicitly.
195
+
196
+ - **L2 — YAML config + CLI**. `sft-wick run config.yaml` executes
197
+ an end-to-end workflow (expand → propagators → sweep → output);
198
+ `--override key=value` patches fields without editing the file.
199
+ `c_closed_form_module` and vertex `coupling_module` fields wire
200
+ user Python modules in. Full round-trip tests in
201
+ `tests/test_workflow_config.py` (CF1–CF5). Example configs:
202
+ `examples/demo1_config.yaml`, `examples/demo2_config.yaml`.
203
+
204
+ - **L1 — high-level workflow API**: `System` (physics spec),
205
+ `Expansion` (diagram-level inspection + per-channel integration),
206
+ `Propagators` (thin cache wrapper), `Result` / `SweepResult`
207
+ (pandas-backed structured output). Bare F/κ^{(n)} tensors —
208
+ wrapper applies MSR factors `-(i^n)/n!` automatically.
209
+ `expansion.by_vertex_type(order)` classifies diagrams by
210
+ F/K composition (demo2-style FF/FK split). New tests
211
+ `tests/test_workflow.py` (WF1–WF5).
212
+
213
+ - **Parallelised lazy propagator spline build** (`evaluate.py`).
214
+ `_LazyTimeSplineCache._build` now uses `joblib.Parallel` when
215
+ `n_jobs != 1` (previously silently ignored for lazy mode).
216
+ `examples/demo1/validate_R_C_derivation.py` went from 400 s → 39 s
217
+ on M-series (10.3× speedup) with the `n_jobs=-1` plumbing.
218
+
219
+ ### Bug fixes
220
+
221
+ - **`DiagramIntegrand.evaluate` scalar path now honours
222
+ `fixed_indices`** (evaluate.py:1322, 1424). Previously
223
+ `_resolve_component(il, {})` was passed `{}` instead of
224
+ `self.fixed_indices`, so observable component labels (`'a'`,
225
+ `'b'`) failed to resolve and the code summed over all
226
+ components — an N-factor overcounting at order 0. The
227
+ vectorised path already handled `fi` correctly; this brings the
228
+ scalar path in sync.
229
+
230
+ ### Bug fixes
231
+
232
+ - **Fixed incorrect C-propagator component resolution in numerical integration**
233
+ (`evaluate.py`). When `build_integrand()` was called with `fixed_indices`
234
+ (e.g. `{'a': 1, 'b': 1}` for selecting a specific correlator component),
235
+ the fixed index names (like `'b'`) were not passed through to the QMC/GL
236
+ integration routines. `_resolve_component('b', idx_map)` failed to
237
+ resolve the index, causing the integrator to **sum over all field
238
+ components** instead of selecting the fixed one. For `N_comp = 2` with
239
+ `iso_C`, this produced a factor-of-2 overcounting per unresolved
240
+ C-propagator index, making perturbative corrections ~1.3–1.8× too large
241
+ depending on the diagram topology.
242
+
243
+ **Fix:** `DiagramIntegrand` now stores the `fixed_indices` dict and
244
+ both `integrate_two_point_qmc` and `integrate_moment_qmc_vectorized`
245
+ merge it into the index map before calling `_resolve_component`.
246
+
247
+ Affected files:
248
+ - `sft_wick/evaluate.py` — added `fixed_indices` field to
249
+ `DiagramIntegrand`; updated `integrate_two_point_qmc` and
250
+ `integrate_moment_qmc_vectorized` to use `ig.fixed_indices`.
251
+ - `sft_wick/perturbation.py` — `build_integrand()` now passes
252
+ `fixed_indices` to the `DiagramIntegrand` constructor.
253
+
254
+ - **Fixed coupling symmetry bug in `compute_moment_numerical`**
255
+ (`perturbation.py`). The canonical grouping of component routings
256
+ reused reference propagators and static coupling symbols, causing
257
+ asymmetric coupling tensors (e.g. `F[1,0,1]=1, F[1,1,0]=0`) to
258
+ produce wrong results. Symmetric tensors were unaffected.
259
+
260
+ **Fix:** Each component routing now produces its own `DiagramTerm`
261
+ with routing-specific propagators. This removes the incorrect
262
+ canonical grouping but increases the number of diagram terms.
@@ -0,0 +1,47 @@
1
+ cff-version: 1.2.0
2
+ message: "If you use this software, please cite both the software and the paper below."
3
+ title: "sft-wick: Feynman diagrammatic expansion and evaluation for statistical field theory"
4
+ abstract: >-
5
+ An open-source Python package that constructs diagram tables and computes
6
+ their integrals numerically for stochastic field dynamics cast into a
7
+ path-integral (Martin-Siggia-Rose) formulation. Given an action and an
8
+ observable, it enumerates topologically distinct Feynman diagrams, derives
9
+ their algebraic coefficients, and evaluates the resulting diagram integrals
10
+ from user-supplied response and cumulant functions. The setting targeted
11
+ includes multi-component, multi-dimensional fields with matrix propagators,
12
+ tensor-valued couplings, and non-Gaussian driving noise specified by
13
+ arbitrary n-point cumulants. The core algorithm enumerates spatial
14
+ topologies before routing component indices, avoiding
15
+ contraction-by-contraction Wick expansion; response-field constraints
16
+ (vanishing response-response contractions, the Ito prescription, absence of
17
+ causal response loops) are enforced during enumeration.
18
+ type: software
19
+ authors:
20
+ - family-names: Zhang
21
+ given-names: Zheng
22
+ email: zzhang0123@gmail.com
23
+ orcid: "https://orcid.org/0000-0002-9154-2803"
24
+ version: "0.2.0"
25
+ license: BSD-3-Clause
26
+ repository-code: "https://github.com/StatFieldTheory/sft-wick"
27
+ url: "https://sft-wick.readthedocs.io"
28
+ keywords:
29
+ - statistical field theory
30
+ - Wick's theorem
31
+ - Feynman diagrams
32
+ - Martin-Siggia-Rose formalism
33
+ - perturbation theory
34
+ - symbolic computation
35
+ preferred-citation:
36
+ type: article
37
+ title: "sft-wick: A formalism and package for Feynman-diagram expansion and evaluation in stochastic field theories"
38
+ authors:
39
+ - family-names: Zhang
40
+ given-names: Zheng
41
+ orcid: "https://orcid.org/0000-0002-9154-2803"
42
+ year: 2026
43
+ url: "https://arxiv.org/abs/2606.19480"
44
+ identifiers:
45
+ - type: other
46
+ value: "arXiv:2606.19480"
47
+ description: arXiv preprint
sft_wick-0.2.0/LICENSE ADDED
@@ -0,0 +1,13 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2026-present, Zheng Zhang
4
+
5
+ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
6
+
7
+ 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
8
+
9
+ 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
10
+
11
+ 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
12
+
13
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.