Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
f05b374
Add validated D2D MOBO workflow through Step 2C
Colin-Qi Jul 27, 2026
600ef60
Fix the GP, add the campaign path, and wire the workbook interface
Jul 29, 2026
0ab1196
Add a DTLZ2 end-to-end acceptance test for the campaign path
Jul 29, 2026
33f101f
Clean the branch: remove the retired Step 1/2A/2B/2C audit apparatus
Jul 29, 2026
19591cc
Add docs/HANDOFF.md for session continuity
Jul 29, 2026
69cc1fc
Compute the objectives from measurements, cross-check the workbook
Jul 30, 2026
6fa1767
Require an explicit hypervolume reference point
Jul 30, 2026
bc1e9f0
Add the batch review artifact and the one-button launcher
Jul 30, 2026
e5ef268
Update the docs: correct three stale claims, record the audit, drop t…
Jul 30, 2026
f528f84
Let the collapse guard tell a dead GP from a working mean function
Jul 30, 2026
9c96b0d
Isolate the launcher UI tests from remembered settings
Jul 30, 2026
f60f23a
Close the launcher's stale-reply race, and keep the unfiltered fit wa…
Jul 30, 2026
ced317e
Wire replicate variance into train_Yvar, ahead of the data
Jul 30, 2026
9d888e4
Add the parameter sweep and the new-data intake command
Jul 30, 2026
29fb7da
Close the 0.089 gap, reconcile the instruments, verify radius binds
Jul 30, 2026
b0251c4
Name the third floor, and bring the docs up to date
Jul 30, 2026
2e392fb
Say what the demo notebook builds, and unstick the constraints header
Jul 30, 2026
f191730
Simulate the campaign loop against a frozen GP oracle
Jul 31, 2026
4b76670
Encode the R1 baseline into model space before transforming it
Jul 31, 2026
4a7a298
Withdraw and reissue R1, and correct what the defect had written down
Jul 31, 2026
41081a9
Credit the current authors, and finish the sweep for artifact-born cl…
Jul 31, 2026
c249e82
Attribute the campaign's objectives with SHAP, and record the group's…
Aug 4, 2026
37c0d75
Sweep the boxplots over beta x radius, replicated across three starti…
Aug 4, 2026
4ea92b5
Keep Tk out of collection, and stop capturing at the fd level
Aug 17, 2026
91ec960
Open the second campaign: new score contract, and the first real cons…
Aug 17, 2026
0a6d7d8
Record the second campaign, and what its data does and does not support
Aug 17, 2026
17a9eac
Render the round's figures when it is proposed, and settle thickness …
Aug 18, 2026
e283a4a
Point the launcher at the campaign that is actually running
Aug 18, 2026
7653cbf
Simulate the new campaign at the ratified cell, and stop reporting th…
Aug 18, 2026
2d1aeb7
Audit the tree for the first push since July, and say which campaign …
Aug 21, 2026
4162530
Open the live campaign on contract v4, and freeze the two moving scores
Sep 2, 2026
9ce8828
Recompute the thickness floor, and call the earlier contracts what th…
Sep 2, 2026
4d113b9
Add the instruments that measured what R0 can and cannot teach
Sep 6, 2026
8f8741e
Withdraw the thickness prior, lower beta to 4, and stop calling -0.14…
Sep 6, 2026
f2fc992
Stop encoding this group's score formulas; the score value is the int…
Sep 6, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file removed .DS_Store
Binary file not shown.
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,15 @@ ENV/
env/
mobo-env/

# Private campaign inputs (never commit experimental workbooks/data)
local_inputs/
local_outputs/
# The launcher writes round reports BESIDE the workbook, so they normally
# land under local_inputs/ -- but only if the workbook is kept there. This
# catches them wherever a user actually puts it.
*_reports/
*_PRIVATE_EVIDENCE_DO_NOT_SHARE.zip

# Distribution / packaging
.Python
build/
Expand Down Expand Up @@ -55,4 +64,5 @@ Desktop.ini
.tox/
.nox/
.pytest_cache/
.mypy_cache/
htmlcov/
7 changes: 0 additions & 7 deletions .vscode/settings.json

This file was deleted.

486 changes: 253 additions & 233 deletions README.md

Large diffs are not rendered by default.

369 changes: 369 additions & 0 deletions configs/campaign_d2d_perovskite.yaml

Large diffs are not rendered by default.

167 changes: 167 additions & 0 deletions configs/campaign_d2d_perovskite_extended_c1c2.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
# DIAGNOSTIC ONLY. Not a campaign contract, and nothing here proposes films.
#
# The workbook is local_inputs/Extended Summary Table C1C2.xlsx (gitignored),
# supplied 2026-09-03 as "summary table with extended dataset from Campaign 1 & 2".
# It holds 45 rows, and the 45 rows are 15 RECIPES MEASURED THREE TIMES: samples
# 1-15, 16-30 and 31-45 carry identical inputs, recipe for recipe. That makes this
# the first dataset in the project that can separate "the recipe did it" from "the
# measurement did it", which is the whole reason for reading it.
#
# WHY IT IS NOT A CONTRACT.
# * The optoelectronic definition MOVED. AK is now `=R2*X2*AA2`, a raw product
# of clamped Voc (V) x floor-corrected photoconductance (S) x capped
# photosensitivity -- not the normalised mean the v4 contract fingerprints.
# Its values span 6.1e-11 to 6.2e-6, five orders of magnitude, and are not in
# [0, 1]. The v4 anchors would map every film to utility 0.
# * Samples 17 and 32 are `speed_2 = 0, time_2 = 60`, which breaks
# second_stage_all_or_nothing. Sample 2 is the same recipe with the group's
# correction (time_2 = 0) applied. The correction reached one of the three
# replicates, not all three.
# * The scores are used AS STORED, all three of them, per the request. That
# includes thickness, which every real contract trains on in nanometres.
#
# Read the intake report before quoting any number out of this file.
campaign:
name: D2D_FA0.9Cs0.1PbI3_extended_c1c2_diagnostic
status: diagnostic
schema_version: d2d-campaign-v4
workbook_profile: d2d_summary_final_v4
source_sheet: R0

# UNCHANGED from v4. All 45 rows land on these grids.
inputs:
- {name: speed_1, unit: rpm, start: 1000, stop: 6000, step: 500}
- {name: time_1, unit: s, start: 5, stop: 50, step: 5}
- {name: speed_2, unit: rpm, start: 0, stop: 5000, step: 500}
- {name: time_2, unit: s, start: 0, stop: 60, step: 5}
- {name: precur_conc, unit: M, start: 1.00, stop: 2.00, step: 0.05}
- {name: precur_vol, unit: uL, start: 40, stop: 200, step: 10}
- {name: anneal_temp, unit: C, start: 100, stop: 185, step: 5}
- {name: anneal_time, unit: min, start: 10, stop: 60, step: 5}
- {name: anti_vol, unit: uL, start: 100, stop: 200, step: 5}
- {name: anti_time, unit: s, start: 9, stop: 25, step: 1}

objectives:
contract_version: d2d-objectives-v5-extended-diagnostic
scaling_mode: fixed_affine
specs:
- name: uniformity
model_source_column: "Uniformity score (Avg (Coverage + (1-Uniformity) + Phase purity))"
transform: affine
goal: maximize
measurement:
recipe: stored
inputs:
- {column: "Uniformity score (Avg (Coverage + (1-Uniformity) + Phase purity))"}
formula_fingerprint:
column: "Uniformity score (Avg (Coverage + (1-Uniformity) + Phase purity))"
formula: "=(L2+O2+P2)/3"
lower_anchor: 0.0
upper_anchor: 1.0
signal_status: exploration_only

- name: optoelectronic
model_source_column: "Optoelectronic score (Normalized (Voc + (0.75*Photoconductance + 0.25*Photosensitivity))/2"
transform: affine
goal: maximize
measurement:
recipe: stored
inputs:
- {column: "Optoelectronic score (Normalized (Voc + (0.75*Photoconductance + 0.25*Photosensitivity))/2"}
# DELIBERATELY the NEW formula. Fingerprinting the v4 one here would make
# every run of this diagnostic shout about a change the group already made.
# The v4 config still fingerprints `=(S2+((0.75*Y2)+(0.25*AB2)))/2`, so the
# change stays audible where it matters -- on the campaign contract.
formula_fingerprint:
column: "Optoelectronic score (Normalized (Voc + (0.75*Photoconductance + 0.25*Photosensitivity))/2"
formula: "=R2*X2*AA2"
agreement_check:
raw: "Photoconductance (Max - based on raw slopes)"
normalized: "Normalized photoconductance (test)"
min_spearman: 0.0
# NOT [0, 1]. A raw triple product is not a normalised score, and anchoring it
# at [0, 1] would put all 45 films on top of each other at utility ~1e-6.
# These anchors bracket the observed span (6.06e-11 .. 6.22e-06) and are
# declared, not derived per round -- but they are declared FROM THIS DATA,
# which is exactly what a campaign contract must never do. Diagnostic only.
lower_anchor: 0.0
upper_anchor: 7.0e-06
signal_status: exploration_only

- name: thickness
# AS STORED, per the request: this is AL = AI = EXP(-(((AH-650)/250)^2)), the
# workbook's normalised thickness, NOT the nanometres every contract trains
# on. The Gaussian target is therefore already applied before the GP sees it,
# which folds a hard non-monotone transform into the response.
model_source_column: "Thickness score (normalized of avg)"
transform: affine
goal: maximize
measurement:
recipe: stored
inputs:
- {column: "Thickness score (normalized of avg)"}
formula_fingerprint:
column: "Thickness score (normalized of avg)"
formula: "=AI2"
lower_anchor: 0.0
upper_anchor: 1.0
signal_status: learnable

reference_point_utility: [-0.01, -0.01, -0.01]

rounds:
r1:
method: ucb_hvi
batch_size: 5
replicates_per_condition: 3
beta: 36.0
candidate_pool_size: 32768
posterior_samples: 256
moment_method: monte_carlo
r2:
method: qlognehvi
batch_size: 3
replicates_per_condition: 3
candidate_pool_size: 32768
mc_samples: 128
sequential_pending: true

# Kept so the samples 17 / 32 violation is REPORTED rather than absorbed.
constraints:
- zero_coupled: [speed_2, time_2]
name: second_stage_all_or_nothing
- sum_upper_strict: {lhs: anti_time, rhs: [time_1, time_2]}
name: antisolvent_lands_while_spinning
- nonzero_minimum: {column: time_2, minimum: 10}
name: second_stage_runs_at_least_10s

review:
probes: []
notes:
- >
45 rows, 15 recipes, three replicates each. Ordinary leave-one-out on this
sheet LEAKS: hold out row 4 and rows 19 and 34 carry the same inputs, so the
GP interpolates its own replicate and the R2 measures reproducibility rather
than prediction. Leave-one-RECIPE-out (all three rows together) is the
honest test. Both are reported; do not quote the row-wise one alone.

local_penalization:
distance_metric: normalized_euclidean
dimension_weights: null
radius: 0.35
min_batch_distance: 0.15
min_observed_distance: 0.0

model:
variant: dim_scaled_prior
observation_noise: fit_from_marginal_likelihood
replicate_variance:
sanity_floor:
thickness: 0.003007
rows_without_replicates: 1

reproducibility:
seed: 73
record_git_commit: true
record_environment_versions: true
record_resolved_config_hash: true
Loading