spikeforge 0.3.2__tar.gz → 0.3.4__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.
- {spikeforge-0.3.2/spikeforge.egg-info → spikeforge-0.3.4}/PKG-INFO +89 -7
- spikeforge-0.3.4/README.md +177 -0
- spikeforge-0.3.4/main.py +48 -0
- spikeforge-0.3.4/main_encodings.py +62 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/pyproject.toml +1 -1
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/__init__.py +2 -2
- spikeforge-0.3.4/spikeforge/affect/__init__.py +5 -0
- spikeforge-0.3.4/spikeforge/affect/regulator.py +77 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/benchmark/energy.py +13 -5
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/benchmark/memory.py +7 -1
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/cli/verify.py +23 -6
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/encoding/latency_trainer.py +2 -2
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/exporters/delta_exporter.py +2 -1
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/exporters/exporter.py +1 -1
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/exporters/latency_raster_exporter.py +2 -1
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/exporters/latency_video_exporter.py +2 -1
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/exporters/presentation_exporter.py +2 -1
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/exporters/random_spike_raster_exporter.py +2 -1
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/exporters/random_spike_video_exporter.py +2 -1
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/exporters/raster_exporter.py +4 -1
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/exporters/video_exporter.py +2 -1
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/memory/held_out_digit_poc.py +1 -1
- spikeforge-0.3.4/spikeforge/targets_extra.py +47 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/training/logger.py +4 -4
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/training/trainer.py +1 -1
- {spikeforge-0.3.2 → spikeforge-0.3.4/spikeforge.egg-info}/PKG-INFO +89 -7
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge.egg-info/SOURCES.txt +3 -0
- spikeforge-0.3.2/README.md +0 -95
- spikeforge-0.3.2/main.py +0 -26
- spikeforge-0.3.2/main_encodings.py +0 -43
- {spikeforge-0.3.2 → spikeforge-0.3.4}/LICENSE +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/setup.cfg +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/benchmark/__init__.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/benchmark/__main__.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/benchmark/cli.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/benchmark/compare.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/benchmark/config.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/benchmark/harness.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/benchmark/serving.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/benchmark/store.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/benchmark/suite.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/benchmark/timing.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/cli/__init__.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/cli/backend_cli.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/cli/extract_cli.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/cli/fixture.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/cli/onnx_cli.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/cli/records_cli.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/compression/__init__.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/compression/codec.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/compression/errors.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/compression/pruning.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/compression/report.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/config.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/data/__init__.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/data/data_loader.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/data/dataset_spec.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/data/datasets.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/data/download_cli.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/data/event_errors.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/data/event_geometry.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/data/event_loader.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/data/image_size.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/data/sample_source.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/data/sequence_source.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/encoding/__init__.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/encoding/delta_trainer.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/encoding/random_spikegen.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/encoding/spike_encoder.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/events/__init__.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/events/dense.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/events/event_bridge.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/events/event_sample.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/events/event_source.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/events/synthetic.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/events/tonic_api.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/exporters/__init__.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/exporters/latency_curve_exporter.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/exporters/plot_utils.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/exporters/reconstruction_exporter.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/exporters/spike_gif_exporter.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/introspection/__init__.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/introspection/comparison.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/introspection/decoding.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/introspection/encoding.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/introspection/firing_rate.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/introspection/histogram.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/introspection/isi.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/introspection/metrics.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/introspection/sparsity.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/introspection/surrogate.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/memory/__init__.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/memory/embedding_readout.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/memory/episode_sampler.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/memory/episodic_trainer.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/memory/few_shot_generalization_poc.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/memory/frozen_classifier.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/memory/hebbian_synapse.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/memory/lockstep_runner.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/memory/one_shot_associative_memory.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/memory/prototypical.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/memory/stdp_synapse.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/memory/stdp_window_poc.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/network/__init__.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/network/hidden_frames.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/network/inference.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/network/model_diff.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/network/model_search.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/network/model_store.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/network/spiking_net.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/neurons/__init__.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/neurons/alpha.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/neurons/contract.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/neurons/handler.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/neurons/lapicque.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/neurons/leaky.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/neurons/recurrent.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/neurons/registry.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/neurons/spike_grad.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/neurons/synaptic.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/nir_bridge/__init__.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/nir_bridge/api.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/nir_bridge/array_codec.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/nir_bridge/drift.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/nir_bridge/errors.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/nir_bridge/exporter.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/nir_bridge/extract.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/nir_bridge/ingest.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/nir_bridge/interpreter.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/nir_bridge/interpreter_result.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/nir_bridge/jsonable.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/nir_bridge/mapped_node.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/nir_bridge/mapper.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/nir_bridge/neuron_kwargs.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/nir_bridge/neuron_nodes.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/nir_bridge/node_builders.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/nir_bridge/node_names.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/nir_bridge/ops_common.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/nir_bridge/ops_linear.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/nir_bridge/ops_neuron.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/nir_bridge/ops_registry.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/nir_bridge/planner.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/nir_bridge/require.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/nir_bridge/roundtrip.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/nir_bridge/serialization.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/nir_bridge/stage_builders.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/nir_bridge/stage_mapping.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/nir_bridge/stages_unmappable.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/nir_bridge/tolerances.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/nir_bridge/torch_map.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/nir_bridge/validation_report.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/nir_bridge/validator.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/observability/__init__.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/observability/json_formatter.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/observability/logging_setup.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/observability/metrics.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/observability/persistence.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/observability/prometheus.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/observability/registry.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/observability/snapshot.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/observability/store.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/observability/timer.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/onnx_bridge/__init__.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/onnx_bridge/api.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/onnx_bridge/errors.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/onnx_bridge/export.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/onnx_bridge/import_onnx.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/onnx_bridge/metadata.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/onnx_bridge/roundtrip.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/onnx_bridge/step_module.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/py.typed +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/rrt/__init__.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/rrt/context_model.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/rrt/falsification_poc.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/rrt/protocol.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/rrt/relation.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/rrt/trial_features.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/runtime/__init__.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/runtime/device.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/runtime/execution_mode.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/runtime/system_stats.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/serving/__init__.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/serving/bundle.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/serving/bundle_manifest.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/serving/encode_spec.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/serving/errors.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/serving/prediction.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/serving/preprocess.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/serving/session.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/serving/state_tree.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/serving/step.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/serving/tensor_codec.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/simulator/__init__.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/simulator/compiled_step.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/simulator/execution.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/simulator/frames.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/simulator/grad_policy.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/simulator/input_shape.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/simulator/module_spec.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/simulator/parallel_runner.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/simulator/production.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/simulator/production_result.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/simulator/runner.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/simulator/state.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/simulator/step_stages.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/simulator/trajectory.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/streaming/__init__.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/streaming/encoding.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/streaming/recipe.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/streaming/serving.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/streaming/stream_source.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/streaming/window_spec.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/topology/__init__.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/topology/attention.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/topology/builder.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/topology/edge.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/topology/kinds.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/topology/multihead_attention.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/topology/ordering.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/topology/positional_encoding.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/topology/presets.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/topology/registry.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/topology/sequence_presets.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/topology/sequence_stages.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/topology/spec.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/topology/stage.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/topology/stage_module.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/topology/stage_modules.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/topology/sum_pool.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/topology/validation.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/tracking/__init__.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/tracking/config_hash.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/tracking/determinism.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/tracking/manifest.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/tracking/seed.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/tracking/sink.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/tracking/sink_probe.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/tracking/sink_records.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/tracking/sinks.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/tracking/tensorboard_sink.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/tracking/versions.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/tracking/wandb_sink.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/training/__init__.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/training/amp_controller.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/training/checkpoint_mixin.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/training/encoding_mixin.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/training/eval_mixin.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/training/event_batches.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/training/event_engine.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/training/multi_device.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/training/scaleup_mixin.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/training/topology_mixin.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge/training/training_engine.py +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge.egg-info/dependency_links.txt +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge.egg-info/entry_points.txt +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge.egg-info/not-zip-safe +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge.egg-info/requires.txt +0 -0
- {spikeforge-0.3.2 → spikeforge-0.3.4}/spikeforge.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: spikeforge
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.4
|
|
4
4
|
Summary: Spiking neural network trainer/experiments for MNIST built with snnTorch and PyTorch.
|
|
5
5
|
Author-email: Capsize LLC <contact@capsizegames.com>
|
|
6
6
|
Maintainer-email: Capsize LLC <contact@capsizegames.com>
|
|
@@ -77,6 +77,27 @@ with a live browser dashboard served over WebSockets.
|
|
|
77
77
|
|
|
78
78
|
## Quickstart
|
|
79
79
|
|
|
80
|
+
No clone, no Docker — five lines of Python:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
pip install spikeforge
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
from spikeforge.training.training_engine import TrainingEngine
|
|
88
|
+
|
|
89
|
+
engine = TrainingEngine(dataset="mnist", hidden=32, epochs=1, num_steps=5)
|
|
90
|
+
for metrics in engine.train():
|
|
91
|
+
last = metrics
|
|
92
|
+
print(last) # {'loss': ..., 'train_accuracy': ..., 'test_accuracy': ...}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
The first run downloads MNIST; later runs are offline. That's the training
|
|
96
|
+
API — `spikeforge-verify`, `spikeforge-benchmark`, and the NIR/deployment/
|
|
97
|
+
energy pieces live in [Usage](documentation/usage.md).
|
|
98
|
+
|
|
99
|
+
Want the live browser dashboard instead? That's the richer, second path:
|
|
100
|
+
|
|
80
101
|
```bash
|
|
81
102
|
git clone https://github.com/capsize-games/spikeforge.git
|
|
82
103
|
cd spikeforge
|
|
@@ -86,10 +107,40 @@ docker compose up --build
|
|
|
86
107
|
Open <http://localhost:8877> — the dashboard connects to the WebSocket on
|
|
87
108
|
the same host and port. No separate backend or proxy to run.
|
|
88
109
|
|
|
89
|
-
|
|
90
|
-
[Usage](documentation/usage.md) and [Quickstart](documentation/quickstart.md)
|
|
110
|
+
See [Usage](documentation/usage.md) and [Quickstart](documentation/quickstart.md)
|
|
91
111
|
for every path (`./install.sh`, local dev with Vite, `examples/`, and the
|
|
92
|
-
|
|
112
|
+
`spikeforge-*` console scripts).
|
|
113
|
+
|
|
114
|
+
## Why not just snnTorch, Norse, Lava, or SpikingJelly?
|
|
115
|
+
|
|
116
|
+
spikeforge doesn't replace those — it trains through
|
|
117
|
+
[snnTorch](https://snntorch.readthedocs.io/) and can deploy to Norse and
|
|
118
|
+
Lava. The question is what it adds on top: a
|
|
119
|
+
[NIR](https://github.com/neuromorphs/NIR)-described interpreter spine with
|
|
120
|
+
an *independent* interpreter that re-executes every exported graph and
|
|
121
|
+
reports numerical drift against the original model (not just export —
|
|
122
|
+
snnTorch and Norse both export NIR too; Lava-DL currently only reads it,
|
|
123
|
+
not writes it), a deployment-capability
|
|
124
|
+
matrix that reports per-target op support and availability instead of just
|
|
125
|
+
declaring support, an event-driven energy estimator, and a live training/
|
|
126
|
+
introspection dashboard. None of the other libraries ship the latter two as
|
|
127
|
+
part of the library itself, to our knowledge — file an issue if that's
|
|
128
|
+
stale.
|
|
129
|
+
|
|
130
|
+
| | snnTorch | Norse | Lava | SpikingJelly | spikeforge |
|
|
131
|
+
|---|---|---|---|---|---|
|
|
132
|
+
| Surrogate-gradient training in PyTorch | ✅ | ✅ | ✅ (Lava-DL) | ✅ | ✅ (via snnTorch) |
|
|
133
|
+
| NIR export (write) | ✅ | ✅ | import-only (Lava-DL reads NIR, doesn't write it) | not part of NIR's official framework list | ✅ |
|
|
134
|
+
| Independent re-execution + drift check of the exported graph | — | — | — | — | ✅ |
|
|
135
|
+
| Per-target deployment capability matrix (op support, honest availability) | — | — | Loihi-focused | — | ✅ (reference/Norse/Lava/SpiNNaker2/Speck/Xylo) |
|
|
136
|
+
| Built-in event-driven energy estimator (SOP/MAC/AC) | — | — | — | — | ✅ (labelled `estimate`, not hardware-measured) |
|
|
137
|
+
| Pretrained model zoo | — | — | — | ✅ (some vision tasks) | catalog infra ships; mostly untrained preset shapes today ([details](spikeforge_hub/models.json)) |
|
|
138
|
+
| Live browser training/introspection dashboard | — | — | — | — | ✅ |
|
|
139
|
+
|
|
140
|
+
If you already have a training loop in snnTorch/Norse/Lava/SpikingJelly and
|
|
141
|
+
don't need interop, deployment reporting, energy estimates, or the
|
|
142
|
+
dashboard, you may not need spikeforge on top of it — that's a fair
|
|
143
|
+
"obviously no," and better than an uninformed "obviously yes."
|
|
93
144
|
|
|
94
145
|
## Features
|
|
95
146
|
|
|
@@ -114,9 +165,31 @@ eight `spikeforge-*` console scripts).
|
|
|
114
165
|
- **Dashboard** — a React + TypeScript UI with training, introspection,
|
|
115
166
|
analysis, targets, energy, and hub panels, and seven guided walkthroughs.
|
|
116
167
|
|
|
168
|
+
## What's implemented vs. experimental vs. spec-only
|
|
169
|
+
|
|
170
|
+
The features list above spans very different levels of maturity. This is
|
|
171
|
+
the ten-second version; each row links to the honest detail.
|
|
172
|
+
|
|
173
|
+
| Capability | Status | Detail |
|
|
174
|
+
|---|---|---|
|
|
175
|
+
| Training, encoding, topologies, checkpointing | Shipped | [Features](documentation/features.md) |
|
|
176
|
+
| NIR export + independent-interpreter drift validation | Shipped | [Interpreter spine](documentation/interpreter-spine.md) |
|
|
177
|
+
| Deployment — `reference` target | Shipped, always available | [Backend execution](documentation/backend-execution.md) |
|
|
178
|
+
| Deployment — `norse`, `lava_loihi2` targets | Shipped, gated on an SDK extra (`pip install spikeforge-targets[norse]` or `[lava]`) | [Targets and interop](documentation/targets-and-interop.md) |
|
|
179
|
+
| Deployment — `spinnaker2`, `speck`, `xylo` targets | Spec-only — registered in the capability matrix, no installable SDK integration yet | [Targets and interop](documentation/targets-and-interop.md) |
|
|
180
|
+
| Energy accounting (SOP/MAC/AC) | Shipped as an explicit `estimate`, not hardware-measured | [Event runtime and energy](documentation/event-runtime-and-energy.md) |
|
|
181
|
+
| Model hub catalog, CLI, dashboard panel, opt-in live HF search | Shipped | [Model hub](documentation/model-hub.md) |
|
|
182
|
+
| Model hub catalog *content* | Thin — bundled NIR shapes today, not trained weights | [`spikeforge_hub/models.json`](spikeforge_hub/models.json) |
|
|
183
|
+
| Sequence/attention topologies (`sequence_mlp`, `sequence_attn`) | Experimental — research scope, not production sequence training | [Sequence primitives](documentation/sequence-primitives.md) |
|
|
184
|
+
| ONNX interop | Shipped, single-step export/import only | [Interop fold-ins](documentation/interop-foldins.md) |
|
|
185
|
+
| Production use-case toolkit: streaming time-series (UC-1) | Shipped | [UC-1](plans/use_case_streaming_timeseries.md) |
|
|
186
|
+
| Production use-case toolkit: UC-2 through UC-10 | Spec-only (design docs behind issues #13–#21) | [plans/index.md](plans/index.md#production-use-cases-pc-0) |
|
|
187
|
+
| Live browser dashboard | Shipped | [Dashboard](documentation/dashboard.md) |
|
|
188
|
+
|
|
117
189
|
## Packages
|
|
118
190
|
|
|
119
|
-
This repository is a single workspace that publishes
|
|
191
|
+
This repository is a single workspace that publishes seven distributions,
|
|
192
|
+
each versioned independently:
|
|
120
193
|
|
|
121
194
|
| Distribution | Import root | Purpose |
|
|
122
195
|
|---|---|---|
|
|
@@ -124,11 +197,20 @@ This repository is a single workspace that publishes four distributions:
|
|
|
124
197
|
| `spikeforge-targets` | `spikeforge_targets` | Deployment targets, quantization, energy accounting, sparse event runtime |
|
|
125
198
|
| `spikeforge-hub` | `spikeforge_hub` | Curated model hub and optional Hugging Face access |
|
|
126
199
|
| `spikeforge-server` | `server` | FastAPI + WebSocket server and dashboard hosting |
|
|
200
|
+
| `spikeforge-serve` | `spikeforge_serve` | Headless REST/WebSocket inference service for a deployment bundle |
|
|
201
|
+
| `spikeforge-clients` | `spikeforge_clients` | Python/TypeScript/CLI clients for `spikeforge-serve` (no torch dependency) |
|
|
202
|
+
| `spikeforge-io` | `spikeforge_io` | Recorded-stream I/O adapters and windowing |
|
|
203
|
+
|
|
204
|
+
`spikeforge-hub` and `spikeforge-targets` sit at `0.1.x` while core is at
|
|
205
|
+
`0.3.x` by design, not neglect — each package is versioned independently, and
|
|
206
|
+
[`compatibility.json`](compatibility.json) is the source of truth for which
|
|
207
|
+
satellite versions go with which core release. If you're pinning versions by
|
|
208
|
+
hand, read that file rather than assuming semver alignment across packages.
|
|
127
209
|
|
|
128
210
|
## Documentation
|
|
129
211
|
|
|
130
|
-
This README
|
|
131
|
-
[`documentation/`](documentation/README.md) is the full reference — install
|
|
212
|
+
This README covers first contact and positioning; it deliberately doesn't
|
|
213
|
+
go deeper. [`documentation/`](documentation/README.md) is the full reference — install
|
|
132
214
|
paths, the CLI tools, architecture, module layout, and the dev workflow —
|
|
133
215
|
written for contributors and coding agents alike. Also see
|
|
134
216
|
[COOKBOOK.md](COOKBOOK.md) for copy-pasteable recipes,
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# spikeforge
|
|
2
|
+
|
|
3
|
+
[](https://github.com/capsize-games/spikeforge/actions/workflows/ci.yml)
|
|
4
|
+
[](https://capsizegames.com/discord)
|
|
5
|
+
[](OPEN_SOURCE_CHECKLIST.md)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
[](https://www.python.org/downloads/)
|
|
8
|
+
[](https://github.com/astral-sh/ruff)
|
|
9
|
+
[](https://github.com/capsize-games/spikeforge/blob/main/CONTRIBUTING.md)
|
|
10
|
+
[](documentation/README.md)
|
|
11
|
+
|
|
12
|
+

|
|
13
|
+
|
|
14
|
+
A spiking-neural-network (SNN) toolkit built on
|
|
15
|
+
[snnTorch](https://snntorch.readthedocs.io/) and PyTorch. Loads MNIST-style
|
|
16
|
+
and neuromorphic event datasets, encodes them into rate, latency, delta, and
|
|
17
|
+
random spikes, and trains, validates, exports, and deploys LIF networks —
|
|
18
|
+
with a live browser dashboard served over WebSockets.
|
|
19
|
+
|
|
20
|
+
> **Pre-1.0.** Before trusting any number this produces, read
|
|
21
|
+
> [Implications and boundaries](documentation/implications-and-boundaries.md).
|
|
22
|
+
|
|
23
|
+
## Quickstart
|
|
24
|
+
|
|
25
|
+
No clone, no Docker — five lines of Python:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pip install spikeforge
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
```python
|
|
32
|
+
from spikeforge.training.training_engine import TrainingEngine
|
|
33
|
+
|
|
34
|
+
engine = TrainingEngine(dataset="mnist", hidden=32, epochs=1, num_steps=5)
|
|
35
|
+
for metrics in engine.train():
|
|
36
|
+
last = metrics
|
|
37
|
+
print(last) # {'loss': ..., 'train_accuracy': ..., 'test_accuracy': ...}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
The first run downloads MNIST; later runs are offline. That's the training
|
|
41
|
+
API — `spikeforge-verify`, `spikeforge-benchmark`, and the NIR/deployment/
|
|
42
|
+
energy pieces live in [Usage](documentation/usage.md).
|
|
43
|
+
|
|
44
|
+
Want the live browser dashboard instead? That's the richer, second path:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
git clone https://github.com/capsize-games/spikeforge.git
|
|
48
|
+
cd spikeforge
|
|
49
|
+
docker compose up --build
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Open <http://localhost:8877> — the dashboard connects to the WebSocket on
|
|
53
|
+
the same host and port. No separate backend or proxy to run.
|
|
54
|
+
|
|
55
|
+
See [Usage](documentation/usage.md) and [Quickstart](documentation/quickstart.md)
|
|
56
|
+
for every path (`./install.sh`, local dev with Vite, `examples/`, and the
|
|
57
|
+
`spikeforge-*` console scripts).
|
|
58
|
+
|
|
59
|
+
## Why not just snnTorch, Norse, Lava, or SpikingJelly?
|
|
60
|
+
|
|
61
|
+
spikeforge doesn't replace those — it trains through
|
|
62
|
+
[snnTorch](https://snntorch.readthedocs.io/) and can deploy to Norse and
|
|
63
|
+
Lava. The question is what it adds on top: a
|
|
64
|
+
[NIR](https://github.com/neuromorphs/NIR)-described interpreter spine with
|
|
65
|
+
an *independent* interpreter that re-executes every exported graph and
|
|
66
|
+
reports numerical drift against the original model (not just export —
|
|
67
|
+
snnTorch and Norse both export NIR too; Lava-DL currently only reads it,
|
|
68
|
+
not writes it), a deployment-capability
|
|
69
|
+
matrix that reports per-target op support and availability instead of just
|
|
70
|
+
declaring support, an event-driven energy estimator, and a live training/
|
|
71
|
+
introspection dashboard. None of the other libraries ship the latter two as
|
|
72
|
+
part of the library itself, to our knowledge — file an issue if that's
|
|
73
|
+
stale.
|
|
74
|
+
|
|
75
|
+
| | snnTorch | Norse | Lava | SpikingJelly | spikeforge |
|
|
76
|
+
|---|---|---|---|---|---|
|
|
77
|
+
| Surrogate-gradient training in PyTorch | ✅ | ✅ | ✅ (Lava-DL) | ✅ | ✅ (via snnTorch) |
|
|
78
|
+
| NIR export (write) | ✅ | ✅ | import-only (Lava-DL reads NIR, doesn't write it) | not part of NIR's official framework list | ✅ |
|
|
79
|
+
| Independent re-execution + drift check of the exported graph | — | — | — | — | ✅ |
|
|
80
|
+
| Per-target deployment capability matrix (op support, honest availability) | — | — | Loihi-focused | — | ✅ (reference/Norse/Lava/SpiNNaker2/Speck/Xylo) |
|
|
81
|
+
| Built-in event-driven energy estimator (SOP/MAC/AC) | — | — | — | — | ✅ (labelled `estimate`, not hardware-measured) |
|
|
82
|
+
| Pretrained model zoo | — | — | — | ✅ (some vision tasks) | catalog infra ships; mostly untrained preset shapes today ([details](spikeforge_hub/models.json)) |
|
|
83
|
+
| Live browser training/introspection dashboard | — | — | — | — | ✅ |
|
|
84
|
+
|
|
85
|
+
If you already have a training loop in snnTorch/Norse/Lava/SpikingJelly and
|
|
86
|
+
don't need interop, deployment reporting, energy estimates, or the
|
|
87
|
+
dashboard, you may not need spikeforge on top of it — that's a fair
|
|
88
|
+
"obviously no," and better than an uninformed "obviously yes."
|
|
89
|
+
|
|
90
|
+
## Features
|
|
91
|
+
|
|
92
|
+
- **Encoding** — rate, latency, delta, and random spike coders.
|
|
93
|
+
- **Training** — fully-connected and convolutional LIF networks with
|
|
94
|
+
surrogate-gradient cross-entropy, checkpointing, and opt-in AMP / gradient
|
|
95
|
+
checkpointing / truncated BPTT / multi-GPU.
|
|
96
|
+
- **Topologies** — `fc_legacy`, `fc_small`, `conv_net`, `recurrent_net`, plus
|
|
97
|
+
the sequence presets `sequence_mlp` and `sequence_attn`.
|
|
98
|
+
- **Datasets** — MNIST, Fashion-MNIST, KMNIST, QMNIST, USPS, EMNIST,
|
|
99
|
+
CIFAR-10, and (via the `events` extra) N-MNIST, DVS128 Gesture,
|
|
100
|
+
CIFAR10-DVS, and Spiking Speech Commands.
|
|
101
|
+
- **Interpreter spine** — NIR export, an independent NIR interpreter, and
|
|
102
|
+
numerical drift validation.
|
|
103
|
+
- **Introspection** — educational-mode `U[t]`/`I[t]`/`S[t]` traces,
|
|
104
|
+
trajectory metrics, and surrogate-derivative curves.
|
|
105
|
+
- **Deployment** — a capability matrix, weight quantization, energy
|
|
106
|
+
accounting, and executable `reference`, `norse`, and `lava_loihi2`
|
|
107
|
+
backends.
|
|
108
|
+
- **Model hub** — a curated, offline-first catalog plus optional live
|
|
109
|
+
Hugging Face search.
|
|
110
|
+
- **Dashboard** — a React + TypeScript UI with training, introspection,
|
|
111
|
+
analysis, targets, energy, and hub panels, and seven guided walkthroughs.
|
|
112
|
+
|
|
113
|
+
## What's implemented vs. experimental vs. spec-only
|
|
114
|
+
|
|
115
|
+
The features list above spans very different levels of maturity. This is
|
|
116
|
+
the ten-second version; each row links to the honest detail.
|
|
117
|
+
|
|
118
|
+
| Capability | Status | Detail |
|
|
119
|
+
|---|---|---|
|
|
120
|
+
| Training, encoding, topologies, checkpointing | Shipped | [Features](documentation/features.md) |
|
|
121
|
+
| NIR export + independent-interpreter drift validation | Shipped | [Interpreter spine](documentation/interpreter-spine.md) |
|
|
122
|
+
| Deployment — `reference` target | Shipped, always available | [Backend execution](documentation/backend-execution.md) |
|
|
123
|
+
| Deployment — `norse`, `lava_loihi2` targets | Shipped, gated on an SDK extra (`pip install spikeforge-targets[norse]` or `[lava]`) | [Targets and interop](documentation/targets-and-interop.md) |
|
|
124
|
+
| Deployment — `spinnaker2`, `speck`, `xylo` targets | Spec-only — registered in the capability matrix, no installable SDK integration yet | [Targets and interop](documentation/targets-and-interop.md) |
|
|
125
|
+
| Energy accounting (SOP/MAC/AC) | Shipped as an explicit `estimate`, not hardware-measured | [Event runtime and energy](documentation/event-runtime-and-energy.md) |
|
|
126
|
+
| Model hub catalog, CLI, dashboard panel, opt-in live HF search | Shipped | [Model hub](documentation/model-hub.md) |
|
|
127
|
+
| Model hub catalog *content* | Thin — bundled NIR shapes today, not trained weights | [`spikeforge_hub/models.json`](spikeforge_hub/models.json) |
|
|
128
|
+
| Sequence/attention topologies (`sequence_mlp`, `sequence_attn`) | Experimental — research scope, not production sequence training | [Sequence primitives](documentation/sequence-primitives.md) |
|
|
129
|
+
| ONNX interop | Shipped, single-step export/import only | [Interop fold-ins](documentation/interop-foldins.md) |
|
|
130
|
+
| Production use-case toolkit: streaming time-series (UC-1) | Shipped | [UC-1](plans/use_case_streaming_timeseries.md) |
|
|
131
|
+
| Production use-case toolkit: UC-2 through UC-10 | Spec-only (design docs behind issues #13–#21) | [plans/index.md](plans/index.md#production-use-cases-pc-0) |
|
|
132
|
+
| Live browser dashboard | Shipped | [Dashboard](documentation/dashboard.md) |
|
|
133
|
+
|
|
134
|
+
## Packages
|
|
135
|
+
|
|
136
|
+
This repository is a single workspace that publishes seven distributions,
|
|
137
|
+
each versioned independently:
|
|
138
|
+
|
|
139
|
+
| Distribution | Import root | Purpose |
|
|
140
|
+
|---|---|---|
|
|
141
|
+
| `spikeforge` | `spikeforge` | Core package: encoders, topologies, training, simulator, NIR bridge, tracking |
|
|
142
|
+
| `spikeforge-targets` | `spikeforge_targets` | Deployment targets, quantization, energy accounting, sparse event runtime |
|
|
143
|
+
| `spikeforge-hub` | `spikeforge_hub` | Curated model hub and optional Hugging Face access |
|
|
144
|
+
| `spikeforge-server` | `server` | FastAPI + WebSocket server and dashboard hosting |
|
|
145
|
+
| `spikeforge-serve` | `spikeforge_serve` | Headless REST/WebSocket inference service for a deployment bundle |
|
|
146
|
+
| `spikeforge-clients` | `spikeforge_clients` | Python/TypeScript/CLI clients for `spikeforge-serve` (no torch dependency) |
|
|
147
|
+
| `spikeforge-io` | `spikeforge_io` | Recorded-stream I/O adapters and windowing |
|
|
148
|
+
|
|
149
|
+
`spikeforge-hub` and `spikeforge-targets` sit at `0.1.x` while core is at
|
|
150
|
+
`0.3.x` by design, not neglect — each package is versioned independently, and
|
|
151
|
+
[`compatibility.json`](compatibility.json) is the source of truth for which
|
|
152
|
+
satellite versions go with which core release. If you're pinning versions by
|
|
153
|
+
hand, read that file rather than assuming semver alignment across packages.
|
|
154
|
+
|
|
155
|
+
## Documentation
|
|
156
|
+
|
|
157
|
+
This README covers first contact and positioning; it deliberately doesn't
|
|
158
|
+
go deeper. [`documentation/`](documentation/README.md) is the full reference — install
|
|
159
|
+
paths, the CLI tools, architecture, module layout, and the dev workflow —
|
|
160
|
+
written for contributors and coding agents alike. Also see
|
|
161
|
+
[COOKBOOK.md](COOKBOOK.md) for copy-pasteable recipes,
|
|
162
|
+
[examples/](examples/) for runnable end-to-end scripts, and
|
|
163
|
+
[plans/](plans/) for design documents and the roadmap.
|
|
164
|
+
|
|
165
|
+
See [CONTRIBUTING.md](https://github.com/capsize-games/spikeforge/blob/main/CONTRIBUTING.md)
|
|
166
|
+
and [rules.md](rules.md) before opening a pull request.
|
|
167
|
+
|
|
168
|
+
## Citing
|
|
169
|
+
|
|
170
|
+
If spikeforge is useful in your research, please cite it — see
|
|
171
|
+
[CITATION.cff](CITATION.cff) (GitHub renders a "Cite this repository"
|
|
172
|
+
button from it automatically).
|
|
173
|
+
|
|
174
|
+
## License
|
|
175
|
+
|
|
176
|
+
Released under the **BSD 3-Clause License** — see [LICENSE](LICENSE) and
|
|
177
|
+
[AUTHORS](AUTHORS).
|
spikeforge-0.3.4/main.py
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""Entry point: train a rate-coded MNIST subset and export all visuals."""
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
from typing import List, Optional
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def _parser() -> argparse.ArgumentParser:
|
|
8
|
+
"""Return the argument parser for the ``spikeforge`` demo CLI."""
|
|
9
|
+
return argparse.ArgumentParser(
|
|
10
|
+
prog="spikeforge",
|
|
11
|
+
description=(
|
|
12
|
+
"Train a rate-coded MNIST subset with snnTorch and export "
|
|
13
|
+
"every tutorial visual (video, GIFs, raster, reconstruction) "
|
|
14
|
+
"to build/. Downloads MNIST on first run; this is the "
|
|
15
|
+
"tutorial demo, not a general-purpose training CLI -- see "
|
|
16
|
+
"spikeforge-verify, spikeforge-benchmark, and "
|
|
17
|
+
"documentation/usage.md for the rest of the toolkit."
|
|
18
|
+
),
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def main(argv: Optional[List[str]] = None) -> None:
|
|
23
|
+
"""Parse ``argv``, then build the trainer and write every output."""
|
|
24
|
+
_parser().parse_args(argv)
|
|
25
|
+
|
|
26
|
+
# Imported lazily so ``--help`` never pays for (or fails on) the heavy
|
|
27
|
+
# snnTorch/matplotlib export stack before argparse has even run.
|
|
28
|
+
from spikeforge.exporters.presentation_exporter import (
|
|
29
|
+
PresentationGifExporter,
|
|
30
|
+
)
|
|
31
|
+
from spikeforge.exporters.raster_exporter import RasterExporter
|
|
32
|
+
from spikeforge.exporters.reconstruction_exporter import (
|
|
33
|
+
ReconstructionExporter,
|
|
34
|
+
)
|
|
35
|
+
from spikeforge.exporters.spike_gif_exporter import SpikeGifExporter
|
|
36
|
+
from spikeforge.exporters.video_exporter import VideoExporter
|
|
37
|
+
from spikeforge.training.logger import SNNTrainerLogger
|
|
38
|
+
|
|
39
|
+
trainer = SNNTrainerLogger(animation_interval=100)
|
|
40
|
+
VideoExporter(trainer).export()
|
|
41
|
+
SpikeGifExporter(trainer).export()
|
|
42
|
+
ReconstructionExporter(trainer).export()
|
|
43
|
+
RasterExporter(trainer).export()
|
|
44
|
+
PresentationGifExporter(trainer).export()
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
if __name__ == "__main__":
|
|
48
|
+
main()
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"""Run the additional tutorial-1 encoding demos (latency/delta/random).
|
|
2
|
+
|
|
3
|
+
Produces latency curve + rasters + animation, delta plots, and random
|
|
4
|
+
spike visuals under build/.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import argparse
|
|
8
|
+
from typing import List, Optional
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _parser() -> argparse.ArgumentParser:
|
|
12
|
+
"""Return the argument parser for the ``spikeforge-encodings`` CLI."""
|
|
13
|
+
return argparse.ArgumentParser(
|
|
14
|
+
prog="spikeforge-encodings",
|
|
15
|
+
description=(
|
|
16
|
+
"Train the latency/delta/random tutorial encoders and export "
|
|
17
|
+
"their visuals to build/. Downloads MNIST on first run; this "
|
|
18
|
+
"is the tutorial demo, not a general-purpose encoding CLI."
|
|
19
|
+
),
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def main(argv: Optional[List[str]] = None) -> None:
|
|
24
|
+
"""Parse ``argv``, then train the encoders and export their visuals."""
|
|
25
|
+
_parser().parse_args(argv)
|
|
26
|
+
|
|
27
|
+
# Imported lazily so ``--help`` never pays for (or fails on) the heavy
|
|
28
|
+
# snnTorch/matplotlib export stack before argparse has even run.
|
|
29
|
+
from spikeforge.encoding.delta_trainer import DeltaTrainer
|
|
30
|
+
from spikeforge.encoding.latency_trainer import LatencyTrainer
|
|
31
|
+
from spikeforge.encoding.random_spikegen import RandomSpikeGenerator
|
|
32
|
+
from spikeforge.exporters.delta_exporter import DeltaPlotExporter
|
|
33
|
+
from spikeforge.exporters.latency_curve_exporter import (
|
|
34
|
+
LatencyCurveExporter,
|
|
35
|
+
)
|
|
36
|
+
from spikeforge.exporters.latency_raster_exporter import (
|
|
37
|
+
LatencyRasterExporter,
|
|
38
|
+
)
|
|
39
|
+
from spikeforge.exporters.latency_video_exporter import (
|
|
40
|
+
LatencyVideoExporter,
|
|
41
|
+
)
|
|
42
|
+
from spikeforge.exporters.random_spike_raster_exporter import (
|
|
43
|
+
RandomSpikeRasterExporter,
|
|
44
|
+
)
|
|
45
|
+
from spikeforge.exporters.random_spike_video_exporter import (
|
|
46
|
+
RandomSpikeVideoExporter,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
latency_trainer = LatencyTrainer(animation_interval=100)
|
|
50
|
+
LatencyCurveExporter(latency_trainer).export()
|
|
51
|
+
LatencyRasterExporter(latency_trainer).export()
|
|
52
|
+
LatencyVideoExporter(latency_trainer).export(key="clip")
|
|
53
|
+
|
|
54
|
+
DeltaPlotExporter(DeltaTrainer()).export()
|
|
55
|
+
|
|
56
|
+
random_spikegen = RandomSpikeGenerator(num_steps=100)
|
|
57
|
+
RandomSpikeRasterExporter(random_spikegen).export()
|
|
58
|
+
RandomSpikeVideoExporter(random_spikegen).export()
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
if __name__ == "__main__":
|
|
62
|
+
main()
|
|
@@ -15,7 +15,7 @@ build-backend = "setuptools.build_meta"
|
|
|
15
15
|
|
|
16
16
|
[project]
|
|
17
17
|
name = "spikeforge"
|
|
18
|
-
version = "0.3.
|
|
18
|
+
version = "0.3.4"
|
|
19
19
|
description = "Spiking neural network trainer/experiments for MNIST built with snnTorch and PyTorch."
|
|
20
20
|
# ``README.md`` and ``LICENSE`` are committed symlinks to the repository-root
|
|
21
21
|
# files: the distribution metadata points at the root documents while
|
|
@@ -7,10 +7,10 @@ from spikeforge.encoding.latency_trainer import (
|
|
|
7
7
|
)
|
|
8
8
|
from spikeforge.encoding.random_spikegen import RandomSpikeGenerator
|
|
9
9
|
from spikeforge.training.logger import SNNTrainerLogger
|
|
10
|
-
from spikeforge.training.trainer import
|
|
10
|
+
from spikeforge.training.trainer import SNNTrainer
|
|
11
11
|
|
|
12
12
|
__all__ = [
|
|
13
|
-
"
|
|
13
|
+
"SNNTrainer",
|
|
14
14
|
"SNNTrainerLogger",
|
|
15
15
|
"LatencyTrainer",
|
|
16
16
|
"DeltaTrainer",
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"""Slow recurrent spiking state for downstream gain modulation."""
|
|
2
|
+
|
|
3
|
+
from typing import NamedTuple, Optional
|
|
4
|
+
|
|
5
|
+
import snntorch as snn
|
|
6
|
+
import torch
|
|
7
|
+
from torch import nn
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class AffectiveOutput(NamedTuple):
|
|
11
|
+
"""One regulator step's spikes and continuous broadcast signal."""
|
|
12
|
+
|
|
13
|
+
spikes: torch.Tensor
|
|
14
|
+
broadcast: torch.Tensor
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class AffectiveRegulator(nn.Module):
|
|
18
|
+
"""Integrate inputs into a slowly decaying SNN-native affect state.
|
|
19
|
+
|
|
20
|
+
The linear projection turns signals such as novelty or confidence into
|
|
21
|
+
currents. A recurrent LIF membrane integrates those currents and is
|
|
22
|
+
exposed as the continuous ``broadcast`` signal; spikes are provided for
|
|
23
|
+
consumers that want an event-native modulation path.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
def __init__(
|
|
27
|
+
self,
|
|
28
|
+
input_size: int,
|
|
29
|
+
state_size: int = 2,
|
|
30
|
+
beta: float = 0.98,
|
|
31
|
+
threshold: float = 1.0,
|
|
32
|
+
device: Optional[torch.device] = None,
|
|
33
|
+
) -> None:
|
|
34
|
+
"""Create a regulator with a persistent state per batch item."""
|
|
35
|
+
super().__init__()
|
|
36
|
+
if input_size < 1 or state_size < 1:
|
|
37
|
+
raise ValueError("input_size and state_size must be positive")
|
|
38
|
+
if not 0.0 < beta < 1.0:
|
|
39
|
+
raise ValueError("beta must be between 0 and 1")
|
|
40
|
+
self.projection = nn.Linear(
|
|
41
|
+
input_size, state_size, bias=False, device=device,
|
|
42
|
+
)
|
|
43
|
+
self.neuron = snn.Leaky(
|
|
44
|
+
beta=beta,
|
|
45
|
+
threshold=threshold,
|
|
46
|
+
reset_mechanism="subtract",
|
|
47
|
+
).to(device)
|
|
48
|
+
self._mem: Optional[torch.Tensor] = None
|
|
49
|
+
|
|
50
|
+
@property
|
|
51
|
+
def state_size(self) -> int:
|
|
52
|
+
"""Return the number of broadcast channels."""
|
|
53
|
+
return self.projection.out_features
|
|
54
|
+
|
|
55
|
+
def reset_state(self, batch_size: int = 1) -> None:
|
|
56
|
+
"""Clear the carried state before a new sequence."""
|
|
57
|
+
if batch_size < 1:
|
|
58
|
+
raise ValueError("batch_size must be positive")
|
|
59
|
+
device = self.projection.weight.device
|
|
60
|
+
self._mem = torch.zeros(batch_size, self.state_size, device=device)
|
|
61
|
+
|
|
62
|
+
def step(self, inputs: torch.Tensor) -> AffectiveOutput:
|
|
63
|
+
"""Advance the regulator for inputs shaped ``[B, input_size]``."""
|
|
64
|
+
if inputs.ndim != 2 or inputs.size(1) != self.projection.in_features:
|
|
65
|
+
raise ValueError(
|
|
66
|
+
"inputs must have shape [B, input_size]"
|
|
67
|
+
)
|
|
68
|
+
batch_size = inputs.size(0)
|
|
69
|
+
if self._mem is None or self._mem.size(0) != batch_size:
|
|
70
|
+
self.reset_state(batch_size)
|
|
71
|
+
current = self.projection(inputs)
|
|
72
|
+
spikes, self._mem = self.neuron(current, self._mem)
|
|
73
|
+
return AffectiveOutput(spikes=spikes, broadcast=self._mem)
|
|
74
|
+
|
|
75
|
+
def forward(self, inputs: torch.Tensor) -> AffectiveOutput:
|
|
76
|
+
"""Alias :meth:`step` for ordinary ``nn.Module`` use."""
|
|
77
|
+
return self.step(inputs)
|
|
@@ -10,20 +10,28 @@ from typing import Any, Dict, Optional
|
|
|
10
10
|
import torch
|
|
11
11
|
|
|
12
12
|
from spikeforge.benchmark.config import BenchmarkConfig
|
|
13
|
+
from spikeforge.targets_extra import require as require_targets
|
|
13
14
|
from spikeforge.topology.stage_module import StageModule
|
|
14
|
-
from spikeforge_targets.energy.accounting import account
|
|
15
|
-
from spikeforge_targets.event_runtime.sparse_runner import sparse_run
|
|
16
15
|
|
|
17
16
|
|
|
18
17
|
def energy_block(
|
|
19
18
|
module: StageModule, spikes: torch.Tensor, config: BenchmarkConfig
|
|
20
19
|
) -> Optional[Dict[str, Any]]:
|
|
21
|
-
"""Return the opt-in event-driven energy estimate, or None.
|
|
20
|
+
"""Return the opt-in event-driven energy estimate, or None.
|
|
21
|
+
|
|
22
|
+
``spikeforge-targets`` is only imported here, once energy accounting is
|
|
23
|
+
actually requested, so a plain ``spikeforge`` install keeps every other
|
|
24
|
+
benchmark path working.
|
|
25
|
+
"""
|
|
22
26
|
if not config.energy:
|
|
23
27
|
return None
|
|
28
|
+
accounting = require_targets("spikeforge_targets.energy.accounting")
|
|
29
|
+
sparse_runner = require_targets(
|
|
30
|
+
"spikeforge_targets.event_runtime.sparse_runner"
|
|
31
|
+
)
|
|
24
32
|
with torch.no_grad():
|
|
25
|
-
result = sparse_run(module, spikes)
|
|
26
|
-
report = account(result, config.energy_target)
|
|
33
|
+
result = sparse_runner.sparse_run(module, spikes)
|
|
34
|
+
report = accounting.account(result, config.energy_target)
|
|
27
35
|
return {
|
|
28
36
|
"report": report.to_dict(),
|
|
29
37
|
"counts": dict(result.counts),
|
|
@@ -4,10 +4,14 @@ Every probe returns ``None`` when its metric is unavailable on this platform,
|
|
|
4
4
|
so a benchmark degrades to skipping rather than failing.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
-
import resource
|
|
8
7
|
import tracemalloc
|
|
9
8
|
from typing import Any, Callable, Dict, Optional
|
|
10
9
|
|
|
10
|
+
try:
|
|
11
|
+
import resource
|
|
12
|
+
except ImportError: # Windows does not provide the POSIX resource module.
|
|
13
|
+
resource = None
|
|
14
|
+
|
|
11
15
|
import torch
|
|
12
16
|
|
|
13
17
|
|
|
@@ -26,6 +30,8 @@ def cuda_peak_bytes(device: Any) -> Optional[int]:
|
|
|
26
30
|
|
|
27
31
|
def process_rss_bytes() -> Optional[int]:
|
|
28
32
|
"""Return the process peak resident set size in bytes, or None."""
|
|
33
|
+
if resource is None:
|
|
34
|
+
return None
|
|
29
35
|
try:
|
|
30
36
|
usage = resource.getrusage(resource.RUSAGE_SELF)
|
|
31
37
|
except (ValueError, OSError):
|
|
@@ -5,9 +5,15 @@ Run as ``python -m spikeforge.cli.verify export --topology conv_net`` or
|
|
|
5
5
|
``validate`` command exits non-zero when the report falls outside tolerance,
|
|
6
6
|
so it works as a CI gate. The deployment subcommands (``targets``, ``deploy``,
|
|
7
7
|
``roundtrip``, ``ingest``) are registered from
|
|
8
|
-
:mod:`spikeforge_targets.cli.target_cli
|
|
9
|
-
checkpoint-tracking subcommands (``records
|
|
10
|
-
:mod:`.records_cli`.
|
|
8
|
+
:mod:`spikeforge_targets.cli.target_cli` when that optional package is
|
|
9
|
+
installed, and the checkpoint-tracking subcommands (``records
|
|
10
|
+
list|diff|manifest``) from :mod:`.records_cli`.
|
|
11
|
+
|
|
12
|
+
``spikeforge-targets`` is not a hard dependency of the core distribution, so
|
|
13
|
+
this module imports it defensively: without it, ``--help`` and the
|
|
14
|
+
``export``/``validate``/``records``/``onnx-*`` subcommands still work, and
|
|
15
|
+
the deployment subcommands are simply absent from the subcommand list (the
|
|
16
|
+
``--help`` description names the install that brings them back).
|
|
11
17
|
"""
|
|
12
18
|
|
|
13
19
|
import argparse
|
|
@@ -30,7 +36,11 @@ from spikeforge.topology import registry
|
|
|
30
36
|
from spikeforge.topology.registry import build_topology
|
|
31
37
|
from spikeforge.topology.spec import TopologySpec
|
|
32
38
|
from spikeforge.topology.stage_module import StageModule
|
|
33
|
-
|
|
39
|
+
|
|
40
|
+
try:
|
|
41
|
+
from spikeforge_targets.cli import target_cli
|
|
42
|
+
except ImportError:
|
|
43
|
+
target_cli = None
|
|
34
44
|
|
|
35
45
|
_Input = Tuple[TopologySpec, StageModule, torch.Tensor]
|
|
36
46
|
|
|
@@ -124,9 +134,15 @@ def _run_validate(args: argparse.Namespace) -> int:
|
|
|
124
134
|
|
|
125
135
|
def _parser() -> argparse.ArgumentParser:
|
|
126
136
|
"""Return the argument parser for the verify CLI."""
|
|
137
|
+
description = "Export and validate topology NIR graphs."
|
|
138
|
+
if target_cli is None:
|
|
139
|
+
description += (
|
|
140
|
+
" (deployment subcommands -- targets, deploy, roundtrip, "
|
|
141
|
+
"test-deploy, ingest -- need: pip install spikeforge-targets)"
|
|
142
|
+
)
|
|
127
143
|
parser = argparse.ArgumentParser(
|
|
128
144
|
prog="spikeforge-verify",
|
|
129
|
-
description=
|
|
145
|
+
description=description,
|
|
130
146
|
)
|
|
131
147
|
subs = parser.add_subparsers(dest="command", required=True)
|
|
132
148
|
export = subs.add_parser("export", help="export a topology to NIR")
|
|
@@ -140,7 +156,8 @@ def _parser() -> argparse.ArgumentParser:
|
|
|
140
156
|
check.add_argument("--steps", type=int, default=10)
|
|
141
157
|
check.add_argument("--seed", type=int, default=0)
|
|
142
158
|
check.set_defaults(handler=_run_validate)
|
|
143
|
-
target_cli
|
|
159
|
+
if target_cli is not None:
|
|
160
|
+
target_cli.add_subcommands(subs)
|
|
144
161
|
records_cli.add_subcommands(subs)
|
|
145
162
|
onnx_cli.add_subcommands(subs)
|
|
146
163
|
return parser
|