jupytermind 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (193) hide show
  1. package/.github/skills/ai-chemistry-scientist/SKILL.md +97 -0
  2. package/.github/skills/ai-chemistry-scientist/manifest.json +156 -0
  3. package/.github/skills/ai-data-scientist/SKILL.md +330 -0
  4. package/.github/skills/ai-genomics-scientist/SKILL.md +98 -0
  5. package/.github/skills/ai-genomics-scientist/manifest.json +93 -0
  6. package/.github/skills/ai-materials-scientist/SKILL.md +51 -0
  7. package/.github/skills/ai-materials-scientist/manifest.json +58 -0
  8. package/.github/skills/ai-scientist/SKILL.md +69 -0
  9. package/.github/skills/ai-scientist/manifest.json +61 -0
  10. package/.github/skills/ai-structural-biology-scientist/SKILL.md +67 -0
  11. package/.github/skills/ai-structural-biology-scientist/manifest.json +72 -0
  12. package/.github/skills/japanese-prose/NOTICE.md +17 -0
  13. package/.github/skills/japanese-prose/SKILL.md +111 -0
  14. package/.github/skills/japanese-prose/references/review-workflow.md +50 -0
  15. package/.github/skills/japanese-prose/references/scoring.md +24 -0
  16. package/.github/skills/japanese-prose/references/writing-guidelines.md +60 -0
  17. package/.github/skills/japanese-prose/scripts/core.py +192 -0
  18. package/.github/skills/japanese-prose/scripts/fixtures/natural.md +5 -0
  19. package/.github/skills/japanese-prose/scripts/fixtures/unnatural.md +5 -0
  20. package/.github/skills/japanese-prose/scripts/lint.py +378 -0
  21. package/.github/skills/japanese-prose/scripts/outline.py +68 -0
  22. package/.github/skills/japanese-prose/scripts/terms.py +112 -0
  23. package/.github/skills/japanese-prose/scripts/test_engine.py +117 -0
  24. package/.github/skills/presentation-planner/SKILL.md +257 -0
  25. package/.github/skills/presentation-planner/assets/design-templates/data-report.yaml +97 -0
  26. package/.github/skills/presentation-planner/assets/design-templates/executive-proposal.yaml +92 -0
  27. package/.github/skills/presentation-planner/assets/design-templates/technical-briefing.yaml +96 -0
  28. package/.github/skills/presentation-planner/assets/scenario-templates/data-report.md +47 -0
  29. package/.github/skills/presentation-planner/assets/scenario-templates/executive-decision.md +43 -0
  30. package/.github/skills/presentation-planner/assets/scenario-templates/technical-briefing.md +45 -0
  31. package/.github/skills/presentation-planner/references/customizing-design-templates.md +160 -0
  32. package/.github/skills/presentation-planner/references/design-spec-schema.md +72 -0
  33. package/.github/skills/presentation-planner/references/handoff-contract.md +49 -0
  34. package/.github/skills/presentation-planner/references/responsibility-boundary.md +32 -0
  35. package/.github/skills/presentation-planner/references/scenario-templates.md +55 -0
  36. package/.github/skills/tech-writer/SKILL.md +434 -0
  37. package/.github/skills/tech-writer/assets/templates/blueprint.md +187 -0
  38. package/.github/skills/tech-writer/assets/templates/design-doc.md +29 -0
  39. package/.github/skills/tech-writer/assets/templates/migration-plan.md +173 -0
  40. package/.github/skills/tech-writer/assets/templates/operations-runbook.md +202 -0
  41. package/.github/skills/tech-writer/assets/templates/pr-description.md +23 -0
  42. package/.github/skills/tech-writer/assets/templates/qiita.md +44 -0
  43. package/.github/skills/tech-writer/assets/templates/readme.md +38 -0
  44. package/.github/skills/tech-writer/assets/templates/requirements-definition.md +170 -0
  45. package/.github/skills/tech-writer/assets/templates/rfi.md +113 -0
  46. package/.github/skills/tech-writer/assets/templates/rfp.md +180 -0
  47. package/.github/skills/tech-writer/assets/templates/security-design.md +167 -0
  48. package/.github/skills/tech-writer/assets/templates/system-design.md +220 -0
  49. package/.github/skills/tech-writer/assets/templates/technical-proposal.md +112 -0
  50. package/.github/skills/tech-writer/assets/templates/test-plan.md +153 -0
  51. package/.github/skills/tech-writer/assets/templates/user-manual.md +22 -0
  52. package/.github/skills/tech-writer/assets/templates/white-paper.md +192 -0
  53. package/.github/skills/tech-writer/references/doctypes/api-docs.md +33 -0
  54. package/.github/skills/tech-writer/references/doctypes/blueprint.md +81 -0
  55. package/.github/skills/tech-writer/references/doctypes/code-comments.md +39 -0
  56. package/.github/skills/tech-writer/references/doctypes/design-doc.md +42 -0
  57. package/.github/skills/tech-writer/references/doctypes/migration-plan.md +63 -0
  58. package/.github/skills/tech-writer/references/doctypes/operations-runbook.md +63 -0
  59. package/.github/skills/tech-writer/references/doctypes/pr-commit.md +82 -0
  60. package/.github/skills/tech-writer/references/doctypes/qiita.md +75 -0
  61. package/.github/skills/tech-writer/references/doctypes/readme.md +43 -0
  62. package/.github/skills/tech-writer/references/doctypes/release-notes.md +30 -0
  63. package/.github/skills/tech-writer/references/doctypes/requirements-definition.md +61 -0
  64. package/.github/skills/tech-writer/references/doctypes/rfi.md +43 -0
  65. package/.github/skills/tech-writer/references/doctypes/rfp.md +46 -0
  66. package/.github/skills/tech-writer/references/doctypes/security-design.md +71 -0
  67. package/.github/skills/tech-writer/references/doctypes/system-design.md +74 -0
  68. package/.github/skills/tech-writer/references/doctypes/technical-proposal.md +49 -0
  69. package/.github/skills/tech-writer/references/doctypes/test-plan.md +67 -0
  70. package/.github/skills/tech-writer/references/doctypes/user-manual.md +58 -0
  71. package/.github/skills/tech-writer/references/doctypes/white-paper.md +84 -0
  72. package/.github/skills/tech-writer/references/doctypes/zenn.md +66 -0
  73. package/.github/skills/tech-writer/references/japanese-prose-optimization.md +110 -0
  74. package/.github/skills/tech-writer/references/style-constitution.md +104 -0
  75. package/.github/skills/tech-writer/scripts/lint.py +412 -0
  76. package/LICENSE +21 -0
  77. package/README.md +92 -0
  78. package/bin/ai-data-scientist.js +123 -0
  79. package/package.json +41 -0
  80. package/pyproject.toml +45 -0
  81. package/src/ai_chemistry_scientist/__init__.py +0 -0
  82. package/src/ai_chemistry_scientist/admet_prediction.py +71 -0
  83. package/src/ai_chemistry_scientist/bioactivity_classification.py +73 -0
  84. package/src/ai_chemistry_scientist/data/sample_molecules.csv +21 -0
  85. package/src/ai_chemistry_scientist/dispatch.py +369 -0
  86. package/src/ai_chemistry_scientist/docking_score.py +97 -0
  87. package/src/ai_chemistry_scientist/drug_likeness_rules.py +84 -0
  88. package/src/ai_chemistry_scientist/evidence.py +41 -0
  89. package/src/ai_chemistry_scientist/molecular_descriptors.py +97 -0
  90. package/src/ai_chemistry_scientist/molecular_formula_mass.py +40 -0
  91. package/src/ai_chemistry_scientist/molecular_similarity.py +78 -0
  92. package/src/ai_chemistry_scientist/qsar_modeling.py +105 -0
  93. package/src/ai_chemistry_scientist/salt_standardization.py +81 -0
  94. package/src/ai_chemistry_scientist/structural_alerts.py +76 -0
  95. package/src/ai_chemistry_scientist/structure_format_conversion.py +84 -0
  96. package/src/ai_chemistry_scientist/validation.py +70 -0
  97. package/src/ai_data_scientist/__init__.py +0 -0
  98. package/src/ai_data_scientist/analysis_assumptions.py +121 -0
  99. package/src/ai_data_scientist/anomaly_detection.py +39 -0
  100. package/src/ai_data_scientist/automl.py +109 -0
  101. package/src/ai_data_scientist/cleaning.py +56 -0
  102. package/src/ai_data_scientist/cli.py +90 -0
  103. package/src/ai_data_scientist/clustering.py +54 -0
  104. package/src/ai_data_scientist/dashboard.py +33 -0
  105. package/src/ai_data_scientist/data_definition.py +100 -0
  106. package/src/ai_data_scientist/data_quality.py +164 -0
  107. package/src/ai_data_scientist/dataset_validation.py +135 -0
  108. package/src/ai_data_scientist/dependency_pins.py +60 -0
  109. package/src/ai_data_scientist/eda.py +82 -0
  110. package/src/ai_data_scientist/experiment_evaluation.py +635 -0
  111. package/src/ai_data_scientist/explainability.py +340 -0
  112. package/src/ai_data_scientist/feature_engineering.py +163 -0
  113. package/src/ai_data_scientist/gate_config.py +32 -0
  114. package/src/ai_data_scientist/ingestion.py +127 -0
  115. package/src/ai_data_scientist/insight_engine.py +180 -0
  116. package/src/ai_data_scientist/japanese_nlp.py +43 -0
  117. package/src/ai_data_scientist/jupyter_launcher.py +137 -0
  118. package/src/ai_data_scientist/jupyter_mcp_client.py +94 -0
  119. package/src/ai_data_scientist/language_router.py +28 -0
  120. package/src/ai_data_scientist/lifecycle.py +221 -0
  121. package/src/ai_data_scientist/mcp_gateway.py +113 -0
  122. package/src/ai_data_scientist/mcp_runtime.py +194 -0
  123. package/src/ai_data_scientist/mcp_transport.py +53 -0
  124. package/src/ai_data_scientist/ml_modeling.py +451 -0
  125. package/src/ai_data_scientist/model_tuning.py +104 -0
  126. package/src/ai_data_scientist/notebook_audit.py +574 -0
  127. package/src/ai_data_scientist/project_manager.py +243 -0
  128. package/src/ai_data_scientist/report_export.py +73 -0
  129. package/src/ai_data_scientist/sensitivity.py +445 -0
  130. package/src/ai_data_scientist/signal_analysis.py +201 -0
  131. package/src/ai_data_scientist/skill_packaging.py +40 -0
  132. package/src/ai_data_scientist/stats_analysis.py +88 -0
  133. package/src/ai_data_scientist/text_nlp.py +44 -0
  134. package/src/ai_data_scientist/timeseries.py +68 -0
  135. package/src/ai_data_scientist/visualization.py +708 -0
  136. package/src/ai_genomics_scientist/__init__.py +1 -0
  137. package/src/ai_genomics_scientist/differential_expression.py +147 -0
  138. package/src/ai_genomics_scientist/dispatch.py +267 -0
  139. package/src/ai_genomics_scientist/evidence.py +45 -0
  140. package/src/ai_genomics_scientist/gene_set_enrichment.py +76 -0
  141. package/src/ai_genomics_scientist/sequence_alignment.py +97 -0
  142. package/src/ai_genomics_scientist/sequence_features.py +111 -0
  143. package/src/ai_genomics_scientist/splice_site_scoring.py +66 -0
  144. package/src/ai_genomics_scientist/validation.py +83 -0
  145. package/src/ai_genomics_scientist/variant_effect.py +147 -0
  146. package/src/ai_genomics_scientist/variant_pathogenicity.py +125 -0
  147. package/src/ai_materials_scientist/__init__.py +0 -0
  148. package/src/ai_materials_scientist/calphad.py +117 -0
  149. package/src/ai_materials_scientist/classical_monte_carlo.py +165 -0
  150. package/src/ai_materials_scientist/crystal_plasticity.py +184 -0
  151. package/src/ai_materials_scientist/dispatch.py +100 -0
  152. package/src/ai_materials_scientist/evidence.py +84 -0
  153. package/src/ai_materials_scientist/fem.py +279 -0
  154. package/src/ai_materials_scientist/kinetic_monte_carlo.py +145 -0
  155. package/src/ai_materials_scientist/molecular_dynamics.py +240 -0
  156. package/src/ai_materials_scientist/phase_field.py +167 -0
  157. package/src/ai_materials_scientist/validation.py +70 -0
  158. package/src/ai_scientist/__init__.py +1 -0
  159. package/src/ai_scientist/completion_gate.py +15 -0
  160. package/src/ai_scientist/data_analysis.py +46 -0
  161. package/src/ai_scientist/evidence_registry.py +99 -0
  162. package/src/ai_scientist/experimental_design.py +20 -0
  163. package/src/ai_scientist/language.py +14 -0
  164. package/src/ai_scientist/latex_renderer.py +41 -0
  165. package/src/ai_scientist/literature_review.py +37 -0
  166. package/src/ai_scientist/manifest.py +87 -0
  167. package/src/ai_scientist/manuscript.py +94 -0
  168. package/src/ai_scientist/mcp_config.py +76 -0
  169. package/src/ai_scientist/mcp_external.py +42 -0
  170. package/src/ai_scientist/mcp_failures.py +23 -0
  171. package/src/ai_scientist/mcp_gateway.py +38 -0
  172. package/src/ai_scientist/mcp_managed.py +180 -0
  173. package/src/ai_scientist/npm_packaging.py +49 -0
  174. package/src/ai_scientist/orchestrator.py +133 -0
  175. package/src/ai_scientist/peer_review.py +60 -0
  176. package/src/ai_scientist/phase_gate.py +74 -0
  177. package/src/ai_scientist/phase_state.py +230 -0
  178. package/src/ai_scientist/presentation.py +56 -0
  179. package/src/ai_scientist/project_config.py +31 -0
  180. package/src/ai_scientist/project_handle.py +74 -0
  181. package/src/ai_scientist/reproducibility.py +20 -0
  182. package/src/ai_scientist/research_planning.py +20 -0
  183. package/src/ai_scientist/skill_invocation.py +21 -0
  184. package/src/ai_scientist/tdd_gate.py +99 -0
  185. package/src/ai_structural_biology_scientist/__init__.py +0 -0
  186. package/src/ai_structural_biology_scientist/contact_map.py +87 -0
  187. package/src/ai_structural_biology_scientist/dispatch.py +269 -0
  188. package/src/ai_structural_biology_scientist/evidence.py +43 -0
  189. package/src/ai_structural_biology_scientist/hydrophobicity.py +101 -0
  190. package/src/ai_structural_biology_scientist/protein_docking_score.py +104 -0
  191. package/src/ai_structural_biology_scientist/secondary_structure.py +95 -0
  192. package/src/ai_structural_biology_scientist/structural_similarity.py +74 -0
  193. package/src/ai_structural_biology_scientist/validation.py +100 -0
@@ -0,0 +1,165 @@
1
+ """Classical Monte Carlo lattice sampling module (DES-AIMS-030 / REQ-AIMS-030)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import numpy as np
6
+
7
+ from ai_materials_scientist.evidence import record_run
8
+ from ai_materials_scientist.validation import (
9
+ check_finite_array,
10
+ register_validator,
11
+ validate_parameters,
12
+ )
13
+
14
+ _MODULE_NAME = "classical-monte-carlo"
15
+
16
+
17
+ def _total_energy_per_site(spins: np.ndarray, J: float) -> float:
18
+ """-J * sum over unique nearest-neighbor bonds, divided by N sites."""
19
+ n = spins.size
20
+ right = np.roll(spins, -1, axis=1)
21
+ down = np.roll(spins, -1, axis=0)
22
+ bond_sum = np.sum(spins * right) + np.sum(spins * down)
23
+ return float(-J * bond_sum / n)
24
+
25
+
26
+ def _local_field(spins: np.ndarray, i: int, j: int, L: int) -> int:
27
+ return (
28
+ spins[(i + 1) % L, j]
29
+ + spins[(i - 1) % L, j]
30
+ + spins[i, (j + 1) % L]
31
+ + spins[i, (j - 1) % L]
32
+ )
33
+
34
+
35
+ def _sweep(spins: np.ndarray, L: int, J: float, T: float, rng: np.random.Generator) -> None:
36
+ """One raster-order full-lattice sweep of single-site Metropolis trial moves."""
37
+ random_values = rng.random((L, L))
38
+ for i in range(L):
39
+ for j in range(L):
40
+ s = spins[i, j]
41
+ neighbor_sum = _local_field(spins, i, j, L)
42
+ delta_e = 2.0 * J * s * neighbor_sum
43
+ if delta_e <= 0 or random_values[i, j] < np.exp(-delta_e / T):
44
+ spins[i, j] = -s
45
+
46
+
47
+ def _mc_validator(params: dict) -> dict:
48
+ """DES-AIMS-002 registered validator for module_name='classical-monte-carlo'."""
49
+ if params["L"] <= 0:
50
+ return {"ok": False, "parameter": "L", "constraint": "L > 0"}
51
+ if params["J"] <= 0:
52
+ return {"ok": False, "parameter": "J", "constraint": "J > 0"}
53
+ if params["T"] <= 0:
54
+ return {"ok": False, "parameter": "T", "constraint": "T > 0"}
55
+ if params["equilibration_sweeps"] < 0:
56
+ return {
57
+ "ok": False,
58
+ "parameter": "equilibration_sweeps",
59
+ "constraint": "equilibration_sweeps >= 0",
60
+ }
61
+ if params["sampling_sweeps"] <= 0:
62
+ return {
63
+ "ok": False,
64
+ "parameter": "sampling_sweeps",
65
+ "constraint": "sampling_sweeps > 0",
66
+ }
67
+ initial_spins = params["initial_spins"]
68
+ finite_check = check_finite_array("initial_spins", initial_spins)
69
+ if not finite_check["ok"]:
70
+ return finite_check
71
+ if initial_spins.shape != (params["L"], params["L"]):
72
+ return {
73
+ "ok": False,
74
+ "parameter": "initial_spins",
75
+ "constraint": "initial_spins.shape == (L, L)",
76
+ }
77
+ if not np.all(np.isin(initial_spins, (-1, 1))):
78
+ return {
79
+ "ok": False,
80
+ "parameter": "initial_spins",
81
+ "constraint": "every spin must be +1 or -1",
82
+ }
83
+ return {"ok": True}
84
+
85
+
86
+ register_validator(_MODULE_NAME, _mc_validator)
87
+
88
+
89
+ def _validate(L, J, T, equilibration_sweeps, sampling_sweeps, initial_spins) -> None:
90
+ result = validate_parameters(
91
+ _MODULE_NAME,
92
+ {
93
+ "L": L,
94
+ "J": J,
95
+ "T": T,
96
+ "equilibration_sweeps": equilibration_sweeps,
97
+ "sampling_sweeps": sampling_sweeps,
98
+ "initial_spins": initial_spins,
99
+ },
100
+ )
101
+ if not result["ok"]:
102
+ raise ValueError(f"{result['parameter']}: {result['constraint']}")
103
+
104
+
105
+ # @id CODE-AIMS-030
106
+ # @implements REQ-AIMS-030 REQ-AIMS-003
107
+ # @design DES-AIMS-030
108
+ def run_classical_monte_carlo(
109
+ L: int,
110
+ J: float,
111
+ T: float,
112
+ equilibration_sweeps: int,
113
+ sampling_sweeps: int,
114
+ seed: int,
115
+ initial_spins: np.ndarray,
116
+ ) -> dict:
117
+ """Run Metropolis-criterion single-site Ising sweeps (REQ-AIMS-030).
118
+
119
+ Rejects any out-of-domain parameter (REQ-AIMS-003) before any spin-flip
120
+ trial move, and never mutates ``initial_spins``.
121
+ """
122
+ _validate(L, J, T, equilibration_sweeps, sampling_sweeps, initial_spins)
123
+
124
+ spins = initial_spins.astype(np.int64).copy()
125
+ rng = np.random.default_rng(seed)
126
+
127
+ for _ in range(equilibration_sweeps):
128
+ _sweep(spins, L, J, T, rng)
129
+
130
+ history = []
131
+ for _ in range(sampling_sweeps):
132
+ _sweep(spins, L, J, T, rng)
133
+ energy_per_site = _total_energy_per_site(spins, J)
134
+ magnetization_per_site = float(np.sum(spins)) / spins.size
135
+ history.append(
136
+ {"energy_per_site": energy_per_site, "magnetization_per_site": magnetization_per_site}
137
+ )
138
+
139
+ mean_energy = float(np.mean([h["energy_per_site"] for h in history]))
140
+ mean_abs_magnetization = float(np.mean([abs(h["magnetization_per_site"]) for h in history]))
141
+
142
+ return {
143
+ "mean_energy": mean_energy,
144
+ "mean_abs_magnetization": mean_abs_magnetization,
145
+ "history": history,
146
+ }
147
+
148
+
149
+ # @id CODE-AIMS-901
150
+ # @implements REQ-AIMS-030 REQ-AIMS-004 REQ-AIMS-005
151
+ # @design DES-AIMS-030
152
+ def run_classical_monte_carlo_with_evidence(**kwargs) -> dict:
153
+ """Run classical Monte Carlo and wrap the result as a reproducible RunRecord."""
154
+ result = run_classical_monte_carlo(**kwargs)
155
+ energies = np.array([h["energy_per_site"] for h in result["history"]], dtype=np.float64)
156
+ magnetizations = np.array(
157
+ [h["magnetization_per_site"] for h in result["history"]], dtype=np.float64
158
+ )
159
+ return record_run(
160
+ module_name=_MODULE_NAME,
161
+ unit_system="ising-reduced",
162
+ params={k: v for k, v in kwargs.items() if k != "initial_spins"},
163
+ arrays={"energy_history": energies, "magnetization_history": magnetizations},
164
+ seed=kwargs["seed"],
165
+ )
@@ -0,0 +1,184 @@
1
+ """Single-point crystal plasticity slip activation module (DES-AIMS-050 / REQ-AIMS-050)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import numpy as np
6
+
7
+ from ai_materials_scientist.evidence import record_run
8
+ from ai_materials_scientist.validation import (
9
+ check_finite_array,
10
+ register_validator,
11
+ validate_parameters,
12
+ )
13
+
14
+ _MODULE_NAME = "crystal-plasticity"
15
+
16
+ # Table CP-12: fixed ordered (plane normal, slip direction) pairs, crystal frame.
17
+ _SLIP_SYSTEMS = [
18
+ ((1, 1, 1), (0, 1, -1)),
19
+ ((1, 1, 1), (1, 0, -1)),
20
+ ((1, 1, 1), (1, -1, 0)),
21
+ ((1, 1, -1), (0, 1, 1)),
22
+ ((1, 1, -1), (1, 0, 1)),
23
+ ((1, 1, -1), (1, -1, 0)),
24
+ ((1, -1, 1), (0, 1, 1)),
25
+ ((1, -1, 1), (1, 0, -1)),
26
+ ((1, -1, 1), (1, 1, 0)),
27
+ ((-1, 1, 1), (0, 1, -1)),
28
+ ((-1, 1, 1), (1, 0, 1)),
29
+ ((-1, 1, 1), (1, 1, 0)),
30
+ ]
31
+
32
+ _ORTHOGONALITY_TOL = 1e-6
33
+ _DETERMINANT_TOL = 1e-6
34
+ _SYMMETRY_TOL = 1e-6
35
+ _MIN_STRESS_NORM = 1e-12
36
+
37
+
38
+ def _cp_validator(params: dict) -> dict:
39
+ """DES-AIMS-002 registered validator for module_name='crystal-plasticity'."""
40
+ q = params["orientation_q"]
41
+ stress = params["stress_tensor"]
42
+ crss = params["crss"]
43
+ gamma_dot_0 = params["gamma_dot_0"]
44
+ n = params["n"]
45
+
46
+ q_check = check_finite_array("orientation_q", q)
47
+ if not q_check["ok"]:
48
+ return q_check
49
+ if q.shape != (3, 3):
50
+ return {"ok": False, "parameter": "orientation_q", "constraint": "shape == (3, 3)"}
51
+ if np.max(np.abs(q.T @ q - np.eye(3))) > _ORTHOGONALITY_TOL:
52
+ return {
53
+ "ok": False,
54
+ "parameter": "orientation_q",
55
+ "constraint": "Q must be orthogonal (Q^T Q == I within 1e-6)",
56
+ }
57
+ if abs(np.linalg.det(q) - 1.0) > _DETERMINANT_TOL:
58
+ return {
59
+ "ok": False,
60
+ "parameter": "orientation_q",
61
+ "constraint": "det(Q) == 1 within 1e-6",
62
+ }
63
+
64
+ stress_check = check_finite_array("stress_tensor", stress)
65
+ if not stress_check["ok"]:
66
+ return stress_check
67
+ if stress.shape != (3, 3):
68
+ return {"ok": False, "parameter": "stress_tensor", "constraint": "shape == (3, 3)"}
69
+ if np.max(np.abs(stress - stress.T)) > _SYMMETRY_TOL:
70
+ return {
71
+ "ok": False,
72
+ "parameter": "stress_tensor",
73
+ "constraint": "stress_tensor must be symmetric within 1e-6",
74
+ }
75
+ if np.linalg.norm(stress) < _MIN_STRESS_NORM:
76
+ return {
77
+ "ok": False,
78
+ "parameter": "stress_tensor",
79
+ "constraint": "Frobenius norm must be >= 1e-12",
80
+ }
81
+
82
+ if crss <= 0 or not np.isfinite(crss):
83
+ return {"ok": False, "parameter": "crss", "constraint": "crss > 0 and finite"}
84
+ if gamma_dot_0 <= 0 or not np.isfinite(gamma_dot_0):
85
+ return {
86
+ "ok": False,
87
+ "parameter": "gamma_dot_0",
88
+ "constraint": "gamma_dot_0 > 0 and finite",
89
+ }
90
+ if n <= 0 or not np.isfinite(n):
91
+ return {"ok": False, "parameter": "n", "constraint": "n > 0 and finite"}
92
+ return {"ok": True}
93
+
94
+
95
+ register_validator(_MODULE_NAME, _cp_validator)
96
+
97
+
98
+ def _validate(orientation_q, stress_tensor, crss, gamma_dot_0, n) -> None:
99
+ result = validate_parameters(
100
+ _MODULE_NAME,
101
+ {
102
+ "orientation_q": orientation_q,
103
+ "stress_tensor": stress_tensor,
104
+ "crss": crss,
105
+ "gamma_dot_0": gamma_dot_0,
106
+ "n": n,
107
+ },
108
+ )
109
+ if not result["ok"]:
110
+ raise ValueError(f"{result['parameter']}: {result['constraint']}")
111
+
112
+
113
+ # @id CODE-AIMS-050
114
+ # @implements REQ-AIMS-050 REQ-AIMS-003
115
+ # @design DES-AIMS-050
116
+ def run_crystal_plasticity(
117
+ orientation_q: np.ndarray,
118
+ stress_tensor: np.ndarray,
119
+ crss: float,
120
+ gamma_dot_0: float,
121
+ n: float,
122
+ ) -> dict:
123
+ """Evaluate Schmid factors, resolved shear stresses, and slip activation.
124
+
125
+ Rejects any out-of-domain parameter (REQ-AIMS-003) before any
126
+ Schmid-factor computation (REQ-AIMS-050).
127
+ """
128
+ _validate(orientation_q, stress_tensor, crss, gamma_dot_0, n)
129
+
130
+ stress_norm = np.linalg.norm(stress_tensor)
131
+ stress_hat = stress_tensor / stress_norm
132
+
133
+ schmid_factors = []
134
+ resolved_shear_stresses = []
135
+ active_systems = []
136
+ shear_strain_rates = []
137
+
138
+ for plane_normal, slip_direction in _SLIP_SYSTEMS:
139
+ n_crystal = np.array(plane_normal, dtype=np.float64)
140
+ d_crystal = np.array(slip_direction, dtype=np.float64)
141
+ n_sample = orientation_q @ n_crystal
142
+ d_sample = orientation_q @ d_crystal
143
+ n_hat = n_sample / np.linalg.norm(n_sample)
144
+ d_hat = d_sample / np.linalg.norm(d_sample)
145
+
146
+ schmid_tensor = np.outer(n_hat, d_hat)
147
+ schmid_factor = float(np.sum(schmid_tensor * stress_hat))
148
+ tau = schmid_factor * stress_norm
149
+
150
+ is_active = bool(abs(tau) >= crss)
151
+ shear_rate = gamma_dot_0 * np.sign(tau) * abs(tau / crss) ** n if is_active else 0.0
152
+
153
+ schmid_factors.append(schmid_factor)
154
+ resolved_shear_stresses.append(tau)
155
+ active_systems.append(is_active)
156
+ shear_strain_rates.append(float(shear_rate))
157
+
158
+ return {
159
+ "schmid_factors": schmid_factors,
160
+ "resolved_shear_stresses": resolved_shear_stresses,
161
+ "active_systems": active_systems,
162
+ "shear_strain_rates": shear_strain_rates,
163
+ }
164
+
165
+
166
+ # @id CODE-AIMS-902
167
+ # @implements REQ-AIMS-050 REQ-AIMS-004 REQ-AIMS-005
168
+ # @design DES-AIMS-050
169
+ def run_crystal_plasticity_with_evidence(**kwargs) -> dict:
170
+ """Run crystal plasticity and wrap the result as a reproducible RunRecord."""
171
+ result = run_crystal_plasticity(**kwargs)
172
+ return record_run(
173
+ module_name=_MODULE_NAME,
174
+ unit_system="stress-units-as-supplied",
175
+ params={k: v for k, v in kwargs.items() if k not in ("orientation_q", "stress_tensor")},
176
+ arrays={
177
+ "schmid_factors": np.array(result["schmid_factors"], dtype=np.float64),
178
+ "resolved_shear_stresses": np.array(
179
+ result["resolved_shear_stresses"], dtype=np.float64
180
+ ),
181
+ "shear_strain_rates": np.array(result["shear_strain_rates"], dtype=np.float64),
182
+ },
183
+ seed=None,
184
+ )
@@ -0,0 +1,100 @@
1
+ """Request dispatcher for ai_materials_scientist (DES-AIMS-001)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from pathlib import Path
7
+
8
+ from ai_data_scientist.language_router import detect_language as _detect_language
9
+
10
+ REPO_ROOT = Path(__file__).resolve().parents[2]
11
+ DEFAULT_MANIFEST_PATH = (
12
+ REPO_ROOT / ".github" / "skills" / "ai-materials-scientist" / "manifest.json"
13
+ )
14
+
15
+
16
+ def load_manifest(manifest_path: Path | None = None) -> dict:
17
+ """Load the static method-name-to-module manifest (DES-AIMS-001)."""
18
+ path = manifest_path or DEFAULT_MANIFEST_PATH
19
+ return json.loads(path.read_text(encoding="utf-8"))
20
+
21
+
22
+ def _matched_methods(request_text: str, manifest: dict) -> list[str]:
23
+ lowered = request_text.lower()
24
+ matched = []
25
+ for method, entry in manifest.items():
26
+ names = entry.get("names", {})
27
+ candidates = list(names.get("en", [])) + list(names.get("ja", []))
28
+ if any(
29
+ (candidate.lower() in lowered) if candidate.isascii() else (candidate in request_text)
30
+ for candidate in candidates
31
+ ):
32
+ matched.append(method)
33
+ return matched
34
+
35
+
36
+ # @id CODE-AIMS-001
37
+ # @implements REQ-AIMS-001 REQ-AIMS-002
38
+ # @design DES-AIMS-001
39
+ def dispatch(
40
+ request_text: str,
41
+ language: str | None = None,
42
+ manifest_path: Path | None = None,
43
+ ) -> dict:
44
+ """Classify ``request_text`` and dispatch to exactly one matched module.
45
+
46
+ ``language`` may be supplied explicitly; otherwise it is detected from
47
+ ``request_text`` (REQ-AIMS-001) and propagated into the result so every
48
+ downstream (module handler, clarification, rejection) path can render
49
+ its user-facing text in that language.
50
+ """
51
+ if not isinstance(request_text, str):
52
+ raise ValueError("request_text: must be a str") # noqa: TRY004
53
+ if language is not None and language not in ("en", "ja"):
54
+ raise ValueError("language: must be 'en' or 'ja' when explicitly supplied")
55
+ detected_language = language or _detect_language(request_text)
56
+ manifest = load_manifest(manifest_path)
57
+ matched = _matched_methods(request_text, manifest)
58
+
59
+ if len(matched) == 1:
60
+ return {
61
+ "outcome": "dispatch",
62
+ "module": matched[0],
63
+ "language": detected_language,
64
+ # Populated once the matched module's handler is implemented and
65
+ # wired in (DES-AIMS-010..070); absent any handler yet, this is
66
+ # explicitly None rather than an omitted key, per DES-AIMS-001's
67
+ # DispatchResult {module, handler_result} contract.
68
+ "handler_result": None,
69
+ }
70
+ if len(matched) > 1:
71
+ return {
72
+ "outcome": "clarification",
73
+ "candidates": matched,
74
+ "language": detected_language,
75
+ "clarification_question": _render_clarification(matched, detected_language),
76
+ }
77
+ return {
78
+ "outcome": "rejected",
79
+ "language": detected_language,
80
+ "rejected_method": _render_rejection(detected_language),
81
+ }
82
+
83
+
84
+ def _render_clarification(candidates: list[str], language: str) -> str:
85
+ """Render a single-sentence clarification question in ``language``.
86
+
87
+ Method names (e.g. ``phase-field``) are the only permitted non-``language``
88
+ tokens, per REQ-AIMS-001's acceptance.
89
+ """
90
+ names = "、".join(candidates) if language == "ja" else ", ".join(candidates)
91
+ if language == "ja":
92
+ return f"{names} のどちらを意図していますか。明確にしてください。"
93
+ return f"Did you mean {names}? Please clarify which method you want."
94
+
95
+
96
+ def _render_rejection(language: str) -> str:
97
+ """Render a single-sentence rejection message in ``language``."""
98
+ if language == "ja":
99
+ return "対応するシミュレーション手法が要求から認識されませんでした。"
100
+ return "No supported simulation method was recognized in your request."
@@ -0,0 +1,84 @@
1
+ """Run evidence recording and JSON-safe codec (DES-AIMS-003 / REQ-AIMS-004/005)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+ import numpy as np
8
+
9
+ SCHEMA_VERSION = 1
10
+
11
+
12
+ # @id CODE-AIMS-003
13
+ # @implements REQ-AIMS-004 REQ-AIMS-005
14
+ # @design DES-AIMS-003
15
+ def record_run(
16
+ module_name: str,
17
+ unit_system: str,
18
+ params: dict[str, Any],
19
+ arrays: dict[str, np.ndarray],
20
+ seed: int | None,
21
+ *,
22
+ allow_integer_arrays: frozenset[str] = frozenset(),
23
+ ) -> dict:
24
+ """Build a RunRecord with exactly `metadata`, `parameters`, `arrays`.
25
+
26
+ ``seed`` is the explicit integer seed for a stochastic module, or
27
+ ``None`` for a deterministic module (REQ-AIMS-004). Every array must be
28
+ ``float64`` unless its name is listed in ``allow_integer_arrays`` (a
29
+ module's own requirement explicitly stating an integer array).
30
+ """
31
+ for name, array in arrays.items():
32
+ if name in allow_integer_arrays:
33
+ continue
34
+ if array.dtype != np.float64:
35
+ raise ValueError(
36
+ f"array '{name}' must be float64 unless explicitly declared integer "
37
+ f"(REQ-AIMS-004), got dtype={array.dtype}"
38
+ )
39
+ return {
40
+ "metadata": {
41
+ "module": module_name,
42
+ "unit_system": unit_system,
43
+ "schema_version": SCHEMA_VERSION,
44
+ "seed": seed,
45
+ },
46
+ "parameters": dict(params),
47
+ "arrays": dict(arrays),
48
+ }
49
+
50
+
51
+ # @id CODE-AIMS-903
52
+ # @implements REQ-AIMS-004
53
+ # @design DES-AIMS-003
54
+ def to_json(run_record: dict) -> dict:
55
+ """Encode a RunRecord's `arrays` ndarrays as {dtype, shape, data}."""
56
+ encoded_arrays = {}
57
+ for name, array in run_record["arrays"].items():
58
+ encoded_arrays[name] = {
59
+ "dtype": str(array.dtype),
60
+ "shape": list(array.shape),
61
+ "data": array.tolist(),
62
+ }
63
+ return {
64
+ "metadata": dict(run_record["metadata"]),
65
+ "parameters": dict(run_record["parameters"]),
66
+ "arrays": encoded_arrays,
67
+ }
68
+
69
+
70
+ # @id CODE-AIMS-904
71
+ # @implements REQ-AIMS-004
72
+ # @design DES-AIMS-003
73
+ def from_json(json_safe_record: dict) -> dict:
74
+ """Reconstruct a RunRecord from its JSON-safe encoding (inverse of to_json)."""
75
+ decoded_arrays = {}
76
+ for name, encoded in json_safe_record["arrays"].items():
77
+ decoded_arrays[name] = np.array(encoded["data"], dtype=encoded["dtype"]).reshape(
78
+ encoded["shape"]
79
+ )
80
+ return {
81
+ "metadata": dict(json_safe_record["metadata"]),
82
+ "parameters": dict(json_safe_record["parameters"]),
83
+ "arrays": decoded_arrays,
84
+ }