psyclaw 0.28.1 → 0.28.2

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 (128) hide show
  1. package/README.md +5 -5
  2. package/agents/recommended/catalog.json +1 -1
  3. package/dist/src/adapters/pi/extension.js +43 -9
  4. package/dist/src/adapters/pi/extension.js.map +1 -1
  5. package/dist/src/ars/mode-editor.d.ts +20 -0
  6. package/dist/src/ars/mode-editor.js +57 -0
  7. package/dist/src/ars/mode-editor.js.map +1 -0
  8. package/dist/src/ars/profile.d.ts +4 -1
  9. package/dist/src/ars/profile.js +23 -6
  10. package/dist/src/ars/profile.js.map +1 -1
  11. package/package.json +10 -3
  12. package/vendor/academic-paper-skills/LICENSE +21 -0
  13. package/vendor/academic-paper-skills/NOTICE.md +7 -0
  14. package/vendor/academic-paper-skills/PSYCLAW_SOURCE.json +14 -0
  15. package/vendor/academic-paper-skills/academic-paper-composer/SKILL.md +840 -0
  16. package/vendor/academic-paper-skills/academic-paper-composer/references/section_guides.md +675 -0
  17. package/vendor/academic-paper-skills/academic-paper-composer/references/writing_standards.md +629 -0
  18. package/vendor/academic-paper-skills/academic-paper-composer/scripts/chapter_quality_check.py +470 -0
  19. package/vendor/academic-paper-skills/academic-paper-composer/scripts/final_evaluation.py +550 -0
  20. package/vendor/academic-paper-skills/academic-paper-strategist/SKILL.md +670 -0
  21. package/vendor/academic-paper-skills/academic-paper-strategist/references/quality_standards.md +336 -0
  22. package/vendor/academic-paper-skills/academic-paper-strategist/references/search_strategy.md +459 -0
  23. package/vendor/academic-paper-skills/academic-paper-strategist/scripts/evaluate_samples.py +300 -0
  24. package/vendor/academic-paper-skills/academic-paper-strategist/scripts/gap_analysis.py +399 -0
  25. package/vendor/ars/pi/wrapper.js +9 -0
  26. package/vendor/nature-skills/LICENSE +201 -0
  27. package/vendor/nature-skills/NOTICE.md +7 -0
  28. package/vendor/nature-skills/PSYCLAW_SOURCE.json +19 -0
  29. package/vendor/nature-skills/skills/nature-figure/README.md +107 -0
  30. package/vendor/nature-skills/skills/nature-figure/README_EN.md +107 -0
  31. package/vendor/nature-skills/skills/nature-figure/SKILL.md +154 -0
  32. package/vendor/nature-skills/skills/nature-figure/agents/openai.yaml +4 -0
  33. package/vendor/nature-skills/skills/nature-figure/evals/evals.json +325 -0
  34. package/vendor/nature-skills/skills/nature-figure/manifest.yaml +103 -0
  35. package/vendor/nature-skills/skills/nature-figure/references/ai-graphical-abstract-workflow.md +129 -0
  36. package/vendor/nature-skills/skills/nature-figure/references/api.md +576 -0
  37. package/vendor/nature-skills/skills/nature-figure/references/asset-adaptation.md +72 -0
  38. package/vendor/nature-skills/skills/nature-figure/references/backend-selection.md +118 -0
  39. package/vendor/nature-skills/skills/nature-figure/references/chart-types.md +348 -0
  40. package/vendor/nature-skills/skills/nature-figure/references/common-patterns.md +371 -0
  41. package/vendor/nature-skills/skills/nature-figure/references/demos.md +58 -0
  42. package/vendor/nature-skills/skills/nature-figure/references/design-theory.md +467 -0
  43. package/vendor/nature-skills/skills/nature-figure/references/figure-contract.md +122 -0
  44. package/vendor/nature-skills/skills/nature-figure/references/figure-legend-conventions.md +90 -0
  45. package/vendor/nature-skills/skills/nature-figure/references/multipanel-evidence-architecture.md +268 -0
  46. package/vendor/nature-skills/skills/nature-figure/references/nature-2026-observations.md +124 -0
  47. package/vendor/nature-skills/skills/nature-figure/references/nature-article-requirements.md +149 -0
  48. package/vendor/nature-skills/skills/nature-figure/references/openrouter-image-generation.md +141 -0
  49. package/vendor/nature-skills/skills/nature-figure/references/qa-contract.md +344 -0
  50. package/vendor/nature-skills/skills/nature-figure/references/r-template-index.md +66 -0
  51. package/vendor/nature-skills/skills/nature-figure/references/r-workflow.md +234 -0
  52. package/vendor/nature-skills/skills/nature-figure/references/template-catalog.md +33 -0
  53. package/vendor/nature-skills/skills/nature-figure/references/tutorials.md +260 -0
  54. package/vendor/nature-skills/skills/nature-figure/requirements.txt +1 -0
  55. package/vendor/nature-skills/skills/nature-figure/scripts/audit_figure_collisions.py +742 -0
  56. package/vendor/nature-skills/skills/nature-figure/scripts/audit_panel_alignment.py +933 -0
  57. package/vendor/nature-skills/skills/nature-figure/scripts/audit_pdf_text.py +152 -0
  58. package/vendor/nature-skills/skills/nature-figure/scripts/figure_safety.py +50 -0
  59. package/vendor/nature-skills/skills/nature-figure/scripts/generate_openrouter_schematic.py +260 -0
  60. package/vendor/nature-skills/skills/nature-figure/scripts/nature_figure_backend.py +93 -0
  61. package/vendor/nature-skills/skills/nature-figure/scripts/panel_alignment.R +188 -0
  62. package/vendor/nature-skills/skills/nature-figure/scripts/plot_templates.py +604 -0
  63. package/vendor/nature-skills/skills/nature-figure/scripts/validate_figure.py +835 -0
  64. package/vendor/nature-skills/skills/nature-figure/static/core/contract.md +55 -0
  65. package/vendor/nature-skills/skills/nature-figure/static/core/stance.md +41 -0
  66. package/vendor/nature-skills/skills/nature-figure/static/fragments/backend/python.md +56 -0
  67. package/vendor/nature-skills/skills/nature-figure/static/fragments/backend/r.md +63 -0
  68. package/vendor/nature-skills/skills/nature-figure/tests/test_figure_safety.py +209 -0
  69. package/vendor/nature-skills/skills/nature-polishing/README.md +58 -0
  70. package/vendor/nature-skills/skills/nature-polishing/README_EN.md +58 -0
  71. package/vendor/nature-skills/skills/nature-polishing/SKILL.md +111 -0
  72. package/vendor/nature-skills/skills/nature-polishing/agents/openai.yaml +4 -0
  73. package/vendor/nature-skills/skills/nature-polishing/manifest.yaml +101 -0
  74. package/vendor/nature-skills/skills/nature-polishing/references/latex-layout.md +211 -0
  75. package/vendor/nature-skills/skills/nature-polishing/references/nat-comms-2025-diction.md +73 -0
  76. package/vendor/nature-skills/skills/nature-polishing/references/phrasebank-playbook.md +175 -0
  77. package/vendor/nature-skills/skills/nature-polishing/references/published-article-patterns.md +126 -0
  78. package/vendor/nature-skills/skills/nature-polishing/references/section-moves.md +252 -0
  79. package/vendor/nature-skills/skills/nature-polishing/references/style-guardrails.md +94 -0
  80. package/vendor/nature-skills/skills/nature-polishing/references/writing-strategy.md +160 -0
  81. package/vendor/nature-skills/skills/nature-polishing/static/core/failure-modes.md +29 -0
  82. package/vendor/nature-skills/skills/nature-polishing/static/core/output-format.md +21 -0
  83. package/vendor/nature-skills/skills/nature-polishing/static/core/stance.md +26 -0
  84. package/vendor/nature-skills/skills/nature-polishing/static/fragments/journal/generic.md +17 -0
  85. package/vendor/nature-skills/skills/nature-polishing/static/fragments/journal/nat-comms.md +38 -0
  86. package/vendor/nature-skills/skills/nature-polishing/static/fragments/journal/nat-mach-intell.md +102 -0
  87. package/vendor/nature-skills/skills/nature-polishing/static/fragments/journal/nature.md +19 -0
  88. package/vendor/nature-skills/skills/nature-polishing/static/fragments/language/en.md +19 -0
  89. package/vendor/nature-skills/skills/nature-polishing/static/fragments/language/zh-to-en.md +19 -0
  90. package/vendor/nature-skills/skills/nature-polishing/static/fragments/paper_type/algorithmic.md +18 -0
  91. package/vendor/nature-skills/skills/nature-polishing/static/fragments/paper_type/hypothesis.md +17 -0
  92. package/vendor/nature-skills/skills/nature-polishing/static/fragments/paper_type/methods.md +35 -0
  93. package/vendor/nature-skills/skills/nature-polishing/static/fragments/paper_type/research.md +29 -0
  94. package/vendor/nature-skills/skills/nature-polishing/static/fragments/paper_type/review.md +17 -0
  95. package/vendor/nature-skills/skills/nature-polishing/static/fragments/section/abstract.md +26 -0
  96. package/vendor/nature-skills/skills/nature-polishing/static/fragments/section/conclusion.md +15 -0
  97. package/vendor/nature-skills/skills/nature-polishing/static/fragments/section/discussion.md +48 -0
  98. package/vendor/nature-skills/skills/nature-polishing/static/fragments/section/intro.md +33 -0
  99. package/vendor/nature-skills/skills/nature-polishing/static/fragments/section/methods.md +26 -0
  100. package/vendor/nature-skills/skills/nature-polishing/static/fragments/section/results.md +61 -0
  101. package/vendor/nature-skills/skills/nature-polishing/static/fragments/section/title.md +17 -0
  102. package/vendor/nature-skills/skills/nature-ref-verifier/README.md +44 -0
  103. package/vendor/nature-skills/skills/nature-ref-verifier/README_EN.md +44 -0
  104. package/vendor/nature-skills/skills/nature-ref-verifier/SKILL.md +196 -0
  105. package/vendor/nature-skills/skills/nature-ref-verifier/agents/openai.yaml +4 -0
  106. package/vendor/nature-skills/skills/nature-ref-verifier/manifest.yaml +19 -0
  107. package/vendor/nature-skills/skills/nature-ref-verifier/references/common-patterns.md +96 -0
  108. package/vendor/nature-skills/skills/nature-shared/README.md +43 -0
  109. package/vendor/nature-skills/skills/nature-shared/README_EN.md +43 -0
  110. package/vendor/nature-skills/skills/nature-shared/SKILL.md +33 -0
  111. package/vendor/nature-skills/skills/nature-shared/agents/openai.yaml +6 -0
  112. package/vendor/nature-skills/skills/nature-shared/core/consistency-sweep.md +122 -0
  113. package/vendor/nature-skills/skills/nature-shared/core/discussion-argument-language.md +192 -0
  114. package/vendor/nature-skills/skills/nature-shared/core/ethics.md +88 -0
  115. package/vendor/nature-skills/skills/nature-shared/core/main-text-discipline.md +179 -0
  116. package/vendor/nature-skills/skills/nature-shared/core/nature-abstract.md +171 -0
  117. package/vendor/nature-skills/skills/nature-shared/core/nature-introduction.md +164 -0
  118. package/vendor/nature-skills/skills/nature-shared/core/nature-results-discussion.md +215 -0
  119. package/vendor/nature-skills/skills/nature-shared/core/paper-type-taxonomy.md +41 -0
  120. package/vendor/nature-skills/skills/nature-shared/core/reader-workflow.md +21 -0
  121. package/vendor/nature-skills/skills/nature-shared/core/research-compliance.md +192 -0
  122. package/vendor/nature-skills/skills/nature-shared/core/terminology-ledger.md +58 -0
  123. package/vendor/nature-skills/skills/nature-shared/journal-formats/nat-comms.md +139 -0
  124. package/vendor/nature-skills/skills/nature-shared/journal-formats/nature-machine-intelligence.md +431 -0
  125. package/vendor/nature-skills/skills/nature-shared/journal-formats/nature.md +313 -0
  126. package/vendor/nature-skills/skills/nature-shared/manifest.yaml +52 -0
  127. package/vendor/nature-skills/skills/nature-shared/scripts/check_consistency.py +273 -0
  128. package/vendor/nature-skills/skills/nature-shared/tests/test_check_consistency.py +63 -0
@@ -0,0 +1,118 @@
1
+ # Backend Selection
2
+
3
+ ## Contents
4
+
5
+ - [Quick decision table](#quick-decision-table)
6
+ - [Backend exclusivity rule](#backend-exclusivity-rule)
7
+ - [Default stacks](#default-stacks)
8
+ - [Mixed workflow rule](#mixed-workflow-rule)
9
+ - [Recommendation language](#recommendation-language)
10
+
11
+
12
+ Backend selection applies only to rendering or editing plotting code. Reuse a choice already established in the same task and its follow-ups; do not ask again merely because a new message omits the language. Read-only figure review and backend-independent data inspection may proceed without this choice. If the backend remains unresolved, retain the one-time Python/R question and pause only dependent plotting steps. Explicit approval requirements and backend exclusivity remain in force.
13
+
14
+ At the start of a plotting task, resolve the user's preferred backend in this order:
15
+
16
+ 1. explicit Python/R choice in the current request;
17
+ 2. clearly language-specific input file or workflow;
18
+ 3. choice already established in this task, then saved preference from `scripts/nature_figure_backend.py get`;
19
+ 4. if neither exists, ask **Python or R? I will remember this as your default.**
20
+
21
+ Pause dependent plotting steps after asking and wait for the user's answer; continue independent inspection. After the answer, save it with
22
+ `scripts/nature_figure_backend.py set python` or `scripts/nature_figure_backend.py set r`.
23
+ Do not infer Python just because the task involves simulation, NumPy-like data, or
24
+ custom layout, and do not infer R just because the task is biological or omics-adjacent.
25
+
26
+ Use the decision table only in either of these cases:
27
+
28
+ - the user explicitly asks you to recommend or choose the backend;
29
+ - the user provides an unambiguous language-specific workflow or file, such as an `.R`
30
+ script, RDS object, Python notebook, or existing Python plotting code.
31
+
32
+ ## Quick decision table
33
+
34
+ | Recommend R when | Recommend Python when |
35
+ |---|---|
36
+ | The user brings R scripts, RData/RDS, Seurat objects, DESeq2/limma outputs, survival models, or ggplot templates | The data pipeline is already Python, NumPy/Pandas arrays, PyTorch/TensorFlow outputs, image arrays, or simulation output |
37
+ | The target plot is `ggplot2`, `patchwork`, `ComplexHeatmap`, `ggtree`, `circlize`, `survminer`, `maftools`, or Seurat/UMAP-heavy | The target plot needs low-level custom layout, Matplotlib patches, image plates, subplot mosaics, or custom drawing primitives |
38
+ | The user provides an R template collection or an existing R plotting workflow | The user wants a self-contained script with matplotlib/seaborn/statsmodels and no R dependency |
39
+ | Heatmap annotations are biologically rich and multi-layered | Image panels and quantitative panels need tight pixel/axis control |
40
+
41
+ If either backend can do the job, honor the user's saved preference. Do not switch
42
+ backends for aesthetics alone. If the user explicitly switches backend, save the new
43
+ preference.
44
+
45
+ ## Backend exclusivity rule
46
+
47
+ Backend choice is not just a syntax preference; it defines the graphics engine for
48
+ the entire deliverable. Once Python or R has been selected, use that backend for
49
+ all of the following:
50
+
51
+ - plotting scripts;
52
+ - mock/simulated data examples that include plotting;
53
+ - preview PNG/TIFF files;
54
+ - SVG/PDF/TIFF exports;
55
+ - visual QA renders and final layout checks.
56
+
57
+ Do not generate a substitute preview or export with the non-selected backend. For
58
+ example, if the user selected R and `Rscript` is missing, do not use Python/matplotlib
59
+ to approximate the figure. If the user selected Python and `matplotlib` or another
60
+ required Python plotting package is missing, do not use R/ggplot2/ComplexHeatmap to
61
+ approximate the figure. Stop, report the selected-backend blocker, and provide the
62
+ selected-backend script plus install/run instructions or request permission to install
63
+ the selected-backend dependencies.
64
+
65
+ The non-selected language is allowed only for non-visual utility work, such as
66
+ listing files, checking CSV dimensions, decompressing an archive, or converting a
67
+ data file before the selected backend draws the figure. It must not import plotting
68
+ libraries, open graphics devices, save image/vector files, or decide visual layout.
69
+
70
+ ## Default stacks
71
+
72
+ ### R
73
+
74
+ - Core plotting: `ggplot2`
75
+ - Multi-panel assembly: `patchwork`
76
+ - Heatmaps: `ComplexHeatmap`, `circlize`
77
+ - Direct labels: `ggrepel`
78
+ - Survival/clinical: `survival`, `survminer`, `forestplot`, `ggplot2`
79
+ - Single-cell/omics: `Seurat`, `SingleCellExperiment`, `ComplexHeatmap`, `ggtree`
80
+ - Export: `svglite`, `grDevices::cairo_pdf`, `ragg`
81
+
82
+ ### Python
83
+
84
+ - Core plotting: `matplotlib`
85
+ - Statistical plots: `seaborn`
86
+ - Layout: `subplot_mosaic`, `GridSpec`
87
+ - Tables/model output: `pandas`, `numpy`, `statsmodels`
88
+ - Images: `matplotlib.imshow`, `skimage`, `tifffile` when needed
89
+ - Export: `fig.savefig(... .svg/.pdf/.tiff)`, `svg.fonttype='none'`,
90
+ `pdf.fonttype=42`
91
+
92
+ ## Mixed workflow rule
93
+
94
+ Use the selected plotting backend for final assembly and all visual output. A mixed
95
+ workflow is reasonable only when the non-selected language performs non-visual data
96
+ preparation and the selected backend assembles the figure. In that case:
97
+
98
+ 1. Export clean source data as CSV/TSV with stable column names.
99
+ 2. Assemble the final figure in the selected backend.
100
+ 3. Keep the source-data file next to the plotting script.
101
+ 4. Do not stitch, preview, QA-render, or export final image/vector outputs from the
102
+ non-selected backend unless the user explicitly changes the selected backend.
103
+
104
+ ## Recommendation language
105
+
106
+ Use direct language:
107
+
108
+ ```text
109
+ For this figure I recommend R because the main burden is ComplexHeatmap-style
110
+ omics annotation and patchwork assembly. I will still keep the export contract
111
+ SVG/PDF/TIFF with editable text.
112
+ ```
113
+
114
+ ```text
115
+ For this figure I recommend Python because the key panel is a custom image plate
116
+ with quantitative overlays and a subplot_mosaic layout. Matplotlib gives tighter
117
+ control over the raster and vector layers.
118
+ ```
@@ -0,0 +1,348 @@
1
+ # Chart Types — Nature Figure Making
2
+
3
+ ## Contents
4
+
5
+ - [Radar / Polar Chart](#radar-polar-chart)
6
+ - [3D Sphere / Conceptual Illustration](#3d-sphere-conceptual-illustration)
7
+ - [Scatter Plot with Color-Coded Clusters](#scatter-plot-with-color-coded-clusters)
8
+ - [Probability + Manifold Concept Panel](#probability-manifold-concept-panel)
9
+ - [Ablation Line Panel with Reference Baselines](#ablation-line-panel-with-reference-baselines)
10
+ - [Fill-Between Area Chart (Stacked trend)](#fill-between-area-chart-stacked-trend)
11
+ - [Log-Scale Bar Chart](#log-scale-bar-chart)
12
+ - [GridSpec Multi-Panel Layout](#gridspec-multi-panel-layout)
13
+ - [Scientific Notation on Y-Axis](#scientific-notation-on-y-axis)
14
+ - [Custom Spine Positioning](#custom-spine-positioning)
15
+ - [Related files](#related-files)
16
+
17
+
18
+ Specialized chart patterns beyond basic bars and trends.
19
+ Each section includes the key code pattern extracted from production scripts.
20
+
21
+ ---
22
+
23
+ ## Radar / Polar Chart
24
+
25
+ Used when comparing multiple methods across many benchmarks simultaneously.
26
+
27
+ ```python
28
+ import numpy as np
29
+ import matplotlib.pyplot as plt
30
+
31
+ def plot_radar(methods, colors, subtask_names, value_matrix,
32
+ benchmark_radii, display_range=(45, 90)):
33
+ """
34
+ Parameters
35
+ ----------
36
+ methods : list[str] — one curve per method
37
+ colors : list[str]
38
+ subtask_names : list[str] — one spoke per subtask (may contain '\\n')
39
+ value_matrix : np.ndarray — shape (n_subtasks, n_methods)
40
+ benchmark_radii: dict — {benchmark_name: [tick1, tick2, ...]} for normalization
41
+ display_range : (r_min, r_max) — polar radial display window
42
+ """
43
+ r_lo, r_hi = display_range
44
+ n_subtasks = len(subtask_names)
45
+ n_methods = len(methods)
46
+
47
+ fig = plt.figure(figsize=(12, 10))
48
+ ax = fig.add_subplot(111, projection='polar')
49
+
50
+ # Evenly spaced angles, clockwise from top
51
+ angles = np.linspace(2 * np.pi, 0, n_subtasks, endpoint=False)
52
+ angles_closed = np.append(angles, angles[0])
53
+
54
+ def _normalize(val, bench):
55
+ radii_list = benchmark_radii.get(bench, [0, 100])
56
+ span = max(radii_list) - min(radii_list)
57
+ if span <= 0:
58
+ return (r_lo + r_hi) / 2
59
+ frac = np.clip((val - min(radii_list)) / span, 0, 1)
60
+ return r_lo + (r_hi - r_lo) * frac
61
+
62
+ subtask_benchmarks = [s.split('\\n', 1)[-1] if '\\n' in s else s
63
+ for s in subtask_names]
64
+
65
+ # Draw data polygons
66
+ for m in range(n_methods):
67
+ norm_vals = np.array([_normalize(value_matrix[i, m], subtask_benchmarks[i])
68
+ for i in range(n_subtasks)])
69
+ closed = np.append(norm_vals, norm_vals[0])
70
+ ax.plot(angles_closed, closed, color=colors[m], lw=2, label=methods[m])
71
+ ax.fill(angles_closed, closed, color=colors[m], alpha=0.05)
72
+ ax.scatter(angles, norm_vals, color=colors[m], s=18, zorder=5)
73
+
74
+ # Style
75
+ ax.set_ylim(r_lo, r_hi)
76
+ ax.set_theta_zero_location('N')
77
+ for spine in ax.spines.values():
78
+ spine.set_visible(False)
79
+ ax.grid(False)
80
+
81
+ # Outer boundary ring
82
+ ax.plot(angles_closed, np.full_like(angles_closed, r_hi),
83
+ color='k', lw=0.8, zorder=4)
84
+
85
+ # Radial spokes
86
+ for a in angles:
87
+ ax.plot([a, a], [r_lo, r_hi], color='gray', lw=0.5, zorder=4)
88
+
89
+ # Benchmark-level contour polygons
90
+ max_levels = max(len(v) for v in benchmark_radii.values())
91
+ for k in range(max_levels):
92
+ disp = np.array([_normalize(benchmark_radii.get(b, [0,100])[
93
+ min(k, len(benchmark_radii.get(b,[0,100]))-1)], b)
94
+ for b in subtask_benchmarks])
95
+ ax.plot(angles_closed, np.append(disp, disp[0]),
96
+ color='k', lw=0.6, zorder=4)
97
+
98
+ ax.set_yticks([r_hi])
99
+ ax.set_yticklabels([])
100
+ ax.set_xticks(angles)
101
+ ax.set_xticklabels([])
102
+
103
+ # Spoke labels (outside outer ring)
104
+ for angle, label in zip(angles, subtask_names):
105
+ r_label = r_hi + 8 + 10 * abs(np.sin(angle))
106
+ ax.text(angle, r_label, label, fontsize=14,
107
+ ha='center', va='center',
108
+ transform=ax.transData, clip_on=False)
109
+
110
+ ax.legend(loc='upper right', bbox_to_anchor=(1.40, 0.05),
111
+ fontsize=15, frameon=False)
112
+ return fig, ax
113
+ ```
114
+
115
+ **Key settings:**
116
+ - `ax.set_theta_zero_location('N')` — top-start convention
117
+ - Remove all default spines/grid; draw custom spokes + contour polygons manually
118
+ - Normalize each spoke independently using per-benchmark tick lists
119
+ - Legend placed **outside** the plot at `bbox_to_anchor=(1.40, 0.05)`
120
+
121
+ ---
122
+
123
+ ## 3D Sphere / Conceptual Illustration
124
+
125
+ Used for geometric conceptual diagrams (e.g., embedding space visualization).
126
+
127
+ ```python
128
+ import numpy as np
129
+ import matplotlib.pyplot as plt
130
+
131
+ def draw_shaded_sphere(ax, light_dir=(-0.5, 0.5, 0.8),
132
+ resolution=512, alpha=1.0,
133
+ extent=(-1, 1, -1, 1)):
134
+ """Draw a 2D shaded disk that mimics a 3D sphere using ray-casting."""
135
+ xs = np.linspace(extent[0], extent[1], resolution)
136
+ ys = np.linspace(extent[2], extent[3], resolution)
137
+ x, y = np.meshgrid(xs, ys)
138
+ r2 = x**2 + y**2
139
+ mask = r2 <= 1.0
140
+
141
+ z = np.zeros_like(x)
142
+ z[mask] = np.sqrt(1.0 - r2[mask])
143
+
144
+ # Surface normals
145
+ nx, ny, nz = x.copy(), y.copy(), z.copy()
146
+ nrm = np.sqrt(nx**2 + ny**2 + nz**2) + 1e-6
147
+ nx, ny, nz = nx/nrm, ny/nrm, nz/nrm
148
+
149
+ # Lambertian shading
150
+ ld = np.array(light_dir, dtype=float)
151
+ ld /= np.linalg.norm(ld)
152
+ intensity = np.maximum(0, nx*ld[0] + ny*ld[1] + nz*ld[2])
153
+
154
+ img = np.ones_like(x)
155
+ img[mask] = np.clip(0.2 + 0.9 * intensity[mask], 0, 1)
156
+
157
+ ax.imshow(img, cmap='gray',
158
+ extent=list(extent),
159
+ vmin=0, vmax=1, alpha=alpha)
160
+ ax.set_axis_off()
161
+ return ax
162
+
163
+
164
+ def plot_3d_scatter_with_arrows(ax, points, grad_vectors,
165
+ point_color='#0c2458', arrow_color='#b64342'):
166
+ """3D scatter plot with gradient arrow annotations."""
167
+ from mpl_toolkits.mplot3d import proj3d
168
+ from matplotlib.patches import FancyArrowPatch
169
+
170
+ class Arrow3D(FancyArrowPatch):
171
+ def __init__(self, xs, ys, zs, *args, **kwargs):
172
+ super().__init__((0,0), (0,0), *args, **kwargs)
173
+ self._verts3d = xs, ys, zs
174
+ def do_3d_projection(self, renderer=None):
175
+ xs, ys, zs = proj3d.proj_transform(*self._verts3d, self.axes.get_proj())
176
+ self.set_positions((xs[0], ys[0]), (xs[1], ys[1]))
177
+ return np.min(zs)
178
+
179
+ ax.scatter(points[:, 0], points[:, 1], points[:, 2],
180
+ s=80, color=point_color, alpha=0.5)
181
+ for p, g in zip(points, grad_vectors):
182
+ arrow = Arrow3D([p[0], p[0]+g[0]], [p[1], p[1]+g[1]], [p[2], p[2]+g[2]],
183
+ mutation_scale=16, lw=4, arrowstyle='->',
184
+ color=arrow_color, alpha=0.8)
185
+ ax.add_artist(arrow)
186
+
187
+ # Clean 3D axes
188
+ ax.grid(False)
189
+ ax.xaxis.pane.set_visible(False)
190
+ ax.yaxis.pane.set_visible(False)
191
+ ax.zaxis.pane.set_visible(False)
192
+ ax.set_xticks([])
193
+ ax.set_yticks([])
194
+ ax.set_zticks([])
195
+ ```
196
+
197
+ ---
198
+
199
+ ## Scatter Plot with Color-Coded Clusters
200
+
201
+ ```python
202
+ def make_scatter(ax, x, y, labels_or_colors,
203
+ size=50, alpha=0.7, edgecolors='none'):
204
+ """Single or multi-cluster scatter."""
205
+ import numpy as np
206
+ ax.scatter(x, y, c=labels_or_colors, s=size,
207
+ alpha=alpha, edgecolors=edgecolors)
208
+ ax.set_axis_off() # for conceptual diagrams; remove for data plots
209
+ ```
210
+
211
+ ---
212
+
213
+ ## Probability + Manifold Concept Panel
214
+
215
+ Use when a manuscript needs a conceptual mechanism panel that links a probability
216
+ shift to a geometric or latent-space explanation. Build the pair from the
217
+ original pattern below: a 1D probability-density panel beside a contour/scatter
218
+ manifold panel.
219
+
220
+ **Pattern:**
221
+ - Left panel: draw 2-3 probability curves with transparent fills; use one vertical
222
+ reference line and a single double-headed arrow to define the conceptual gap.
223
+ - Right panel: sample points around smooth center curves, show low-alpha clouds,
224
+ contour density bands, and a small number of highlighted trajectory markers.
225
+ - Keep axes only where they carry meaning. The manifold panel can be axis-free if
226
+ labels and arrows carry the explanation.
227
+ - All math labels and manifold names must map to real manuscript concepts. Do not
228
+ reuse demo labels such as `VIG` or `DPO` unless they are the user's actual terms.
229
+
230
+ ```python
231
+ fig, (ax_prob, ax_manifold) = plt.subplots(1, 2, figsize=(24, 6))
232
+ plot_distribution(ax_prob) # probability curves + conceptual gap arrow
233
+ plot_manifold(ax_manifold) # density contours + trajectory markers
234
+ fig.tight_layout(pad=0.5)
235
+ ```
236
+
237
+ ---
238
+
239
+ ## Ablation Line Panel with Reference Baselines
240
+
241
+ Use when an ablation compares data fraction, hyperparameters, or coupled metrics
242
+ across a small set of methods. A robust original layout uses three horizontal
243
+ panels: data fraction, one hyperparameter sweep, and a dual-axis coupled metric
244
+ sweep.
245
+
246
+ **Pattern:**
247
+ - Use a dashed horizontal baseline for the simple/reference model.
248
+ - Use a dotted horizontal line for a meaningful operating point, e.g. "ours at
249
+ 25% data", only when that comparison is called out in the text.
250
+ - Use `twinx()` sparingly. If two y-axes are needed, color each y label to match
251
+ the corresponding series and keep tick ranges narrow.
252
+ - Put legends inside low-density regions of each panel; avoid one giant legend
253
+ if panel-specific series differ.
254
+
255
+ ```python
256
+ fig, axes = plt.subplots(1, 3, figsize=(27, 6),
257
+ gridspec_kw={"width_ratios": [1.1, 1, 1]})
258
+ axes[0].plot(x, baseline, color="black", alpha=0.3, lw=4, ls="--")
259
+ axes[0].plot(x, reference, color=hero_color, lw=3, ls=":")
260
+ ax2 = axes[2].twinx()
261
+ ```
262
+
263
+ ---
264
+
265
+ ## Fill-Between Area Chart (Stacked trend)
266
+
267
+ Used for cumulative publication counts, stacked contributions, etc.
268
+
269
+ ```python
270
+ # Filled area (stacked) with hatch for print safety
271
+ ax.fill_between(x, 0, y_bottom,
272
+ color='#ffa8a6', label='Category A')
273
+ ax.fill_between(x, 0, y_top,
274
+ color='#9BC8FA',
275
+ hatch='///', # hatch for grayscale print
276
+ edgecolor='black',
277
+ label='Category B')
278
+ # Erase border artifacts
279
+ ax.fill_between(x, 0, y_top,
280
+ facecolor='none',
281
+ edgecolor='white',
282
+ linewidth=2)
283
+
284
+ # Overlay the trend line for exact values
285
+ ax.plot(x, y_top, lw=3, color='#13457E')
286
+ ax.plot(x, y_bottom, lw=3, color='#850c0a')
287
+ ```
288
+
289
+ ---
290
+
291
+ ## Log-Scale Bar Chart
292
+
293
+ ```python
294
+ ax.set_yscale('log')
295
+ ymin, ymax = ax.get_ylim()
296
+ ax.set_ylim(ymin, ymax * 20) # expand top for annotations
297
+
298
+ # Annotate values above bars
299
+ for i, val in enumerate(values):
300
+ ax.text(i, val * 1.1, f'{val:.3f}',
301
+ ha='center', va='bottom', fontsize=16)
302
+ ```
303
+
304
+ ---
305
+
306
+ ## GridSpec Multi-Panel Layout
307
+
308
+ ```python
309
+ from matplotlib import gridspec
310
+
311
+ # 2-row, 4-column layout
312
+ fig = plt.figure(figsize=(36, 12))
313
+ gs = gridspec.GridSpec(2, 4)
314
+
315
+ ax_top_left = fig.add_subplot(gs[0, 0])
316
+ ax_top_right = fig.add_subplot(gs[0, 1:3]) # span columns 1-2
317
+ ax_legend = fig.add_subplot(gs[0, 3]) # legend panel
318
+ ax_bottom = fig.add_subplot(gs[1, :]) # full-width bottom
319
+ ```
320
+
321
+ ---
322
+
323
+ ## Scientific Notation on Y-Axis
324
+
325
+ ```python
326
+ ax.ticklabel_format(axis='y', style='sci', scilimits=(0, 0))
327
+ ```
328
+
329
+ ---
330
+
331
+ ## Custom Spine Positioning
332
+
333
+ ```python
334
+ # Move bottom spine to y=0 (for negative values)
335
+ ax.spines['bottom'].set_position(('data', 0))
336
+ ax.xaxis.set_ticks_position('bottom')
337
+ ax.spines['left'].set_bounds(0, y_max)
338
+ ```
339
+
340
+ ---
341
+
342
+ ## Related files
343
+
344
+ - [SKILL.md](../SKILL.md) — When to use this skill
345
+ - [api.md](api.md) — PALETTE and core helper signatures
346
+ - [common-patterns.md](common-patterns.md) — Bar, trend, and layout patterns
347
+ - [design-theory.md](design-theory.md) — Rationale and color theory
348
+ - [tutorials.md](tutorials.md) — Full end-to-end walkthroughs