natal-core 0.1.0.dev1__tar.gz → 0.1.0.dev2__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.
- natal_core-0.1.0.dev2/.github/copilot-instructions.md +55 -0
- {natal_core-0.1.0.dev1 → natal_core-0.1.0.dev2}/.gitignore +2 -0
- natal_core-0.1.0.dev2/.python-version +1 -0
- natal_core-0.1.0.dev2/.readthedocs.yaml +15 -0
- natal_core-0.1.0.dev2/.vscode/settings.json +5 -0
- natal_core-0.1.0.dev2/AGENTS.md +63 -0
- natal_core-0.1.0.dev2/CHANGELOG.md +109 -0
- {natal_core-0.1.0.dev1 → natal_core-0.1.0.dev2}/PKG-INFO +4 -2
- {natal_core-0.1.0.dev1 → natal_core-0.1.0.dev2}/README.md +3 -1
- natal_core-0.1.0.dev2/TODO.md +20 -0
- natal_core-0.1.0.dev2/benchmarks/README.md +100 -0
- natal_core-0.1.0.dev2/benchmarks/benchmark_kernel_overhead.py +126 -0
- natal_core-0.1.0.dev2/benchmarks/benchmark_stages.py +44 -0
- natal_core-0.1.0.dev2/benchmarks/profile_numba_step_breakdown.py +321 -0
- natal_core-0.1.0.dev2/benchmarks/profile_random_modes.py +164 -0
- natal_core-0.1.0.dev2/benchmarks/profile_repro_direct.py +153 -0
- natal_core-0.1.0.dev2/benchmarks/profile_single_deme_repro.py +146 -0
- natal_core-0.1.0.dev2/benchmarks/profile_spatial_hex_run.py +49 -0
- natal_core-0.1.0.dev2/count_errors.py +59 -0
- natal_core-0.1.0.dev2/demos/discrete.py +74 -0
- natal_core-0.1.0.dev2/demos/discrete_ui.py +65 -0
- natal_core-0.1.0.dev2/demos/mosquito.py +103 -0
- natal_core-0.1.0.dev2/demos/mosquito_ui.py +105 -0
- natal_core-0.1.0.dev2/demos/spatial.py +153 -0
- natal_core-0.1.0.dev2/demos/spatial_hex.py +124 -0
- natal_core-0.1.0.dev2/demos/spatial_hex_discrete.py +130 -0
- natal_core-0.1.0.dev2/demos/spatial_hex_ui.py +116 -0
- natal_core-0.1.0.dev2/demos/spatial_ui.py +123 -0
- natal_core-0.1.0.dev2/docs/allele_conversion_rules.md +160 -0
- natal_core-0.1.0.dev2/docs/api/age_structured_population.md +13 -0
- natal_core-0.1.0.dev2/docs/api/algorithms.md +13 -0
- natal_core-0.1.0.dev2/docs/api/base_population.md +13 -0
- natal_core-0.1.0.dev2/docs/api/discrete_generation_population.md +15 -0
- natal_core-0.1.0.dev2/docs/api/genetic_entities.md +13 -0
- natal_core-0.1.0.dev2/docs/api/genetic_patterns.md +13 -0
- natal_core-0.1.0.dev2/docs/api/genetic_presets.md +15 -0
- natal_core-0.1.0.dev2/docs/api/genetic_structures.md +13 -0
- natal_core-0.1.0.dev2/docs/api/hook_dsl.md +13 -0
- natal_core-0.1.0.dev2/docs/api/index.md +27 -0
- natal_core-0.1.0.dev2/docs/api/modifiers.md +13 -0
- natal_core-0.1.0.dev2/docs/api/population_builder.md +13 -0
- natal_core-0.1.0.dev2/docs/api/population_config.md +14 -0
- natal_core-0.1.0.dev2/docs/api/population_state.md +13 -0
- natal_core-0.1.0.dev2/docs/api/simulation_kernels.md +13 -0
- natal_core-0.1.0.dev2/docs/api/state_translation.md +16 -0
- natal_core-0.1.0.dev2/docs/builder_system.md +420 -0
- natal_core-0.1.0.dev2/docs/en/.readthedocs.yaml +1 -0
- natal_core-0.1.0.dev2/docs/en/allele_conversion_rules.md +1 -0
- natal_core-0.1.0.dev2/docs/en/api/age_structured_population.md +1 -0
- natal_core-0.1.0.dev2/docs/en/api/algorithms.md +1 -0
- natal_core-0.1.0.dev2/docs/en/api/base_population.md +1 -0
- natal_core-0.1.0.dev2/docs/en/api/discrete_generation_population.md +1 -0
- natal_core-0.1.0.dev2/docs/en/api/genetic_entities.md +1 -0
- natal_core-0.1.0.dev2/docs/en/api/genetic_patterns.md +1 -0
- natal_core-0.1.0.dev2/docs/en/api/genetic_presets.md +1 -0
- natal_core-0.1.0.dev2/docs/en/api/genetic_structures.md +1 -0
- natal_core-0.1.0.dev2/docs/en/api/hook_dsl.md +1 -0
- natal_core-0.1.0.dev2/docs/en/api/index.md +1 -0
- natal_core-0.1.0.dev2/docs/en/api/modifiers.md +1 -0
- natal_core-0.1.0.dev2/docs/en/api/population_builder.md +1 -0
- natal_core-0.1.0.dev2/docs/en/api/population_config.md +1 -0
- natal_core-0.1.0.dev2/docs/en/api/population_state.md +1 -0
- natal_core-0.1.0.dev2/docs/en/api/simulation_kernels.md +1 -0
- natal_core-0.1.0.dev2/docs/en/api/state_translation.md +1 -0
- natal_core-0.1.0.dev2/docs/en/builder_system.md +1 -0
- natal_core-0.1.0.dev2/docs/en/genetic_presets.md +1 -0
- natal_core-0.1.0.dev2/docs/en/genetic_structures.md +1 -0
- natal_core-0.1.0.dev2/docs/en/genotype_filter.md +1 -0
- natal_core-0.1.0.dev2/docs/en/genotype_patterns.md +1 -0
- natal_core-0.1.0.dev2/docs/en/hooks.md +1 -0
- natal_core-0.1.0.dev2/docs/en/index.md +1 -0
- natal_core-0.1.0.dev2/docs/en/index_registry.md +1 -0
- natal_core-0.1.0.dev2/docs/en/modifiers.md +1 -0
- natal_core-0.1.0.dev2/docs/en/natal-brand.svg +1 -0
- natal_core-0.1.0.dev2/docs/en/numba_optimization.md +1 -0
- natal_core-0.1.0.dev2/docs/en/observation_rules.md +1 -0
- natal_core-0.1.0.dev2/docs/en/population_state_config.md +1 -0
- natal_core-0.1.0.dev2/docs/en/preset_encapsulation_and_validation.md +1 -0
- natal_core-0.1.0.dev2/docs/en/quickstart.md +1 -0
- natal_core-0.1.0.dev2/docs/en/simulation_kernels.md +1 -0
- natal_core-0.1.0.dev2/docs/en/spatial_simulation_guide.md +1 -0
- natal_core-0.1.0.dev2/docs/genetic_presets.md +515 -0
- natal_core-0.1.0.dev2/docs/genetic_structures.md +500 -0
- natal_core-0.1.0.dev2/docs/genotype_filter.md +136 -0
- natal_core-0.1.0.dev2/docs/genotype_patterns.md +149 -0
- natal_core-0.1.0.dev2/docs/hooks.md +185 -0
- natal_core-0.1.0.dev2/docs/index.md +88 -0
- natal_core-0.1.0.dev2/docs/index_registry.md +337 -0
- natal_core-0.1.0.dev2/docs/modifiers.md +212 -0
- natal_core-0.1.0.dev2/docs/natal-brand.svg +21246 -0
- natal_core-0.1.0.dev2/docs/numba_optimization.md +172 -0
- natal_core-0.1.0.dev2/docs/observation_rules.md +660 -0
- natal_core-0.1.0.dev2/docs/population_state_config.md +272 -0
- natal_core-0.1.0.dev2/docs/preset_encapsulation_and_validation.md +114 -0
- natal_core-0.1.0.dev2/docs/quickstart.md +468 -0
- natal_core-0.1.0.dev2/docs/requirements-rtd.txt +5 -0
- natal_core-0.1.0.dev2/docs/requirements-zh.txt +4 -0
- natal_core-0.1.0.dev2/docs/requirements.txt +4 -0
- natal_core-0.1.0.dev2/docs/simulation_kernels.md +222 -0
- natal_core-0.1.0.dev2/docs/spatial_simulation_guide.md +246 -0
- natal_core-0.1.0.dev2/docs/zh/.readthedocs.yaml +15 -0
- natal_core-0.1.0.dev2/docs/zh/allele_conversion_rules.md +160 -0
- natal_core-0.1.0.dev2/docs/zh/builder_system.md +420 -0
- natal_core-0.1.0.dev2/docs/zh/genetic_presets.md +509 -0
- natal_core-0.1.0.dev2/docs/zh/genetic_structures.md +500 -0
- natal_core-0.1.0.dev2/docs/zh/genotype_filter.md +136 -0
- natal_core-0.1.0.dev2/docs/zh/genotype_patterns.md +149 -0
- natal_core-0.1.0.dev2/docs/zh/hooks.md +185 -0
- natal_core-0.1.0.dev2/docs/zh/index.md +88 -0
- natal_core-0.1.0.dev2/docs/zh/index_registry.md +337 -0
- natal_core-0.1.0.dev2/docs/zh/modifiers.md +212 -0
- natal_core-0.1.0.dev2/docs/zh/natal-brand.svg +21246 -0
- natal_core-0.1.0.dev2/docs/zh/numba_optimization.md +173 -0
- natal_core-0.1.0.dev2/docs/zh/observation_rules.md +708 -0
- natal_core-0.1.0.dev2/docs/zh/population_state_config.md +272 -0
- natal_core-0.1.0.dev2/docs/zh/preset_encapsulation_and_validation.md +114 -0
- natal_core-0.1.0.dev2/docs/zh/quickstart.md +468 -0
- natal_core-0.1.0.dev2/docs/zh/simulation_kernels.md +222 -0
- natal_core-0.1.0.dev2/docs/zh/spatial_simulation_guide.md +246 -0
- natal_core-0.1.0.dev2/docstring_spec.md +293 -0
- natal_core-0.1.0.dev2/docstring_spec_cn.md +300 -0
- natal_core-0.1.0.dev2/example_google.py +296 -0
- natal_core-0.1.0.dev2/mkdocs.en.yml +121 -0
- natal_core-0.1.0.dev2/mkdocs.yml +121 -0
- natal_core-0.1.0.dev2/mkdocs.zh.yml +121 -0
- natal_core-0.1.0.dev2/natal-brand.svg +21246 -0
- natal_core-0.1.0.dev2/natal.svg +8647 -0
- {natal_core-0.1.0.dev1 → natal_core-0.1.0.dev2}/pyproject.toml +4 -6
- natal_core-0.1.0.dev2/quality_checks_spec.md +196 -0
- natal_core-0.1.0.dev2/quality_checks_spec_cn.md +194 -0
- natal_core-0.1.0.dev2/scripts/check_docstring_fields.py +304 -0
- natal_core-0.1.0.dev2/scripts/cleanup_docs.py +62 -0
- natal_core-0.1.0.dev2/scripts/docstring_standardizer.py +262 -0
- natal_core-0.1.0.dev2/scripts/final_docstring_cleanup.py +233 -0
- natal_core-0.1.0.dev2/scripts/generate_api_docs.py +155 -0
- natal_core-0.1.0.dev2/scripts/generate_enhanced_api_docs.py +228 -0
- natal_core-0.1.0.dev2/scripts/generate_init_pyi.py +111 -0
- natal_core-0.1.0.dev2/scripts/normalize_docstrings.py +181 -0
- natal_core-0.1.0.dev2/src/natal/__init__.py +131 -0
- natal_core-0.1.0.dev2/src/natal/__init__.pyi +359 -0
- natal_core-0.1.0.dev2/src/natal/age_structured_population.py +787 -0
- natal_core-0.1.0.dev2/src/natal/algorithms.py +1475 -0
- natal_core-0.1.0.dev2/src/natal/base_population.py +1411 -0
- natal_core-0.1.0.dev2/src/natal/discrete_generation_population.py +423 -0
- natal_core-0.1.0.dev2/src/natal/gamete_allele_conversion.py +667 -0
- natal_core-0.1.0.dev2/src/natal/genetic_entities.py +1093 -0
- natal_core-0.1.0.dev2/src/natal/genetic_patterns.py +1028 -0
- natal_core-0.1.0.dev2/src/natal/genetic_presets.py +1342 -0
- natal_core-0.1.0.dev2/src/natal/genetic_structures.py +3342 -0
- natal_core-0.1.0.dev2/src/natal/helpers.py +45 -0
- natal_core-0.1.0.dev2/src/natal/hook_dsl.py +53 -0
- natal_core-0.1.0.dev2/src/natal/hooks/__init__.py +93 -0
- natal_core-0.1.0.dev2/src/natal/hooks/compiler.py +370 -0
- natal_core-0.1.0.dev2/src/natal/hooks/declarative.py +710 -0
- natal_core-0.1.0.dev2/src/natal/hooks/executor.py +610 -0
- natal_core-0.1.0.dev2/src/natal/hooks/selector.py +247 -0
- natal_core-0.1.0.dev2/src/natal/hooks/types.py +288 -0
- natal_core-0.1.0.dev2/src/natal/index_registry.py +600 -0
- natal_core-0.1.0.dev2/src/natal/kernels/__init__.py +2 -0
- natal_core-0.1.0.dev2/src/natal/kernels/codegen.py +170 -0
- natal_core-0.1.0.dev2/src/natal/kernels/simulation_kernels.py +519 -0
- natal_core-0.1.0.dev2/src/natal/kernels/spatial_simulation_kernels.py +1670 -0
- natal_core-0.1.0.dev2/src/natal/modifiers.py +547 -0
- natal_core-0.1.0.dev2/src/natal/numba_compat.py +460 -0
- natal_core-0.1.0.dev2/src/natal/numba_utils.py +718 -0
- natal_core-0.1.0.dev2/src/natal/observation.py +533 -0
- natal_core-0.1.0.dev2/src/natal/population_builder.py +1908 -0
- natal_core-0.1.0.dev2/src/natal/population_config.py +875 -0
- natal_core-0.1.0.dev2/src/natal/population_state.py +358 -0
- natal_core-0.1.0.dev2/src/natal/py.typed +0 -0
- natal_core-0.1.0.dev2/src/natal/simulation_kernels.py +30 -0
- natal_core-0.1.0.dev2/src/natal/spatial_population.py +695 -0
- natal_core-0.1.0.dev2/src/natal/spatial_simulation_kernels.py +29 -0
- natal_core-0.1.0.dev2/src/natal/spatial_topology.py +522 -0
- natal_core-0.1.0.dev2/src/natal/state_translation.py +543 -0
- natal_core-0.1.0.dev2/src/natal/type_def.py +121 -0
- natal_core-0.1.0.dev2/src/natal/ui/__init__.py +25 -0
- natal_core-0.1.0.dev2/src/natal/ui/dashboard.py +34 -0
- natal_core-0.1.0.dev2/src/natal/ui/dashboard_population.py +1366 -0
- natal_core-0.1.0.dev2/src/natal/ui/spatial_dashboard.py +975 -0
- natal_core-0.1.0.dev2/src/natal/visualization.py +110 -0
- natal_core-0.1.0.dev2/src/natal/zygote_allele_conversion.py +682 -0
- natal_core-0.1.0.dev2/tests/__init__.py +1 -0
- natal_core-0.1.0.dev2/tests/conftest.py +55 -0
- natal_core-0.1.0.dev2/tests/test_age_structured_population.py +150 -0
- natal_core-0.1.0.dev2/tests/test_bracket_patterns.py +129 -0
- natal_core-0.1.0.dev2/tests/test_corrected_sampling.py +53 -0
- natal_core-0.1.0.dev2/tests/test_discrete_builder_initial_state.py +113 -0
- natal_core-0.1.0.dev2/tests/test_discrete_population.py +194 -0
- natal_core-0.1.0.dev2/tests/test_genetic_entities.py +206 -0
- natal_core-0.1.0.dev2/tests/test_genetic_patterns.py +113 -0
- natal_core-0.1.0.dev2/tests/test_genetic_structures.py +189 -0
- natal_core-0.1.0.dev2/tests/test_helpers.py +97 -0
- natal_core-0.1.0.dev2/tests/test_hook_condition_interpreter.py +69 -0
- natal_core-0.1.0.dev2/tests/test_hook_deme_selector.py +108 -0
- natal_core-0.1.0.dev2/tests/test_hook_kernel_ops.py +272 -0
- natal_core-0.1.0.dev2/tests/test_hook_numba_strict_mode.py +131 -0
- natal_core-0.1.0.dev2/tests/test_improved_sampling.py +57 -0
- natal_core-0.1.0.dev2/tests/test_index_registry.py +145 -0
- natal_core-0.1.0.dev2/tests/test_packaging.py +12 -0
- natal_core-0.1.0.dev2/tests/test_pattern_comprehensive.py +264 -0
- natal_core-0.1.0.dev2/tests/test_pattern_omission_syntax.py +336 -0
- natal_core-0.1.0.dev2/tests/test_population_builder_fitness_patterns.py +259 -0
- natal_core-0.1.0.dev2/tests/test_population_builder_initial_injection.py +112 -0
- natal_core-0.1.0.dev2/tests/test_recipe_fitness_patch.py +106 -0
- natal_core-0.1.0.dev2/tests/test_recipe_species_binding.py +100 -0
- natal_core-0.1.0.dev2/tests/test_sampling_consistency.py +78 -0
- natal_core-0.1.0.dev2/tests/test_spatial_codegen_wrappers.py +40 -0
- natal_core-0.1.0.dev2/tests/test_spatial_dashboard.py +157 -0
- natal_core-0.1.0.dev2/tests/test_spatial_kernel_template_order.py +21 -0
- natal_core-0.1.0.dev2/tests/test_spatial_population_integration.py +198 -0
- natal_core-0.1.0.dev2/tests/test_spatial_population_run.py +419 -0
- natal_core-0.1.0.dev2/tests/test_spatial_population_structure.py +138 -0
- natal_core-0.1.0.dev2/tests/test_spatial_simulation_kernels.py +87 -0
- natal_core-0.1.0.dev2/tests/test_spatial_topology.py +166 -0
- natal_core-0.1.0.dev2/tests/test_state_translation.py +340 -0
- natal_core-0.1.0.dev2/tests/test_toxin_antidote_drive.py +399 -0
- natal_core-0.1.0.dev2/tests/test_type_def.py +100 -0
- natal_core-0.1.0.dev2/uv.lock +4370 -0
- {natal_core-0.1.0.dev1 → natal_core-0.1.0.dev2}/LICENSE +0 -0
- {natal_core-0.1.0.dev1 → natal_core-0.1.0.dev2}/src/natal/kernels/templates/kernel_wrappers.py.tmpl +0 -0
- {natal_core-0.1.0.dev1 → natal_core-0.1.0.dev2}/src/natal/kernels/templates/spatial_kernel_wrappers.py.tmpl +0 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Copilot Workspace Instructions for NATAL Core
|
|
2
|
+
|
|
3
|
+
Apply these rules for all tasks in this repository.
|
|
4
|
+
|
|
5
|
+
## Rule Priority
|
|
6
|
+
|
|
7
|
+
Use this priority order when rules overlap:
|
|
8
|
+
1. docstring_spec.md
|
|
9
|
+
2. quality_checks_spec.md
|
|
10
|
+
3. docstring_spec_cn.md
|
|
11
|
+
4. quality_checks_spec_cn.md
|
|
12
|
+
5. AGENTS.md
|
|
13
|
+
|
|
14
|
+
## Mandatory Validation
|
|
15
|
+
|
|
16
|
+
Before finalizing any code change, run and pass:
|
|
17
|
+
- pytest
|
|
18
|
+
- pyright
|
|
19
|
+
- ruff check src demos
|
|
20
|
+
|
|
21
|
+
If public exports changed, also run:
|
|
22
|
+
- python scripts/generate_init_pyi.py
|
|
23
|
+
|
|
24
|
+
## Docstring Rules
|
|
25
|
+
|
|
26
|
+
- Follow Google style section names only.
|
|
27
|
+
- Keep docstring content in English.
|
|
28
|
+
- Ensure typed parameters, returns, and attributes.
|
|
29
|
+
|
|
30
|
+
## Testing Rules
|
|
31
|
+
|
|
32
|
+
- Add or update tests for non-trivial behavior changes.
|
|
33
|
+
- Include boundary and error-path coverage when relevant.
|
|
34
|
+
- Prefer pytest-collected tests over script-style smoke tests.
|
|
35
|
+
- Keep tests deterministic.
|
|
36
|
+
|
|
37
|
+
## Type and Lint Rules
|
|
38
|
+
|
|
39
|
+
- Avoid new implicit Any in modified code.
|
|
40
|
+
- Do not add # type: ignore unless necessary and justified.
|
|
41
|
+
- Do not add broad Ruff ignore rules.
|
|
42
|
+
|
|
43
|
+
## Response Requirements
|
|
44
|
+
|
|
45
|
+
For implementation tasks, include:
|
|
46
|
+
- Changed files summary
|
|
47
|
+
- Behavior impact summary
|
|
48
|
+
- Validation commands executed
|
|
49
|
+
- Residual risks or follow-up items if any
|
|
50
|
+
|
|
51
|
+
## Working Style
|
|
52
|
+
|
|
53
|
+
- Keep changes small and focused.
|
|
54
|
+
- Preserve existing behavior unless the task explicitly asks for behavior changes.
|
|
55
|
+
- Prefer minimal-scope refactors.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.12
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# NATAL Core Agent Rules
|
|
2
|
+
|
|
3
|
+
This file defines default instructions for AI Agents working in this repository.
|
|
4
|
+
These rules are always-on at repository scope.
|
|
5
|
+
|
|
6
|
+
## 1) Source of truth
|
|
7
|
+
|
|
8
|
+
Follow these documents in descending priority:
|
|
9
|
+
1. docstring_spec.md
|
|
10
|
+
2. quality_checks_spec.md
|
|
11
|
+
3. docstring_spec_cn.md (Chinese explanation)
|
|
12
|
+
4. quality_checks_spec_cn.md (Chinese explanation)
|
|
13
|
+
|
|
14
|
+
If rules conflict, prefer the English specification files.
|
|
15
|
+
|
|
16
|
+
## 2) Mandatory quality gates before proposing completion
|
|
17
|
+
|
|
18
|
+
For code changes, run and pass all of the following:
|
|
19
|
+
- pytest
|
|
20
|
+
- pyright
|
|
21
|
+
- ruff check src demos
|
|
22
|
+
|
|
23
|
+
Activate the repository virtual environment before running `pyright` so it uses
|
|
24
|
+
the project-installed dependencies and configuration.
|
|
25
|
+
|
|
26
|
+
After those commands complete, review `docs/` for any needed documentation updates and apply them before finalizing the change.
|
|
27
|
+
|
|
28
|
+
If API exports changed, also run:
|
|
29
|
+
- python scripts/generate_init_pyi.py
|
|
30
|
+
|
|
31
|
+
## 3) Docstring requirements
|
|
32
|
+
|
|
33
|
+
- Use Google style sections only.
|
|
34
|
+
- Keep docstring text in English.
|
|
35
|
+
- Ensure all parameters/returns/attributes are explicitly typed (annotation preferred).
|
|
36
|
+
- Do not invent section headers.
|
|
37
|
+
|
|
38
|
+
## 4) Testing requirements
|
|
39
|
+
|
|
40
|
+
- Add or update tests for all non-trivial behavior changes.
|
|
41
|
+
- Cover happy path, boundaries, and error paths when relevant.
|
|
42
|
+
- Keep tests deterministic (fixed seeds for random behavior).
|
|
43
|
+
- Prefer pytest-collected tests over script-style smoke tests.
|
|
44
|
+
|
|
45
|
+
## 5) Typing and linting requirements
|
|
46
|
+
|
|
47
|
+
- Do not introduce new implicit Any in modified code.
|
|
48
|
+
- Do not add # type: ignore unless absolutely necessary with a clear reason.
|
|
49
|
+
- Do not add broad Ruff ignores; keep exceptions minimal and documented.
|
|
50
|
+
|
|
51
|
+
## 6) Change note requirements (human and AI)
|
|
52
|
+
|
|
53
|
+
Every change summary must include:
|
|
54
|
+
- Files changed
|
|
55
|
+
- Behavior changes
|
|
56
|
+
- Validation commands executed
|
|
57
|
+
- Residual risks or follow-up items (if any)
|
|
58
|
+
|
|
59
|
+
## 7) Working mode for this repository
|
|
60
|
+
|
|
61
|
+
- Repository is mainly solo-maintained with occasional external contributions.
|
|
62
|
+
- Keep changes small and focused.
|
|
63
|
+
- Lightweight review is acceptable, but quality gates are never optional.
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 🎉 2026.4.4.b - v0.0.0-beta released!
|
|
4
|
+
- Refactor sampling method naming from 'use_dirichlet_sampling' to 'use_continuous_sampling'
|
|
5
|
+
|
|
6
|
+
## 2026.4.4
|
|
7
|
+
- feat(spatial): Add support for sparse CSR adjacency matrices and extend migration strategy (heterogenic kernels / hybrid)
|
|
8
|
+
- feat(genetic_presets): Add ToxinAntidoteDrive preset and corresponding tests
|
|
9
|
+
|
|
10
|
+
## 2026.4.3
|
|
11
|
+
implemented spatial population with stable numba cache & parallel computation
|
|
12
|
+
|
|
13
|
+
## 2026.3.28.c
|
|
14
|
+
add type annotations
|
|
15
|
+
|
|
16
|
+
## 2026.3.28.b
|
|
17
|
+
add tests
|
|
18
|
+
|
|
19
|
+
## 2026.3.28
|
|
20
|
+
`Species.from_dict` (and any repeated child-structure creation) crashed with `ValueError: Child structure '…' already exists` on a second call with the same name, even though `GeneticStructure.__new__` and `_single_register` already silently return cached instances. The fix makes the entire singleton chain consistent.
|
|
21
|
+
|
|
22
|
+
### Changes
|
|
23
|
+
|
|
24
|
+
- **`ChildStructureRegistry.add`** — return `_storage[name]` instead of raising when the name already exists. `add` is now idempotent, matching `__new__`/`_single_register` semantics.
|
|
25
|
+
|
|
26
|
+
```python
|
|
27
|
+
# Before: second call raises
|
|
28
|
+
sp1 = Species.from_dict("Mosquito", {"chr1": {"loc": ["WT", "Dr"]}})
|
|
29
|
+
sp2 = Species.from_dict("Mosquito", {"chr1": {"loc": ["WT", "Dr"]}})
|
|
30
|
+
# → ValueError: Child structure 'chr1' already exists.
|
|
31
|
+
|
|
32
|
+
# After: same cached instance returned
|
|
33
|
+
assert sp1 is sp2 # ✓
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
- **`tests/conftest.py`** — removed all cache-clearing fixtures (`_GLOBAL_STRUCTURE_CACHE`, `GeneticEntity._instance_cache`, `Genotype._cache`). Clearing entity caches while live `Locus._entities` sets retained old references caused `EntityRegistry` (identity-based deduplication) to register fresh objects as duplicates, producing spurious extra alleles. The singleton chain is now coherent end-to-end without manual clearing.
|
|
37
|
+
|
|
38
|
+
- **`tests/test_genetic_structures.py`** — two regression tests covering idempotent `from_dict` (same instance returned) and no chromosome duplication on repeated calls.
|
|
39
|
+
|
|
40
|
+
- **`.gitignore`** — removed `tests/` and `test_*` entries that were suppressing test file tracking.
|
|
41
|
+
|
|
42
|
+
## 2026.3.27
|
|
43
|
+
- 提升数值稳定性:修复了连续化抽样中的数值稳定性问题,当 $n \le 1$ 时退化为确定性
|
|
44
|
+
- 初步重构文档和 docstring
|
|
45
|
+
- 部分公用代码迁移到其他模块
|
|
46
|
+
|
|
47
|
+
## 2026.3.26.e
|
|
48
|
+
- 优化 UI 字体大小和绘图描点策略
|
|
49
|
+
|
|
50
|
+
## 2026.3.26.d
|
|
51
|
+
- 修复了 algorithms 中未正确处理配子储存与替换的问题
|
|
52
|
+
- 为 algorithms 增加概率范围限制和异常报错
|
|
53
|
+
- 为 algorithms 支持完整的 mating rates
|
|
54
|
+
- 增加 numba 随机种子设置方法
|
|
55
|
+
- 优化 UI 界面
|
|
56
|
+
|
|
57
|
+
## 2026.3.26.c
|
|
58
|
+
- 重命名 `hook/` -> `hooks/`,并同步更新相关导入路径
|
|
59
|
+
- 重命名 `kernel/` -> `kernels/`,并同步更新相关导入路径
|
|
60
|
+
|
|
61
|
+
## 2026.3.26.b
|
|
62
|
+
- 移除 `simulation_kernels.py` 中未使用的 `run/run_tick` 旧入口,统一走 hook codegen 生成的 wrapper
|
|
63
|
+
- 将 kernel wrapper codegen 从 `hook/compiler.py` 拆分到 `natal/kernel/codegen.py`,并将模板外置到 `src/natal/kernel/templates/kernel_wrappers.py.tmpl`
|
|
64
|
+
- 将核心 `simulation_kernels` 实现迁移到 `natal/kernel/simulation_kernels.py`,保留 `natal/simulation_kernels.py` 兼容转发
|
|
65
|
+
- 包结构重命名:`natal/hook` -> `natal/hooks`,`natal/kernel` -> `natal/kernels`,并同步更新内部导入与模板引用
|
|
66
|
+
|
|
67
|
+
## 2026.3.26
|
|
68
|
+
- 修复 `HomingDrive` 中 maternal deposition 总是有效的 bug
|
|
69
|
+
- 增加 spatial model 的一些模块
|
|
70
|
+
|
|
71
|
+
## 2026.3.25
|
|
72
|
+
- 继续根据类型检查进行了一些修改,当前版本可用
|
|
73
|
+
- 修改 UI title 和 favicon
|
|
74
|
+
|
|
75
|
+
## 2026.3.24
|
|
76
|
+
- 根据类型检查进行了一些修改,当前版本可用
|
|
77
|
+
|
|
78
|
+
## 2026.3.23
|
|
79
|
+
- 创建 webui
|
|
80
|
+
- 部分概念重命名:`IndexCore` -> `IndexRegistry`,`recipe` -> `preset`
|
|
81
|
+
- 恢复 `AgeStructuredPopulation` 的生存率、初始状态构建逻辑
|
|
82
|
+
- 修复了 `gamete_allele_conversion.py` 中 `sex_filter` 无效的问题(现在可以识别字符串和数字表示的性别,且无效的表示会报错)
|
|
83
|
+
- 优化文档
|
|
84
|
+
|
|
85
|
+
## 2026.3.21
|
|
86
|
+
- 重构 hook,优化 hook 缓存
|
|
87
|
+
|
|
88
|
+
## 2026.3.20
|
|
89
|
+
- 移除 `jitclass`,`PopulationState`, `DiscretePopulationState` 和 `PopulationConfig` 全部改为用 `NamedTuple`
|
|
90
|
+
- 优化 `njit` 缓存
|
|
91
|
+
- 增加编译提示信息
|
|
92
|
+
|
|
93
|
+
## 2026.3.18.b
|
|
94
|
+
- Hook 执行内核拆分:将 CSR 执行逻辑抽为外部函数 `execute_csr_event_arrays`,统一以 `HookProgram` 作为数据载体
|
|
95
|
+
- 新增 `HookProgram` 与 `build_hook_program` 作为非 jitclass 路径的数据接口(第一阶段)
|
|
96
|
+
- 完全移除 `HookRegistry` 符号,核心内核统一走 `HookProgram + execute_csr_event_program`
|
|
97
|
+
- 优化 Declarative `when` 表达式的解析,支持 `and`、`or`、`not` 等逻辑运算符,增强表达能力
|
|
98
|
+
- 为 `Op.scale` / `Op.subtract` / `Op.sample` / `Op.set_count` 增加抽样逻辑
|
|
99
|
+
|
|
100
|
+
## 2026.3.18
|
|
101
|
+
- 完善 HomingModificationDrive 逻辑
|
|
102
|
+
- 修复了配子产生的默认逻辑,此前会产生各种 gamete label 的配子(且每一种归一化),现在只产生默认 label("default" 或 index 0)
|
|
103
|
+
- 修复了 hooks 的 bug(响应式 hook 被注册但未被使用)
|
|
104
|
+
- 临时移除 `algorithms.fertilize_with_mating_genotype` 中对 `P` 的归一化,以支持致死效应的模拟(需后续检查正确性)
|
|
105
|
+
|
|
106
|
+
## 2026.3.14
|
|
107
|
+
- 优化种群初始化方式
|
|
108
|
+
- 加入高级转换规则和 recipe 支持
|
|
109
|
+
- 引入基因型 pattern 解析器
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: natal-core
|
|
3
|
-
Version: 0.1.0.
|
|
3
|
+
Version: 0.1.0.dev2
|
|
4
4
|
Summary: Numba-Accelerated Toolkit for Analysis of Lifecycle-based population genetic dynamics
|
|
5
5
|
Project-URL: Homepage, https://github.com/jyzhu-pointless/natal-core
|
|
6
6
|
Project-URL: Documentation, https://natal-core.readthedocs.io/en/latest/
|
|
@@ -33,9 +33,11 @@ Requires-Dist: pytest>=7.0.0; extra == 'test'
|
|
|
33
33
|
Description-Content-Type: text/markdown
|
|
34
34
|
|
|
35
35
|
# ⚡️ NATAL Core
|
|
36
|
+
|
|
36
37
|
**N**umba-**A**ccelerated **T**oolkit for **A**nalysis of **L**ifecycles.
|
|
37
38
|
|
|
38
|
-
[](https://github.com/jyzhu-pointless/natal-core/releases/latest)
|
|
40
|
+
[](https://pypi.org/project/natal-core/)
|
|
39
41
|
[](https://www.python.org/downloads/)
|
|
40
42
|
[](https://numpy.org/)
|
|
41
43
|
[](https://numba.pydata.org/)
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
# ⚡️ NATAL Core
|
|
2
|
+
|
|
2
3
|
**N**umba-**A**ccelerated **T**oolkit for **A**nalysis of **L**ifecycles.
|
|
3
4
|
|
|
4
|
-
[](https://github.com/jyzhu-pointless/natal-core/releases/latest)
|
|
6
|
+
[](https://pypi.org/project/natal-core/)
|
|
5
7
|
[](https://www.python.org/downloads/)
|
|
6
8
|
[](https://numpy.org/)
|
|
7
9
|
[](https://numba.pydata.org/)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# TODO
|
|
2
|
+
|
|
3
|
+
- 复用 `state_translation` 模块的翻译函数到 WebUI
|
|
4
|
+
- `Species.from_dict` 构建后,应当支持指定性染色体
|
|
5
|
+
- 检查性染色体的遗传逻辑是否正确(遗传张量雌雄 index 应该不同)
|
|
6
|
+
|
|
7
|
+
## initialization / finish 现状
|
|
8
|
+
|
|
9
|
+
```txt
|
|
10
|
+
事件定义里仍有 initialization、finish(以及 first/early/late)。
|
|
11
|
+
base_population.py (line 51)
|
|
12
|
+
types.py (line 124)
|
|
13
|
+
kernel 加速路径目前只执行 first/early/late(CSR+chain)。
|
|
14
|
+
simulation_kernels.py (line 382)
|
|
15
|
+
finish 是 Python 层触发(run 结束或 finish_simulation()),不在 kernel 事件链里。
|
|
16
|
+
age_structured_population.py (line 878)
|
|
17
|
+
discrete_generation_population.py (line 233)
|
|
18
|
+
base_population.py (line 801)
|
|
19
|
+
initialization 目前也在 Python 事件体系里,不在 kernel 执行路径。
|
|
20
|
+
```
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# Benchmarking & Profiling Scripts
|
|
2
|
+
|
|
3
|
+
This directory contains performance analysis and optimization scripts for natal-core.
|
|
4
|
+
|
|
5
|
+
## Scripts Overview
|
|
6
|
+
|
|
7
|
+
### Profiling Scripts
|
|
8
|
+
|
|
9
|
+
- **profile_spatial_hex_run.py**: Profile spatial.run(5) with 100x100 demes using cProfile
|
|
10
|
+
```bash
|
|
11
|
+
python benchmarks/profile_spatial_hex_run.py
|
|
12
|
+
```
|
|
13
|
+
Identifies Python-layer hotspots and cumulative function call patterns.
|
|
14
|
+
|
|
15
|
+
- **profile_single_deme_repro.py**: Profile single-deme reproduction (older approach, may need updates)
|
|
16
|
+
```bash
|
|
17
|
+
python benchmarks/profile_single_deme_repro.py
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
- **profile_repro_direct.py**: Direct kernel profiling using low-level SimulationKernels API
|
|
21
|
+
```bash
|
|
22
|
+
python benchmarks/profile_repro_direct.py
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### Benchmarking Scripts
|
|
26
|
+
|
|
27
|
+
- **benchmark_kernel_overhead.py**: Compare state management overhead vs kernel execution time
|
|
28
|
+
```bash
|
|
29
|
+
python benchmarks/benchmark_kernel_overhead.py
|
|
30
|
+
```
|
|
31
|
+
Measures cost of stacking/unstacking arrays and compares with pure kernel time.
|
|
32
|
+
|
|
33
|
+
- **benchmark_stages.py**: Per-tick performance analysis
|
|
34
|
+
```bash
|
|
35
|
+
python benchmarks/benchmark_stages.py
|
|
36
|
+
```
|
|
37
|
+
Measures individual tick execution cost across multiple runs.
|
|
38
|
+
|
|
39
|
+
## Usage Tips
|
|
40
|
+
|
|
41
|
+
### Running from Project Root
|
|
42
|
+
All scripts use PYTHONPATH-relative imports, so run from project root:
|
|
43
|
+
```bash
|
|
44
|
+
cd /Users/pointless/Desktop/work/natal-core
|
|
45
|
+
python benchmarks/profile_spatial_hex_run.py
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Interpreting Results
|
|
49
|
+
|
|
50
|
+
**cProfile Output**:
|
|
51
|
+
- Look for functions with high `cumtime` and high `ncalls`
|
|
52
|
+
- `tottime` shows time spent in function itself
|
|
53
|
+
- `cumtime` includes time in called functions
|
|
54
|
+
|
|
55
|
+
**Benchmark Output**:
|
|
56
|
+
- Average ± std dev gives performance stability indication
|
|
57
|
+
- Variance > 10% suggests system-level noise or thermal effects
|
|
58
|
+
- Single best/worst runs often excluded from analysis
|
|
59
|
+
|
|
60
|
+
### Key Metrics from Last Run
|
|
61
|
+
- **Spatial.run(5) total**: ~2.74-2.88s (5 runs)
|
|
62
|
+
- **Per-tick cost**: ~0.574s ± 0.053s
|
|
63
|
+
- **Python overhead**: 1.4% (state management)
|
|
64
|
+
- **Numba kernel time**: 98.6% (bottleneck)
|
|
65
|
+
|
|
66
|
+
## Performance Analysis Results
|
|
67
|
+
|
|
68
|
+
📊 See `OPTIMIZATION_REPORT.md` in project root for detailed findings.
|
|
69
|
+
|
|
70
|
+
### Recent Optimization
|
|
71
|
+
Copy removal in spatial kernels (run_spatial_reproduction, run_spatial_survival, run_spatial_aging) achieved:
|
|
72
|
+
- 29% speedup in state stack/unstack (3.74s → 2.64s)
|
|
73
|
+
- 56% faster pytest suite (15.83s → 6.89s)
|
|
74
|
+
- 1-2% overall demo improvement
|
|
75
|
+
|
|
76
|
+
### Bottleneck Identified
|
|
77
|
+
98.6% of time spent in @njit Numba code:
|
|
78
|
+
- Not visible in cProfile (Numba functions appear as single call)
|
|
79
|
+
- Dominated by algorithmic cost (mating, fertilization, offspring generation)
|
|
80
|
+
- Further optimization requires:
|
|
81
|
+
- Numba algorithm tuning
|
|
82
|
+
- Parallelization flags adjustment
|
|
83
|
+
- Memory layout optimization
|
|
84
|
+
|
|
85
|
+
## Future Optimization Directions
|
|
86
|
+
|
|
87
|
+
1. **Numba Algorithm Level**
|
|
88
|
+
- Profile mating probability matrix computation
|
|
89
|
+
- Optimize offspring probability tensor construction
|
|
90
|
+
- Reduce allocations in inner loops
|
|
91
|
+
|
|
92
|
+
2. **Parallelization**
|
|
93
|
+
- Test different n_jobs values
|
|
94
|
+
- Verify prange scheduling is optimal
|
|
95
|
+
- Check thread synchronization overhead
|
|
96
|
+
|
|
97
|
+
3. **State Management** (higher risk)
|
|
98
|
+
- Cache frequently accessed arrays
|
|
99
|
+
- Consider mutable state structure (refactor from NamedTuple)
|
|
100
|
+
- Batch state updates
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# type:ignore
|
|
2
|
+
"""
|
|
3
|
+
Compare execution time of run_spatial_fn directly to identify if time is in Numba or Python.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import sys
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
# Ensure project root is in path for imports
|
|
10
|
+
project_root = Path(__file__).parent.parent
|
|
11
|
+
if str(project_root) not in sys.path:
|
|
12
|
+
sys.path.insert(0, str(project_root))
|
|
13
|
+
|
|
14
|
+
import time
|
|
15
|
+
import numpy as np
|
|
16
|
+
from demos.spatial_hex import build_hex_spatial_population
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def benchmark_runs():
|
|
20
|
+
"""Run 3 iterations of spatial.run(5) and measure times."""
|
|
21
|
+
times = []
|
|
22
|
+
|
|
23
|
+
for run_idx in range(3):
|
|
24
|
+
spatial = build_hex_spatial_population()
|
|
25
|
+
spatial.run(1) # warmup
|
|
26
|
+
|
|
27
|
+
start = time.perf_counter()
|
|
28
|
+
spatial.run(5)
|
|
29
|
+
elapsed = time.perf_counter() - start
|
|
30
|
+
times.append(elapsed)
|
|
31
|
+
|
|
32
|
+
print(f"Run {run_idx + 1}: {elapsed:.4f}s")
|
|
33
|
+
|
|
34
|
+
print(f"\nAverage: {np.mean(times):.4f}s (±{np.std(times):.4f}s)")
|
|
35
|
+
return times
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def benchmark_kernel_time():
|
|
39
|
+
"""Measure just the Numba kernel call without state management."""
|
|
40
|
+
spatial = build_hex_spatial_population()
|
|
41
|
+
spatial.run(1) # warmup
|
|
42
|
+
|
|
43
|
+
# Manually reconstruct what run() does, but time only the kernel call
|
|
44
|
+
n_steps = 5
|
|
45
|
+
|
|
46
|
+
# Setup
|
|
47
|
+
hooks = spatial._demes[0].get_compiled_event_hooks()
|
|
48
|
+
run_fn = hooks.run_spatial_fn
|
|
49
|
+
registry = hooks.registry
|
|
50
|
+
config = spatial._shared_config()
|
|
51
|
+
|
|
52
|
+
# Method 1: Measure including state stack/unstack
|
|
53
|
+
print("=" * 60)
|
|
54
|
+
print("Method 1: Including state stack/unstack")
|
|
55
|
+
start = time.perf_counter()
|
|
56
|
+
for _ in range(3):
|
|
57
|
+
ind_all, sperm_all = spatial._stack_deme_state_arrays()
|
|
58
|
+
final_state_tuple, _history, was_stopped = run_fn(
|
|
59
|
+
ind_count_all=ind_all,
|
|
60
|
+
sperm_store_all=sperm_all,
|
|
61
|
+
config=config,
|
|
62
|
+
registry=registry,
|
|
63
|
+
tick=int(spatial._tick),
|
|
64
|
+
n_ticks=int(n_steps),
|
|
65
|
+
adjacency=spatial._adjacency,
|
|
66
|
+
migration_mode=spatial._migration_mode_code,
|
|
67
|
+
topology_rows=int(spatial._topology.rows),
|
|
68
|
+
topology_cols=int(spatial._topology.cols),
|
|
69
|
+
topology_wrap=bool(spatial._topology.wrap),
|
|
70
|
+
migration_kernel=spatial._migration_kernel_array(),
|
|
71
|
+
kernel_include_center=bool(spatial._kernel_include_center),
|
|
72
|
+
migration_rate=float(spatial._migration_rate),
|
|
73
|
+
)
|
|
74
|
+
spatial._apply_stacked_state(final_state_tuple[0], final_state_tuple[1], int(final_state_tuple[2]))
|
|
75
|
+
elapsed = time.perf_counter() - start
|
|
76
|
+
print(f" 3 runs (with stack/unstack): {elapsed:.4f}s ({elapsed/3:.4f}s per run)")
|
|
77
|
+
|
|
78
|
+
# Reset for next test
|
|
79
|
+
spatial = build_hex_spatial_population()
|
|
80
|
+
spatial.run(1) # warmup
|
|
81
|
+
hooks = spatial._demes[0].get_compiled_event_hooks()
|
|
82
|
+
run_fn = hooks.run_spatial_fn
|
|
83
|
+
registry = hooks.registry
|
|
84
|
+
config = spatial._shared_config()
|
|
85
|
+
|
|
86
|
+
# Method 2: Measure with pre-stacked arrays
|
|
87
|
+
print("=" * 60)
|
|
88
|
+
print("Method 2: Pre-stacked arrays (one allocation)")
|
|
89
|
+
start = time.perf_counter()
|
|
90
|
+
|
|
91
|
+
# Stack once
|
|
92
|
+
ind_all, sperm_all = spatial._stack_deme_state_arrays()
|
|
93
|
+
|
|
94
|
+
# Time 3 runs with same arrays
|
|
95
|
+
t0 = time.perf_counter()
|
|
96
|
+
for _ in range(3):
|
|
97
|
+
final_state_tuple, _history, was_stopped = run_fn(
|
|
98
|
+
ind_count_all=ind_all,
|
|
99
|
+
sperm_store_all=sperm_all,
|
|
100
|
+
config=config,
|
|
101
|
+
registry=registry,
|
|
102
|
+
tick=0,
|
|
103
|
+
n_ticks=5,
|
|
104
|
+
adjacency=spatial._adjacency,
|
|
105
|
+
migration_mode=spatial._migration_mode_code,
|
|
106
|
+
topology_rows=int(spatial._topology.rows),
|
|
107
|
+
topology_cols=int(spatial._topology.cols),
|
|
108
|
+
topology_wrap=bool(spatial._topology.wrap),
|
|
109
|
+
migration_kernel=spatial._migration_kernel_array(),
|
|
110
|
+
kernel_include_center=bool(spatial._kernel_include_center),
|
|
111
|
+
migration_rate=float(spatial._migration_rate),
|
|
112
|
+
)
|
|
113
|
+
# Don't apply state for this benchmark
|
|
114
|
+
|
|
115
|
+
elapsed = time.perf_counter() - t0
|
|
116
|
+
print(f" 3 runs (pre-stacked, no unstack): {elapsed:.4f}s ({elapsed/3:.4f}s per run)")
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
if __name__ == "__main__":
|
|
120
|
+
print("=" * 60)
|
|
121
|
+
print("BENCHMARK: spatial.run(5) with 100x100 demes (3 runs)")
|
|
122
|
+
print("=" * 60)
|
|
123
|
+
benchmark_runs()
|
|
124
|
+
|
|
125
|
+
print("\n")
|
|
126
|
+
benchmark_kernel_time()
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# type:ignore
|
|
2
|
+
"""
|
|
3
|
+
Micro-benchmark to measure individual tick costs in spatial simulation.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import sys
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
# Ensure project root is in path for imports
|
|
10
|
+
project_root = Path(__file__).parent.parent
|
|
11
|
+
if str(project_root) not in sys.path:
|
|
12
|
+
sys.path.insert(0, str(project_root))
|
|
13
|
+
|
|
14
|
+
import time
|
|
15
|
+
import numpy as np
|
|
16
|
+
from demos.spatial_hex import build_hex_spatial_population
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def benchmark_individual_stages():
|
|
20
|
+
"""Measure cost of individual ticks."""
|
|
21
|
+
spatial = build_hex_spatial_population()
|
|
22
|
+
spatial.run(1) # warmup
|
|
23
|
+
|
|
24
|
+
print("=" * 60)
|
|
25
|
+
print("Per-tick Performance Analysis")
|
|
26
|
+
print("=" * 60)
|
|
27
|
+
|
|
28
|
+
times = []
|
|
29
|
+
for run_idx in range(3):
|
|
30
|
+
spatial = build_hex_spatial_population()
|
|
31
|
+
spatial.run(1) # warmup
|
|
32
|
+
|
|
33
|
+
start = time.perf_counter()
|
|
34
|
+
spatial.run(1) # measure 1 tick
|
|
35
|
+
elapsed = time.perf_counter() - start
|
|
36
|
+
times.append(elapsed)
|
|
37
|
+
|
|
38
|
+
print(f"Run {run_idx + 1}, 1 tick: {elapsed:.4f}s")
|
|
39
|
+
|
|
40
|
+
print(f"\nAverage per tick: {np.mean(times):.4f}s (±{np.std(times):.4f}s)")
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
if __name__ == "__main__":
|
|
44
|
+
benchmark_individual_stages()
|