pyfly-lightning 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. pyfly_lightning-0.1.0/CHANGELOG.md +189 -0
  2. pyfly_lightning-0.1.0/CITATION.cff +35 -0
  3. pyfly_lightning-0.1.0/LICENSE +21 -0
  4. pyfly_lightning-0.1.0/MANIFEST.in +11 -0
  5. pyfly_lightning-0.1.0/PKG-INFO +428 -0
  6. pyfly_lightning-0.1.0/PLAN.md +464 -0
  7. pyfly_lightning-0.1.0/README.md +363 -0
  8. pyfly_lightning-0.1.0/docs/README.md +7 -0
  9. pyfly_lightning-0.1.0/docs/architecture.md +937 -0
  10. pyfly_lightning-0.1.0/docs/census-wm-ports.md +132 -0
  11. pyfly_lightning-0.1.0/docs/quickstart.md +97 -0
  12. pyfly_lightning-0.1.0/examples/delayed_match.py +213 -0
  13. pyfly_lightning-0.1.0/examples/emotion_pipeline.py +235 -0
  14. pyfly_lightning-0.1.0/examples/freeze_prune.py +147 -0
  15. pyfly_lightning-0.1.0/examples/lowdata.py +189 -0
  16. pyfly_lightning-0.1.0/examples/mnist.py +181 -0
  17. pyfly_lightning-0.1.0/examples/quickstart.py +28 -0
  18. pyfly_lightning-0.1.0/examples/reports/delayed-match.json +83 -0
  19. pyfly_lightning-0.1.0/examples/reports/emotion-pipeline.json +218 -0
  20. pyfly_lightning-0.1.0/examples/reports/emotion-unfrozen.json +200 -0
  21. pyfly_lightning-0.1.0/examples/reports/freeze-prune.json +83 -0
  22. pyfly_lightning-0.1.0/examples/reports/iris-fit.json +41 -0
  23. pyfly_lightning-0.1.0/examples/reports/lowdata-iris-3seed.json +64 -0
  24. pyfly_lightning-0.1.0/examples/reports/lowdata-iris.json +65 -0
  25. pyfly_lightning-0.1.0/examples/reports/mnist-smoke.json +36 -0
  26. pyfly_lightning-0.1.0/examples/reports/prune-evo.json +96 -0
  27. pyfly_lightning-0.1.0/examples/reports/reward-bus.json +99 -0
  28. pyfly_lightning-0.1.0/examples/reports/rl-dqn.json +48 -0
  29. pyfly_lightning-0.1.0/examples/reward_bus.py +131 -0
  30. pyfly_lightning-0.1.0/examples/rl_dqn.py +119 -0
  31. pyfly_lightning-0.1.0/pyfly_lightning/__init__.py +29 -0
  32. pyfly_lightning-0.1.0/pyfly_lightning/__main__.py +5 -0
  33. pyfly_lightning-0.1.0/pyfly_lightning/api.py +322 -0
  34. pyfly_lightning-0.1.0/pyfly_lightning/auto.py +128 -0
  35. pyfly_lightning-0.1.0/pyfly_lightning/cli.py +107 -0
  36. pyfly_lightning-0.1.0/pyfly_lightning/data/__init__.py +7 -0
  37. pyfly_lightning-0.1.0/pyfly_lightning/data/_checksums.json +14 -0
  38. pyfly_lightning-0.1.0/pyfly_lightning/data/download.py +98 -0
  39. pyfly_lightning-0.1.0/pyfly_lightning/data/graph.py +433 -0
  40. pyfly_lightning-0.1.0/pyfly_lightning/data/registry.py +127 -0
  41. pyfly_lightning-0.1.0/pyfly_lightning/data/schema.py +212 -0
  42. pyfly_lightning-0.1.0/pyfly_lightning/envs.py +82 -0
  43. pyfly_lightning-0.1.0/pyfly_lightning/freeze.py +42 -0
  44. pyfly_lightning-0.1.0/pyfly_lightning/gates.py +148 -0
  45. pyfly_lightning-0.1.0/pyfly_lightning/model/__init__.py +8 -0
  46. pyfly_lightning-0.1.0/pyfly_lightning/model/nulls.py +70 -0
  47. pyfly_lightning-0.1.0/pyfly_lightning/model/routing.py +384 -0
  48. pyfly_lightning-0.1.0/pyfly_lightning/model/substrate.py +293 -0
  49. pyfly_lightning-0.1.0/pyfly_lightning/prune.py +202 -0
  50. pyfly_lightning-0.1.0/pyfly_lightning/prune_evo.py +278 -0
  51. pyfly_lightning-0.1.0/pyfly_lightning/reward.py +338 -0
  52. pyfly_lightning-0.1.0/pyfly_lightning/sim/__init__.py +3 -0
  53. pyfly_lightning-0.1.0/pyfly_lightning/sim/bench.py +113 -0
  54. pyfly_lightning-0.1.0/pyfly_lightning/sim/lif.py +195 -0
  55. pyfly_lightning-0.1.0/pyfly_lightning/sim/probe.py +135 -0
  56. pyfly_lightning-0.1.0/pyfly_lightning/train/__init__.py +16 -0
  57. pyfly_lightning-0.1.0/pyfly_lightning/train/controls.py +73 -0
  58. pyfly_lightning-0.1.0/pyfly_lightning/train/es.py +154 -0
  59. pyfly_lightning-0.1.0/pyfly_lightning/train/module.py +306 -0
  60. pyfly_lightning-0.1.0/pyfly_lightning/train/outer.py +167 -0
  61. pyfly_lightning-0.1.0/pyfly_lightning/train/pipeline.py +255 -0
  62. pyfly_lightning-0.1.0/pyfly_lightning/train/rl.py +370 -0
  63. pyfly_lightning-0.1.0/pyfly_lightning/train/tasks.py +73 -0
  64. pyfly_lightning-0.1.0/pyfly_lightning.egg-info/PKG-INFO +428 -0
  65. pyfly_lightning-0.1.0/pyfly_lightning.egg-info/SOURCES.txt +87 -0
  66. pyfly_lightning-0.1.0/pyfly_lightning.egg-info/dependency_links.txt +1 -0
  67. pyfly_lightning-0.1.0/pyfly_lightning.egg-info/entry_points.txt +2 -0
  68. pyfly_lightning-0.1.0/pyfly_lightning.egg-info/requires.txt +25 -0
  69. pyfly_lightning-0.1.0/pyfly_lightning.egg-info/top_level.txt +1 -0
  70. pyfly_lightning-0.1.0/pyproject.toml +68 -0
  71. pyfly_lightning-0.1.0/setup.cfg +4 -0
  72. pyfly_lightning-0.1.0/tests/conftest.py +35 -0
  73. pyfly_lightning-0.1.0/tests/test_api.py +86 -0
  74. pyfly_lightning-0.1.0/tests/test_freeze_prune.py +113 -0
  75. pyfly_lightning-0.1.0/tests/test_gates.py +71 -0
  76. pyfly_lightning-0.1.0/tests/test_generic_and_auto.py +292 -0
  77. pyfly_lightning-0.1.0/tests/test_outer_evo.py +138 -0
  78. pyfly_lightning-0.1.0/tests/test_probe_and_pipeline.py +169 -0
  79. pyfly_lightning-0.1.0/tests/test_registry.py +39 -0
  80. pyfly_lightning-0.1.0/tests/test_reward.py +167 -0
  81. pyfly_lightning-0.1.0/tests/test_rl.py +79 -0
  82. pyfly_lightning-0.1.0/tests/test_routing.py +92 -0
  83. pyfly_lightning-0.1.0/tests/test_schema_and_graph.py +64 -0
  84. pyfly_lightning-0.1.0/tests/test_sequence_and_es.py +122 -0
  85. pyfly_lightning-0.1.0/tests/test_sim.py +91 -0
  86. pyfly_lightning-0.1.0/tests/test_train.py +71 -0
  87. pyfly_lightning-0.1.0/tools/census_wm_ports.py +185 -0
  88. pyfly_lightning-0.1.0/tools/crossvalidate_flybrain.py +201 -0
  89. pyfly_lightning-0.1.0/tools/topology_null_check.py +94 -0
@@ -0,0 +1,189 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0 (2026-09-20)
4
+
5
+ ### Changed (packaging)
6
+ - **The import name is `pyfly_lightning`, not `pyfly`.** PyPI already has an unrelated
7
+ `pyfly` (a load-testing framework) that installs a top-level `pyfly/` package. Two
8
+ distributions owning the same top-level directory get merged in `site-packages`: one
9
+ `__init__.py` wins and uninstalling either removes the other's files. Renamed to follow
10
+ the `pytorch-lightning` / `pytorch_lightning` convention. Verified by installing both
11
+ into one environment and uninstalling each in turn -- the other survives intact.
12
+ - The console script is `pyfly-lightning` (was `pyfly`), matching the distribution name,
13
+ with `python -m pyfly_lightning` as an equivalent. The other package claims no console
14
+ script, but keeping the command distinct avoids reviving the confusion the rename fixes.
15
+
16
+ First release with a usable API. Data layer, simulator, staged pipeline, controls,
17
+ automatic planner and CLI.
18
+
19
+ **Read this before citing any number.** Every claim about the substrate's value that
20
+ this project measured came back negative: the connectome's specific wiring is
21
+ interchangeable with its own degree-preserving rewiring, and under training it loses
22
+ to a permutation of its own weights. What IS supported is that the substrate plus an
23
+ automatically optimised interface works, and that the substrate need not be the fly.
24
+ See `docs/architecture.md` sections 11 and 22-23 and the README.
25
+
26
+ ### Added
27
+ - `pyfly_lightning.auto.plan`: derives substrate size, pools, latent, T and the ES budget from the task's
28
+ shape, with `Plan.reason` recording the measurement behind every choice.
29
+ - `pyfly.fit(X, y)`: one call from a task to a fitted model plus the mandatory controls and a
30
+ verdict.
31
+ - `SparseConnectome.from_edgelist` and `NeuronTable.from_dataframe`: bring ANY connectome, with
32
+ arbitrary node labels and column names. `Substrate.build` now keeps label space and code space
33
+ separate, canonicalises custom column names at the boundary, and raises a named error when the
34
+ I/O boundary is undeclared instead of failing much later.
35
+ - `Substrate.positions_of()`, `pyfly.load()`, `FitResult`.
36
+ - 103 tests.
37
+ - `pyfly_lightning.sim.probe`: `echo_probe` (with a `late_echo` metric that separates wave
38
+ propagation from recirculation), `separability_probe`, `broadcast_coverage`.
39
+ - `LifTorch(differentiable=True)`: a smooth gate that keeps the autograd graph, so the
40
+ whole encoder -> substrate -> decoder pipeline trains end to end. Plus
41
+ `set_trainable_weights()`, which keeps the connectome's wiring while making every edge
42
+ weight a parameter.
43
+ - `pyfly_lightning.train.pipeline.StagedPipeline`: encode (ES) -> decode (backprop) -> modulate
44
+ (DAN readback), with an inference-safe modulation channel.
45
+ - `examples/emotion_pipeline.py`: the six-arm comparison, gated.
46
+ - 91 tests.
47
+ - `pyfly_lightning.gates.structure_function_gate`: the M1 structure->function gate with its shuffled-weight
48
+ control, reproducing Shiu et al.'s protocol with the target discovered from the data (their MN9
49
+ is absent from the public FlyWire 783 release and from `sez_neurons.pickle`).
50
+ - `Substrate.build(extra_input_ids=)`, since stratified sensory sampling yields only a couple of
51
+ neurons per class -- far too few to drive a named circuit.
52
+ - `pyfly_lightning.sim.bench` memory-ceiling reporting.
53
+ - 73 tests.
54
+ - `pyfly_lightning.train.outer`: `StudySpace` / `Param` / `OuterLoop`, using Vizier when installed and a
55
+ randomised search otherwise, recording which backend actually ran. `default_space()` covers the
56
+ knobs PLAN.md v0.3 lists.
57
+ - `pyfly_lightning.train.rl.PPOAgent`: actor-critic with clipped objective, entropy bonus and normalised
58
+ advantage. `CueDelayChoice(act_every_step=True)` gives a genuinely multi-step trajectory.
59
+ - `pyfly_lightning.prune_evo`: NSGA-II over group-level genomes with crowding distance and a Pareto front,
60
+ plus `group_labels_by_depth` / `_by_scc` / `_by_component`. Returns `front_collapsed` when the
61
+ front is degenerate instead of presenting it as a result.
62
+ - 68 tests.
63
+ - `pyfly_lightning.envs.CueDelayChoice`: cue, delay, one-shot choice. Balanced cues, ±1 reward.
64
+ - `pyfly_lightning.train.rl`: `ReplayBuffer`, `QHead`, `DQNAgent` (epsilon schedule, target network,
65
+ optional `RewardCalibrator` in the reward path) and `random_policy_baseline`. `feature_fn`
66
+ lets the pooled-feature control reuse the identical training loop.
67
+ - `examples/rl_dqn.py`, with random / rewired / pooled controls.
68
+ - 59 tests, including a regression test for the random-baseline seeding bug and a positive
69
+ control that DQN learns when the features carry the signal.
70
+ - `pyfly_lightning.reward`: `ObjectiveComposer` (frozen: a trainer may rescale the signal but never
71
+ reweight the task), `RewardCalibrator` (five methods, output bounded to [-1, +1]),
72
+ `CreditChannel` (multicast with latency and separate negative-valence targets),
73
+ `PlasticityPolicy` variants with `NoPlasticity` as the default, `PlasticPool` for
74
+ three-factor updates restricted to a pool's internal edges, and `run_manifest()`.
75
+ - `examples/reward_bus.py`, gated on three controls.
76
+ - 53 tests.
77
+ - `pyfly_lightning.freeze`: `FreezeState` for the four distinct freeze meanings (plasticity, adapter,
78
+ topology, state) plus `FrozenError`. ES refuses to write a frozen adapter; masking refuses
79
+ while `topology` is frozen.
80
+ - `pyfly_lightning.prune`: `MaskPruner` with a tolerance contract and rollback, multi-signal importance
81
+ (activity over every node, graph degree, optional causal ablation by group), protected I/O, and
82
+ a `PruneReport`. `FlyModule.node_activity()` records per-node spiking without the readout.
83
+ - `examples/freeze_prune.py`: freeze -> prune -> verify, gated.
84
+ - 37 tests.
85
+ - `pyfly_lightning.train.es`: antithetic OpenAI-ES with rank normalisation, optimising the encoder and
86
+ router (the parameters upstream of the gradient firewall, which no gradient can reach).
87
+ Fitness is closed-form: extract spike counts, solve a ridge readout analytically, score.
88
+ - `FlyModule.features(..., stacked=)`: feedforward and recurrent (sequence) modes, with tail steps
89
+ so the last frame can actually reach the outputs, and a time-resolved readout so ordering
90
+ survives. `forward()` is unchanged and uses `stacked_readout`.
91
+ - `Substrate.build(include_pools=)` and `Substrate.recurrence_report()`.
92
+ - `examples/delayed_match.py`: the decisive experiment, with all four controls.
93
+ - 32 tests.
94
+ - `pyfly_lightning.sim`: LIF dynamics with a numpy reference backend and a bit-identical torch backend;
95
+ sign-preserving L1 row normalisation (the signed-sum version silently deleted inhibition).
96
+ - `pyfly_lightning.model.substrate`: connected substrate slices grown from sampled inputs that are
97
+ guaranteed to contain reachable output neurons, plus drive calibration to a target input
98
+ firing rate.
99
+ - `pyfly_lightning.model.nulls`: degree-preserving rewiring (unique disjoint index sampling -- the
100
+ with-replacement version corrupted degree preservation), weight shuffling.
101
+ - `pyfly_lightning.train`: `FlyModule`, a real `lightning.LightningModule` when lightning is installed
102
+ (plain `nn.Module` fallback otherwise), `PooledFeatureMLP`, and the four-control report
103
+ contract with an `UNSUPPORTED` verdict by default.
104
+ - `examples/mnist.py`: the MNIST smoke test, with all four controls and a saved report.
105
+ - 27 tests; the suite runs green with data and skips cleanly without it.
106
+ - `pyfly_lightning.data`: dataset registry, sha256-pinned downloader, cache, `NeuronTable`,
107
+ `SparseConnectome`, and an auditable `SignReport` for the transmitter->sign assumption.
108
+ - `pyfly_lightning.model.routing`: structured `SensorySurface` + `GroupRouter`, parameterised by
109
+ connectome structure (optic-lobe hex columns, olfactory glomeruli, labelled lines)
110
+ rather than a dense matrix. `learnable=False` gives the reservoir baseline.
111
+ - `tools/census_wm_ports.py`: reproduces the working-memory port census.
112
+ - `docs/census-wm-ports.md`: why the central complex is the working-memory port.
113
+ - `docs/architecture.md`: the three-network design, training stages, gradient options.
114
+
115
+ ### Verified by cross-validation (section 25)
116
+ - Against `flybrain` (github.com/alextitonis/fly.ai), an independent implementation of the
117
+ same connectome with sha256-pinned prebuilt matrices:
118
+ graph reproduced exactly (166,700 nodes / 25,582,938 edges, identical sparsity,
119
+ max |difference| 5.96e-08 = float32 eps); dynamics agree to 5.9e-06 of 507,598 spikes
120
+ over 200 steps with per-neuron rate correlation r = 1.000000.
121
+ - `prepare_weights(normalize="input"|"output")`: the two projects normalize on opposite
122
+ axes. Both are L1 and sign-preserving; they are different dynamical systems, and our
123
+ recorded numbers are all under `"output"`.
124
+ - `tools/crossvalidate_flybrain.py` keeps the check reproducible.
125
+
126
+ ### Measured, low data (section 24)
127
+ - Iris, per-fold ES, 3 seeds x 5 folds: fly 0.7644 vs **rewired 0.7711**, crossing over
128
+ between seeds. CPU vs raw 4 features 0.8289.
129
+ - A first run showed fly +5.3pp over rewired; that was a leak (ES on all 150 samples
130
+ before CV) plus a single seed, and the corrected run removes it. Fifth independent
131
+ negative, and the cleanest.
132
+
133
+ ### Measured, staged pipeline (section 22)
134
+ - C1 replicated over seeds 0/1/2: +15.3, +14.5, +7.6 points; mean +12.5, positive in every
135
+ seed. The direct-input baseline itself moves 0.4507-0.4927, so the honest claim is "7.6 to
136
+ 15.3 points", not one number.
137
+ - Method 0.6087 vs direct input + PPL1 0.4560 -> **C1 holds, +15.3pp**.
138
+ - Dopamine channel 0.6087 vs 0.4927 without -> **C2 holds, +11.6pp** (+25.2pp unfrozen).
139
+ - Topology: fly 0.6087 vs rewired 0.6607 -> **C3 fails**; with trainable weights,
140
+ shuffled weights do best (0.7360), so the real weights are a worse prior than their own
141
+ permutation.
142
+ - The connectome's contribution that IS positive: CX converts a slice from a wire that
143
+ dies in 2 steps into one that recirculates for 60+ (`late_echo` 0.0 -> 30.0).
144
+
145
+ ### Measured, M1 gate
146
+ - real connectivity 1.00 vs shuffled weights 0.44 (delta +0.56) -> **PASS**. The paper reports
147
+ 100% vs 1/100; the real arm matches, the shuffled arm is weaker for four documented reasons.
148
+ - Ablation: with raw (un-normalised) weights both arms are 1.00 and the control is vacuous, because
149
+ the network saturates. Row normalisation is what makes the gate discriminating, not a
150
+ numerical convenience.
151
+
152
+ ### Measured, M6 throughput
153
+ - Refutes risk R2: 16,384 nodes / 528,013 edges uses 50.1 MB of a 6 GB card (99.2% headroom).
154
+ Latency is flat at ~0.15 ms/step, so throughput (~6,750 steps/s) is the constraint, not memory.
155
+
156
+ ### Measured, evolutionary pruning
157
+ - NSGA-II verified on a known synthetic front and on a synthetic objective that forces a real
158
+ trade-off.
159
+ - On the real substrate the Pareto front COLLAPSES: every non-dominated point shares one behaviour
160
+ score, because the grouping has one giant component (99% of nodes) and the model has almost no
161
+ behaviour to preserve (0.2113 against 10 classes, chance 0.10). Reported as
162
+ `front_collapsed: true`, not presented as a result.
163
+
164
+ ### Measured, RL
165
+ - DQN on cue-delay-choice: random 0.4725, fly 0.4850, rewired 0.4850 (identical), pooled 1.0000.
166
+ Fourth confirmation of the section 11 finding. The pooled control proves the task is solvable
167
+ and the loop is sound, so the substrate itself is not carrying the cue across the delay.
168
+
169
+ ### Measured, reward layer
170
+ - Bounded calibration across rewards spanning +-1e5 for all five methods.
171
+ - `NoPlasticity` is a verified no-op (`total|dW| = 0.0`); three-factor plasticity changes
172
+ weights (0.1629); an 8-step-delayed signal acts 256x more weakly than an aligned one
173
+ (0.000636 vs 0.1629) -- the empirical reason this is stimulation, not a loss.
174
+
175
+ ### Measured, pruning
176
+ - Freeze then mask-prune on MNIST: edges 166,600 -> 53,974 (-67.6%) with the held-out score
177
+ unchanged at 0.145, 1,429 of 4,096 nodes masked out, protected positions intact
178
+ (`examples/reports/freeze-prune.json`).
179
+
180
+ ### Measured, negative
181
+ - Delayed match-to-sample: fly 0.556 vs **rewired 0.591**, pooled MLP 1.000. The rewiring wins.
182
+ Third independent confirmation that the topology prior is not contributing
183
+ (`docs/architecture.md` sections 11 and 13).
184
+
185
+ ### Verified against MaleCNS v1.0
186
+ - 165,122 `Traced` neurons; 164,587 node graph; 25,563,197 significant-only directed edges.
187
+ - Declared I/O boundary: 15,896 inputs, 2,295 outputs.
188
+ - Input surface: 39,616 neurons in 1,269 structural groups; router 41,877 params at
189
+ `latent_dim=32, rank=0` vs 1,267,712 dense (30.3x).
@@ -0,0 +1,35 @@
1
+ cff-version: 1.2.0
2
+ message: "If you use this software, cite it and the underlying datasets."
3
+ title: pyfly-lightning
4
+ authors:
5
+ - name: NewJerseyStyle
6
+ abstract: >-
7
+ A framework in which the substrate is a frozen Drosophila connectome and the
8
+ only trained parts are encode/decode adapters around declared ports.
9
+ type: software
10
+ license: MIT
11
+ version: 0.1.0
12
+ date-released: "2026-09-17"
13
+ preferred-citation:
14
+ type: software
15
+ title: pyfly-lightning
16
+ year: 2026
17
+ references:
18
+ - type: article
19
+ title: "Neuronal wiring diagram of an adult brain"
20
+ year: 2024
21
+ journal: Nature
22
+ authors:
23
+ - family-names: Dorkenwald
24
+ - type: article
25
+ title: "Sexual dimorphism in the complete connectome of the Drosophila male central nervous system"
26
+ year: 2026
27
+ journal: Cell
28
+ authors:
29
+ - family-names: Berg
30
+ - type: article
31
+ title: "A Drosophila computational brain model reveals sensorimotor processing"
32
+ year: 2024
33
+ journal: Nature
34
+ authors:
35
+ - family-names: Shiu
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 pyfly-lightning contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,11 @@
1
+ include LICENSE
2
+ include README.md
3
+ include CHANGELOG.md
4
+ include CITATION.cff
5
+ include PLAN.md
6
+ recursive-include docs *.md
7
+ recursive-include examples *.py *.json
8
+ recursive-include tests *.py
9
+ recursive-include tools *.py
10
+ recursive-exclude * __pycache__
11
+ recursive-exclude * *.py[cod]