tests/test.sh exit code (0 → resolved); the classification below is post-hoc and cannot change it.cat /app/train_and_predict.py
#!/usr/bin/env python3
"""Starter baseline for the Airfoil Self-Noise surrogate task.
This deliberately modest Ridge model is useful as a working end-to-end
baseline, but it is not strong enough to clear the hidden grouped split.
"""
from pathlib import Path
import numpy as np
import pandas as pd
from sklearn.base import BaseEstimator, TransformerMixin
from sklearn.linear_model import Ridge
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
DATA_DIR = Path("/app/data")
OUTPUT_DIR = Path("/app/output")
TARGET = "scaled_sound_pressure_level"
PREDICTION = "predicted_scaled_sound_pressure_level"
FEATURES = [
"frequency",
"attack_angle",
"chord_length",
"free_stream_velocity",
"suction_side_displacement_thickness",
]
class AirfoilFeatures(BaseEstimator, TransformerMixin):
def fit(self, X, y=None):
return self
def transform(self, X):
frame = pd.DataFrame(X, columns=FEATURES if not isinstance(X, pd.DataFrame) else X.columns)
frequency = frame["frequency"].astype(float)
angle = frame["attack_angle"].astype(float)
chord = frame["chord_length"].astype(float)
velocity = frame["free_stream_velocity"].astype(float)
thickness = frame["suction_side_displacement_thickness"].astype(float)
log_frequency = np.log10(frequency)
log_thickness = np.log10(thickness)
out = pd.DataFrame(index=frame.index)
out["frequency"] = frequency
out["attack_angle"] = angle
out["chord_length"] = chord
out["free_stream_velocity"] = velocity
out["suction_side_displacement_thickness"] = thickness
out["log_frequency"] = log_frequency
out["log_thickness"] = log_thickness
out["freq_velocity"] = log_frequency * velocity
out["freq_angle"] = log_frequency * angle
out["angle_velocity"] = angle * velocity
out["thickness_angle"] = log_thickness * angle
out["chord_velocity"] = chord * velocity
return out.to_numpy(dtype=float)
def load_labeled_data() -> pd.DataFrame:
parts = [pd.read_csv(DATA_DIR / "train.csv")]
validation_path = DATA_DIR / "validation.csv"
if validation_path.exists():
parts.append(pd.read_csv(validation_path))
return pd.concat(parts, ignore_index=True)
def load_evaluation_features() -> pd.DataFrame:
test_path = DATA_DIR / "test_features.csv"
if test_path.exists():
return pd.read_csv(test_path)
validation = pd.read_csv(DATA_DIR / "validation.csv")
return validation.drop(columns=[TARGET])
def main() -> None:
labeled = load_labeled_data()
evaluation = load_evaluation_features()
model = make_pipeline(
AirfoilFeatures(),
StandardScaler(),
Ridge(alpha=10.0),
)
model.fit(labeled[FEATURES], labeled[TARGET])
predictions = model.predict(evaluation[FEATURES])
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
pd.DataFrame(
{
"sample_id": evaluation["sample_id"].astype(str),
PREDICTION: predictions,
}
).to_csv(OUTPUT_DIR / "predictions.csv", index=False)
if __name__ == "__main__":
main()cd /app && echo "=== Directory structure ===" && ls -la /app && echo "" && echo "=== Data dir ===" && ls -la /app/data && echo "" && echo "=== train.csv head ===" && head -5 /app/data/train.csv && echo "" && echo "=== validation.csv head ===" && head -5 /app/data/validation.csv && echo "" && echo "=== test_features.csv head ===" && head -5 /app/data/test_features.csv
=== Directory structure === total 20 drwxrws--- 4 h2tagent h2tagent 4096 Jun 21 05:20 . drwxr-xr-x 26 root root 4096 Jun 21 05:25 .. drwxrws--- 2 h2tagent h2tagent 4096 Jun 21 05:20 data drwxrws--- 2 h2tagent h2tagent 4096 Jun 21 05:20 output -rwxrwx--- 1 h2tagent h2tagent 3211 Jun 21 05:18 train_and_predict.py === Data dir === total 128 drwxrws--- 2 h2tagent h2tagent 4096 Jun 21 05:20 . drwxrws--- 4 h2tagent h2tagent 4096 Jun 21 05:20 .. -rw-rw---- 1 h2tagent h2tagent 3595 Jun 21 05:18 test_features.csv -rw-rw---- 1 h2tagent h2tagent 97417 Jun 21 05:18 train.csv -rw-rw---- 1 h2tagent h2tagent 20351 Jun 21 05:18 validation.csv === train.csv head === sample_id,frequency,attack_angle,chord_length,free_stream_velocity,suction_side_displacement_thickness,scaled_sound_pressure_level af_train_0000,5003.72368377,0.034997843085,0.101695197262,71.2730786718,0.00120791730525,131.375168508 af_train_0001,1997.22544774,15.6337827466,0.101571096822,71.3314610261,0.0436885053477,119.435701427 af_train_0002,1248.10938712,9.47742633424,0.0253695328899,31.6576914656,0.00461706652486,131.137445156 af_train_0003,200.228488983,15.5929878809,0.101720824729,39.5544682279,0.0527780201324,124.741187675 === validation.csv head === sample_id,frequency,attack_angle,chord_length,free_stream_velocity,suction_side_displacement_thickness,scaled_sound_pressure_level af_val_0000,1998.53240303,12.3252028109,0.101492415739,39.5853154533,0.040726689388,115.836432573 af_val_0001,629.718430551,0.0186262062548,0.152498614376,39.5692331074,0.00193529208188,124.213347611 af_val_0002,12482.9663422,0.0105284256024,0.0253720007765,71.3111171362,0.000400617998197,133.621156597 af_val_0003,1600.32755515,0.0338230474173,0.0253792825467,39.6448710656,0.000429527467403,123.534534482 === test_features.csv head === sample_id,frequency,attack_angle,chord_length,free_stream_velocity,suction_side_displacement_thickness af_val_0000,1998.53240303,12.3252028109,0.101492415739,39.5853154533,0.040726689388 af_val_0001,629.718430551,0.0186262062548,0.152498614376,39.5692331074,0.00193529208188 af_val_0002,12482.9663422,0.0105284256024,0.0253720007765,71.3111171362,0.000400617998197 af_val_0003,1600.32755515,0.0338230474173,0.0253792825467,39.6448710656,0.000429527467403
cd /app && python3 -c "
import pandas as pd
import numpy as np
train = pd.read_csv('data/train.csv')
val = pd.read_csv('data/validation.csv')
test = pd.read_csv('data/test_features.csv')
print('train shape', train.shape)
print('val shape', val.shape)
print('test shape', test.shape)
print()
print('=== train describe ===')
print(train.describe().T)
print()
print('=== val describe ===')
print(val.describe().T)
"train shape (956, 7)
val shape (201, 7)
test shape (40, 6)
=== train describe ===
count ... max
frequency 956.0 ... 20025.582040
attack_angle 956.0 ... 22.234770
chord_length 956.0 ... 0.305164
free_stream_velocity 956.0 ... 71.406950
suction_side_displacement_thickness 956.0 ... 0.058556
scaled_sound_pressure_level 956.0 ... 140.289040
[6 rows x 8 columns]
=== val describe ===
count ... max
frequency 201.0 ... 20017.248537
attack_angle 201.0 ... 17.434768
chord_length 201.0 ... 0.305164
free_stream_velocity 201.0 ... 71.406950
suction_side_displacement_thickness 201.0 ... 0.040928
scaled_sound_pressure_level 201.0 ... 138.661407
[6 rows x 8 columns]cd /app && python3 -c "
import pandas as pd
import numpy as np
pd.set_option('display.width', 200)
pd.set_option('display.max_columns', 20)
train = pd.read_csv('data/train.csv')
val = pd.read_csv('data/validation.csv')
test = pd.read_csv('data/test_features.csv')
for name, df in [('train', train), ('val', val), ('test', test)]:
print('===', name, '===')
print(df.describe().T[['mean','std','min','max']])
print()
"=== train ===
mean std min max
frequency 2688.724820 2909.340380 199.660258 20025.582040
attack_angle 7.381738 5.859864 -0.035000 22.234770
chord_length 0.133004 0.092255 0.025370 0.305164
free_stream_velocity 51.207469 15.659942 31.652457 71.406950
suction_side_displacement_thickness 0.012331 0.013939 0.000411 0.058556
scaled_sound_pressure_level 125.402453 6.704146 104.935002 140.289040
=== val ===
mean std min max
frequency 3328.983497 3672.955796 199.661620 20017.248537
attack_angle 5.906241 5.519653 -0.034726 17.434768
chord_length 0.107417 0.085874 0.025370 0.305164
free_stream_velocity 46.757698 13.846173 31.652508 71.406950
suction_side_displacement_thickness 0.011145 0.014028 0.000400 0.040928
scaled_sound_pressure_level 126.483017 6.979769 109.788714 138.661407
=== test ===
mean std min max
frequency 2867.873122 2698.854811 314.994039 12482.966342
attack_angle 4.689172 5.510792 -0.034726 17.418442
chord_length 0.089545 0.079918 0.025370 0.304951
free_stream_velocity 48.139533 12.865910 31.686642 71.387026
suction_side_displacement_thickness 0.007538 0.012234 0.000401 0.040833cd /app && python3 -c "
import pandas as pd
import numpy as np
train = pd.read_csv('data/train.csv')
val = pd.read_csv('data/validation.csv')
cond = ['attack_angle','chord_length','free_stream_velocity']
# Check if displacement thickness is constant within a condition group
g = train.groupby(cond)['suction_side_displacement_thickness'].nunique()
print('Train: unique thickness per condition group - max:', g.max(), 'min:', g.min())
print('Num train condition groups:', train.groupby(cond).ngroups)
print('Num val condition groups:', val.groupby(cond).ngroups)
# rows per group
gs = train.groupby(cond).size()
print('rows per group train: min', gs.min(), 'max', gs.max(), 'mean', gs.mean())
print()
# Overlap of condition groups between train and val
train_conds = set(map(tuple, train[cond].round(6).values))
val_conds = set(map(tuple, val[cond].round(6).values))
print('train cond groups:', len(train_conds))
print('val cond groups:', len(val_conds))
print('overlap:', len(train_conds & val_conds))
"Train: unique thickness per condition group - max: 1 min: 1 Num train condition groups: 956 Num val condition groups: 201 rows per group train: min 1 max 1 mean 1.0 train cond groups: 956 val cond groups: 201 overlap: 0
cd /app && python3 -c "
import pandas as pd
import numpy as np
train = pd.read_csv('data/train.csv')
val = pd.read_csv('data/validation.csv')
test = pd.read_csv('data/test_features.csv')
allf = pd.concat([train,val,test], ignore_index=True)
# velocity clusters
for col in ['free_stream_velocity','chord_length']:
v = np.sort(allf[col].values)
print('===', col, '===')
print(np.round(np.unique(np.round(v,1) if col=='free_stream_velocity' else np.round(v,4)), 4))
print()
# attack angle - original grid
va = np.sort(train['attack_angle'].values)
print('attack_angle sample sorted (first 40):', np.round(va[:40],2))
"=== free_stream_velocity === [31.7 39.5 39.6 39.7 55.4 55.5 55.6 71.2 71.3 71.4] === chord_length === [0.0254 0.0507 0.0508 0.0509 0.1015 0.1016 0.1017 0.1522 0.1523 0.1524 0.1525 0.1526 0.2283 0.2284 0.2285 0.2286 0.2287 0.2288 0.2289 0.3044 0.3045 0.3046 0.3047 0.3048 0.3049 0.305 0.3051 0.3052] attack_angle sample sorted (first 40): [-0.03 -0.03 -0.03 -0.03 -0.03 -0.03 -0.03 -0.03 -0.03 -0.03 -0.03 -0.03 -0.03 -0.03 -0.03 -0.03 -0.03 -0.03 -0.03 -0.03 -0.03 -0.03 -0.03 -0.03 -0.03 -0.03 -0.03 -0.03 -0.03 -0.03 -0.03 -0.03 -0.03 -0.03 -0.03 -0.03 -0.03 -0.03 -0.03 -0.03]
cd /app && python3 -c "
import sklearn; print('sklearn', sklearn.__version__)
import numpy, scipy, pandas; print('numpy', numpy.__version__, 'scipy', scipy.__version__, 'pandas', pandas.__version__)
" 2>&1; echo "---xgboost---"; python3 -c "import xgboost; print('xgboost', xgboost.__version__)" 2>&1 | head -1; echo "---lightgbm---"; python3 -c "import lightgbm; print('lightgbm', lightgbm.__version__)" 2>&1 | head -1; echo "---catboost---"; python3 -c "import catboost; print('catboost', catboost.__version__)" 2>&1 | head -1; echo "---torch---"; python3 -c "import torch; print('torch', torch.__version__)" 2>&1 | head -1sklearn 1.4.2 numpy 1.26.4 scipy 1.17.1 pandas 2.2.2 ---xgboost--- Traceback (most recent call last): ---lightgbm--- Traceback (most recent call last): ---catboost--- Traceback (most recent call last): ---torch--- Traceback (most recent call last):
cd /app && timeout 30 pip install xgboost 2>&1 | tail -3; echo "exit: done"
WARNING: Retrying (Retry(total=0, connect=None, read=None, redirect=None, status=None)) after connection broken by 'NewConnectionError('<pip._vendor.urllib3.connection.HTTPSConnection object at 0x7ec3eefd7e60>: Failed to establish a new connection: [Errno -3] Temporary failure in name resolution')': /simple/xgboost/
ERROR: Could not find a version that satisfies the requirement xgboost (from versions: none)
ERROR: No matching distribution found for xgboost
exit: donecd /app && python3 -c "
import pandas as pd, numpy as np
train = pd.read_csv('data/train.csv'); val = pd.read_csv('data/validation.csv'); test = pd.read_csv('data/test_features.csv')
allf = pd.concat([train,val,test], ignore_index=True)
# Original UCI grids
angles_uci = np.array([0,1.5,3.0,4.0,5.3,6.7,7.3,8.9,9.5,9.9,11.2,12.3,12.6,15.4,15.6,17.4,19.7,22.2])
vel_uci = np.array([31.7,39.6,55.5,71.3])
chord_uci = np.array([0.0254,0.0508,0.1016,0.1524,0.2286,0.3048])
for col, grid in [('attack_angle',angles_uci),('free_stream_velocity',vel_uci),('chord_length',chord_uci)]:
v = allf[col].values
nearest = grid[np.abs(v[:,None]-grid[None,:]).argmin(1)]
dev = v - nearest
print(col, 'max abs dev from nearest UCI grid:', np.round(np.abs(dev).max(),5), 'mean abs dev:', np.round(np.abs(dev).mean(),5))
"attack_angle max abs dev from nearest UCI grid: 0.535 mean abs dev: 0.09703 free_stream_velocity max abs dev from nearest UCI grid: 0.10695 mean abs dev: 0.04789 chord_length max abs dev from nearest UCI grid: 0.00036 mean abs dev: 0.0001
cd /app && python3 -c "
import pandas as pd, numpy as np
train = pd.read_csv('data/train.csv'); val = pd.read_csv('data/validation.csv'); test = pd.read_csv('data/test_features.csv')
allf = pd.concat([train,val,test], ignore_index=True)
v = np.sort(allf['attack_angle'].values)
# find gaps > 0.3 to separate clusters
gaps = np.diff(v)
breaks = np.where(gaps>0.3)[0]
clusters = np.split(v, breaks+1)
print('num angle clusters:', len(clusters))
for c in clusters:
print(f' center={c.mean():.3f} n={len(c)} range=[{c.min():.3f},{c.max():.3f}] width={c.max()-c.min():.3f}')
"num angle clusters: 19 center=-0.002 n=246 range=[-0.035,0.035] width=0.070 center=1.498 n=31 range=[1.465,1.535] width=0.070 center=2.001 n=56 range=[1.965,2.035] width=0.070 center=3.192 n=82 range=[2.965,3.335] width=0.370 center=4.042 n=100 range=[3.965,4.235] width=0.270 center=4.803 n=44 range=[4.765,4.835] width=0.070 center=5.352 n=28 range=[5.268,5.435] width=0.167 center=6.700 n=36 range=[6.665,6.735] width=0.070 center=7.260 n=81 range=[7.165,7.335] width=0.170 center=8.399 n=36 range=[8.365,8.435] width=0.070 center=8.901 n=32 range=[8.865,8.935] width=0.070 center=9.498 n=40 range=[9.465,9.535] width=0.070 center=9.900 n=49 range=[9.865,9.935] width=0.070 center=11.203 n=28 range=[11.165,11.235] width=0.070 center=12.474 n=134 range=[12.265,12.735] width=0.470 center=15.476 n=79 range=[15.365,15.635] width=0.270 center=17.400 n=66 range=[17.365,17.435] width=0.070 center=19.694 n=14 range=[19.665,19.728] width=0.063 center=22.204 n=15 range=[22.168,22.235] width=0.067
cd /app && cat > /tmp/harness.py << 'EOF'
import pandas as pd, numpy as np
from sklearn.model_selection import GroupKFold
FEATURES = ["frequency","attack_angle","chord_length","free_stream_velocity","suction_side_displacement_thickness"]
TARGET = "scaled_sound_pressure_level"
def load():
tr = pd.read_csv('/app/data/train.csv')
va = pd.read_csv('/app/data/validation.csv')
return pd.concat([tr,va], ignore_index=True)
VEL = np.array([31.7,39.6,55.5,71.3])
CHORD = np.array([0.0254,0.0508,0.1016,0.1524,0.2286,0.3048])
def snap(arr, grid):
return grid[np.abs(arr[:,None]-grid[None,:]).argmin(1)]
def cond_key(df):
vel = snap(df['free_stream_velocity'].values, VEL)
ch = snap(df['chord_length'].values, CHORD)
# cluster angle via gaps
a = df['attack_angle'].values
order = np.argsort(a); asort = a[order]
gaps = np.diff(asort); breaks = np.where(gaps>0.25)[0]
lab = np.zeros(len(a),dtype=int); cur=0
labels_sorted = np.zeros(len(a),dtype=int)
c=0
for i in range(len(asort)):
if i>0 and asort[i]-asort[i-1]>0.25: c+=1
labels_sorted[i]=c
angle_lab = np.empty(len(a),dtype=int); angle_lab[order]=labels_sorted
keys = [f"{v:.1f}_{cc:.4f}_{al}" for v,cc,al in zip(vel,ch,angle_lab)]
return np.array(keys)
def evaluate(df, pred):
err = pred - df[TARGET].values
rmse = np.sqrt(np.mean(err**2))
mae = np.mean(np.abs(err))
keys = cond_key(df)
cond_rmse = []
for k in np.unique(keys):
m = keys==k
cond_rmse.append(np.sqrt(np.mean(err[m]**2)))
cond_rmse = np.array(cond_rmse)
return dict(rmse=rmse, mae=mae,
p90=np.percentile(cond_rmse,90),
p95=np.percentile(cond_rmse,95),
mx=cond_rmse.max(),
ngroups=len(cond_rmse))
def group_cv(make_model, df, n_splits=5, seed=0, feat_fn=None):
keys = cond_key(df)
uniq = np.unique(keys)
gkf = GroupKFold(n_splits=n_splits)
oof = np.zeros(len(df))
X = df[FEATURES]; y = df[TARGET].values
for tri, tei in gkf.split(X, y, groups=keys):
m = make_model()
Xtr = feat_fn(X.iloc[tri]) if feat_fn else X.iloc[tri]
Xte = feat_fn(X.iloc[tei]) if feat_fn else X.iloc[tei]
m.fit(Xtr, y[tri])
oof[tei] = m.predict(Xte)
return evaluate(df, oof)
if __name__=='__main__':
df = load()
print('n conditions in labeled data:', len(np.unique(cond_key(df))), 'rows:', len(df))
EOF
python3 /tmp/harness.pyn conditions in labeled data: 82 rows: 1157
cd /app && cat > /tmp/bench1.py << 'EOF'
import sys; sys.path.insert(0,'/tmp')
from harness import *
import numpy as np, pandas as pd
from sklearn.ensemble import RandomForestRegressor, ExtraTreesRegressor, HistGradientBoostingRegressor, GradientBoostingRegressor
from sklearn.linear_model import Ridge
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
df = load()
def raw(X): return X
models = {
'Ridge_raw': lambda: make_pipeline(StandardScaler(), Ridge(alpha=10.0)),
'RF': lambda: RandomForestRegressor(n_estimators=400, n_jobs=-1, random_state=0),
'ExtraTrees': lambda: ExtraTreesRegressor(n_estimators=400, n_jobs=-1, random_state=0),
'HGB': lambda: HistGradientBoostingRegressor(max_iter=500, learning_rate=0.05, random_state=0),
'GBR': lambda: GradientBoostingRegressor(n_estimators=500, learning_rate=0.05, max_depth=3, random_state=0),
}
for name, mk in models.items():
r = group_cv(mk, df, n_splits=5)
print(f"{name:12s} rmse={r['rmse']:.3f} mae={r['mae']:.3f} p90={r['p90']:.3f} p95={r['p95']:.3f} max={r['mx']:.3f}")
EOF
python3 /tmp/bench1.pyRidge_raw rmse=5.013 mae=3.905 p90=7.100 p95=7.673 max=9.578 RF rmse=2.999 mae=2.314 p90=4.300 p95=4.772 max=5.735 ExtraTrees rmse=2.822 mae=2.151 p90=3.974 p95=4.699 max=5.638 HGB rmse=2.897 mae=2.225 p90=4.137 p95=4.729 max=6.753 GBR rmse=2.997 mae=2.321 p90=4.234 p95=4.683 max=5.503
cd /app && cat > /tmp/feats.py << 'EOF'
import numpy as np, pandas as pd
NU = 1.5e-5 # kinematic viscosity of air
def engineer(X):
f = X['frequency'].astype(float).values
a = X['attack_angle'].astype(float).values
c = X['chord_length'].astype(float).values
U = X['free_stream_velocity'].astype(float).values
d = X['suction_side_displacement_thickness'].astype(float).values
out = {}
out['log_f'] = np.log10(f)
out['angle'] = a
out['log_c'] = np.log10(c)
out['U'] = U
out['log_d'] = np.log10(d)
# physics
out['strouhal'] = np.log10(f*d/U) # Strouhal number (log)
out['Re_c'] = np.log10(U*c/NU) # chord Reynolds
out['Re_d'] = np.log10(U*d/NU) # thickness Reynolds
out['fd'] = np.log10(f*d)
out['mach'] = U/340.0
out['d_over_c'] = np.log10(d/c)
out['angle_rad'] = np.deg2rad(a)
out['sin_a'] = np.sin(np.deg2rad(a))
out['log_f_sq'] = np.log10(f)**2
return pd.DataFrame(out, index=X.index)
EOF
cat > /tmp/bench2.py << 'EOF'
import sys; sys.path.insert(0,'/tmp')
from harness import *
from feats import engineer
import numpy as np
from sklearn.ensemble import RandomForestRegressor, ExtraTreesRegressor, HistGradientBoostingRegressor, GradientBoostingRegressor
from sklearn.linear_model import Ridge
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.neural_network import MLPRegressor
df = load()
models = {
'Ridge_eng': lambda: make_pipeline(StandardScaler(), Ridge(alpha=1.0)),
'RF_eng': lambda: RandomForestRegressor(n_estimators=400, n_jobs=-1, random_state=0),
'ExtraTrees_eng': lambda: ExtraTreesRegressor(n_estimators=400, n_jobs=-1, random_state=0),
'HGB_eng': lambda: HistGradientBoostingRegressor(max_iter=600, learning_rate=0.05, random_state=0),
'GBR_eng': lambda: GradientBoostingRegressor(n_estimators=600, learning_rate=0.05, max_depth=3, random_state=0),
'MLP_eng': lambda: make_pipeline(StandardScaler(), MLPRegressor(hidden_layer_sizes=(128,128,64), max_iter=2000, alpha=1e-3, random_state=0)),
}
for name, mk in models.items():
r = group_cv(mk, df, n_splits=5, feat_fn=engineer)
print(f"{name:16s} rmse={r['rmse']:.3f} mae={r['mae']:.3f} p90={r['p90']:.3f} p95={r['p95']:.3f} max={r['mx']:.3f}")
EOF
python3 /tmp/bench2.pyRidge_eng rmse=5.128 mae=3.945 p90=6.912 p95=7.889 max=10.631 RF_eng rmse=2.462 mae=1.856 p90=3.582 p95=3.851 max=5.481 ExtraTrees_eng rmse=2.316 mae=1.699 p90=3.310 p95=3.742 max=5.646 HGB_eng rmse=2.413 mae=1.823 p90=3.586 p95=4.193 max=5.025 GBR_eng rmse=2.760 mae=2.067 p90=4.042 p95=4.592 max=7.575 MLP_eng rmse=3.043 mae=2.257 p90=4.536 p95=5.484 max=7.500
cd /app && python3 -c "
import sys; sys.path.insert(0,'/tmp')
from harness import cond_key
import pandas as pd, numpy as np
tr = pd.read_csv('/app/data/train.csv'); va = pd.read_csv('/app/data/validation.csv')
allf = pd.concat([tr,va], ignore_index=True)
k = cond_key(allf)
ktr = k[:len(tr)]; kva = k[len(tr):]
print('train snapped conditions:', len(np.unique(ktr)))
print('val snapped conditions:', len(np.unique(kva)))
print('overlap:', len(set(ktr)&set(kva)))
print('val-only conditions:', len(set(kva)-set(ktr)))
# rows per val condition
import collections
c = collections.Counter(kva)
print('val rows per condition: min/mean/max', min(c.values()), round(np.mean(list(c.values())),1), max(c.values()))
"train snapped conditions: 67 val snapped conditions: 15 overlap: 0 val-only conditions: 15 val rows per condition: min/mean/max 8 13.4 18
cd /app && cat > /tmp/bench3.py << 'EOF'
import sys; sys.path.insert(0,'/tmp')
from harness import evaluate, FEATURES, TARGET, cond_key
from feats import engineer
import numpy as np, pandas as pd
from sklearn.ensemble import RandomForestRegressor, ExtraTreesRegressor, HistGradientBoostingRegressor
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.neural_network import MLPRegressor
tr = pd.read_csv('/app/data/train.csv'); va = pd.read_csv('/app/data/validation.csv')
Xtr, ytr = engineer(tr[FEATURES]), tr[TARGET].values
Xva = engineer(va[FEATURES])
def report(name, pred):
r = evaluate(va, pred)
print(f"{name:16s} rmse={r['rmse']:.3f} mae={r['mae']:.3f} p90={r['p90']:.3f} p95={r['p95']:.3f} max={r['mx']:.3f} ngrp={r['ngroups']}")
models = {
'RF': lambda: RandomForestRegressor(n_estimators=600, n_jobs=-1, random_state=0),
'ExtraTrees': lambda: ExtraTreesRegressor(n_estimators=600, n_jobs=-1, random_state=0),
'HGB': lambda: HistGradientBoostingRegressor(max_iter=600, learning_rate=0.05, random_state=0),
'MLP': lambda: make_pipeline(StandardScaler(), MLPRegressor(hidden_layer_sizes=(128,128,64), max_iter=3000, alpha=1e-3, random_state=0)),
}
for name, mk in models.items():
m = mk(); m.fit(Xtr, ytr); report(name, m.predict(Xva))
EOF
python3 /tmp/bench3.pyRF rmse=2.569 mae=2.022 p90=3.703 p95=3.803 max=3.813 ngrp=15 ExtraTrees rmse=2.157 mae=1.659 p90=3.227 p95=3.361 max=3.597 ngrp=15 HGB rmse=2.468 mae=1.904 p90=3.656 p95=3.807 max=3.906 ngrp=15 MLP rmse=3.032 mae=2.243 p90=4.369 p95=4.673 max=5.184 ngrp=15
cd /app && cat > /tmp/diag.py << 'EOF'
import sys; sys.path.insert(0,'/tmp')
from harness import FEATURES, TARGET, cond_key, VEL, CHORD, snap
from feats import engineer
import numpy as np, pandas as pd
from sklearn.ensemble import ExtraTreesRegressor
tr = pd.read_csv('/app/data/train.csv'); va = pd.read_csv('/app/data/validation.csv')
Xtr, ytr = engineer(tr[FEATURES]), tr[TARGET].values
Xva = engineer(va[FEATURES])
m = ExtraTreesRegressor(n_estimators=600, n_jobs=-1, random_state=0); m.fit(Xtr, ytr)
pred = m.predict(Xva)
err = pred - va[TARGET].values
keys = cond_key(va)
rows=[]
for k in np.unique(keys):
mask = keys==k
sub = va[mask]
rmse = np.sqrt(np.mean(err[mask]**2))
bias = np.mean(err[mask])
rows.append((k, mask.sum(), rmse, bias, sub['attack_angle'].mean(), sub['chord_length'].mean(), sub['free_stream_velocity'].mean(), sub['frequency'].min(), sub['frequency'].max()))
r = pd.DataFrame(rows, columns=['key','n','rmse','bias','angle','chord','vel','fmin','fmax']).sort_values('rmse',ascending=False)
pd.set_option('display.width',200)
print(r.to_string(index=False))
EOF
python3 /tmp/diag.pykey n rmse bias angle chord vel fmin fmax 71.3_0.0254_0 10 3.597379 0.155125 0.016846 0.025399 71.354087 2500.994730 20017.248537 55.5_0.1016_7 16 3.259926 -2.605290 12.301458 0.101604 55.514674 199.807248 6291.367118 39.6_0.0254_8 15 3.176799 0.017012 17.399239 0.025400 39.582282 200.199479 5001.540499 39.6_0.1016_7 16 2.622832 -2.353021 12.301440 0.101606 39.584861 199.661620 6299.282000 31.7_0.3048_0 18 2.221407 1.800996 0.002455 0.304850 31.696343 199.676136 9984.682697 55.5_0.1016_4 8 2.207098 -1.312146 6.681868 0.101618 55.497174 500.450561 2504.199654 55.5_0.0508_5 12 2.066137 0.332950 8.404495 0.050814 55.542907 400.019433 5008.303211 39.6_0.0254_3 14 1.705524 0.665938 4.799941 0.025402 39.581086 499.731621 10004.090709 31.7_0.2286_2 15 1.622628 -0.811178 4.001627 0.228600 31.682930 250.424836 6292.827687 71.3_0.0254_3 11 1.501510 -0.980738 4.802919 0.025397 71.304755 2003.364554 20013.223277 39.6_0.0254_0 11 1.274597 0.218518 -0.008083 0.025397 39.627032 1600.327555 15978.590818 71.3_0.1016_1 12 1.251451 0.228243 3.311753 0.101572 71.263363 798.837157 9994.040516 55.5_0.0508_0 13 1.201392 0.002311 -0.002772 0.050792 55.497693 799.803576 12515.209939 39.6_0.1524_0 14 1.171762 0.326849 -0.006416 0.152387 39.622576 500.381136 9993.587967 31.7_0.1524_6 16 0.979755 -0.301411 9.900994 0.152411 31.714249 199.767339 6292.417921
cd /app && cat > /tmp/bench4.py << 'EOF'
import sys; sys.path.insert(0,'/tmp')
from harness import evaluate, FEATURES, TARGET
from feats import engineer
import numpy as np, pandas as pd
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.svm import SVR
from sklearn.kernel_ridge import KernelRidge
from sklearn.gaussian_process import GaussianProcessRegressor
from sklearn.gaussian_process.kernels import RBF, ConstantKernel, WhiteKernel, Matern
tr = pd.read_csv('/app/data/train.csv'); va = pd.read_csv('/app/data/validation.csv')
Xtr, ytr = engineer(tr[FEATURES]), tr[TARGET].values
Xva = engineer(va[FEATURES])
def report(name, pred):
r = evaluate(va, pred)
print(f"{name:22s} rmse={r['rmse']:.3f} mae={r['mae']:.3f} p90={r['p90']:.3f} p95={r['p95']:.3f} max={r['mx']:.3f}")
models = {
'SVR_rbf': lambda: make_pipeline(StandardScaler(), SVR(C=10, gamma='scale', epsilon=0.1)),
'SVR_rbf_C30': lambda: make_pipeline(StandardScaler(), SVR(C=30, gamma=0.1, epsilon=0.05)),
'KRR_rbf': lambda: make_pipeline(StandardScaler(), KernelRidge(alpha=0.1, kernel='rbf', gamma=0.1)),
'GP_matern': lambda: make_pipeline(StandardScaler(), GaussianProcessRegressor(
kernel=ConstantKernel(1.0)*Matern(length_scale=np.ones(Xtr.shape[1]), nu=1.5)+WhiteKernel(0.1),
normalize_y=True, n_restarts_optimizer=0, random_state=0)),
}
for name, mk in models.items():
m = mk(); m.fit(Xtr, ytr); report(name, m.predict(Xva))
EOF
python3 /tmp/bench4.pySVR_rbf rmse=2.659 mae=1.906 p90=4.208 p95=4.379 max=4.403 SVR_rbf_C30 rmse=2.829 mae=2.032 p90=4.443 p95=4.758 max=5.185 KRR_rbf rmse=7.303 mae=4.328 p90=12.591 p95=19.161 max=23.263 GP_matern rmse=1.969 mae=1.476 p90=2.943 p95=3.422 max=3.989 [stderr] /usr/local/lib/python3.12/dist-packages/sklearn/gaussian_process/kernels.py:455: ConvergenceWarning: The optimal value found for dimension 4 of parameter k1__k2__length_scale is close to the specified upper bound 100000.0. Increasing the bound and calling fit again may find a better value. warnings.warn( /usr/local/lib/python3.12/dist-packages/sklearn/gaussian_process/kernels.py:455: ConvergenceWarning: The optimal value found for dimension 7 of parameter k1__k2__length_scale is close to the specified upper bound 100000.0. Increasing the bound and calling fit again may find a better value. warnings.warn( /usr/local/lib/python3.12/dist-packages/sklearn/gaussian_process/kernels.py:455: ConvergenceWarning: The optimal value found for dimension 13 of parameter k1__k2__length_scale is close to the specified upper bound 100000.0. Increasing the bound and calling fit again may find a better value. warnings.warn(
cd /app && cat > /tmp/bench5.py << 'EOF'
import sys, warnings; sys.path.insert(0,'/tmp'); warnings.filterwarnings('ignore')
from harness import evaluate, FEATURES, TARGET
from feats import engineer
import numpy as np, pandas as pd
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.ensemble import ExtraTreesRegressor
from sklearn.gaussian_process import GaussianProcessRegressor
from sklearn.gaussian_process.kernels import RBF, ConstantKernel as C, WhiteKernel, Matern
tr = pd.read_csv('/app/data/train.csv'); va = pd.read_csv('/app/data/validation.csv')
Xtr, ytr = engineer(tr[FEATURES]), tr[TARGET].values
Xva = engineer(va[FEATURES])
d = Xtr.shape[1]
def report(name, pred):
r = evaluate(va, pred)
print(f"{name:22s} rmse={r['rmse']:.3f} mae={r['mae']:.3f} p90={r['p90']:.3f} p95={r['p95']:.3f} max={r['mx']:.3f}")
def gp(nu, wk=0.1):
return make_pipeline(StandardScaler(), GaussianProcessRegressor(
kernel=C(1.0)*Matern(length_scale=np.ones(d), nu=nu)+WhiteKernel(wk, (1e-3,10.0)),
normalize_y=True, n_restarts_optimizer=2, random_state=0, alpha=1e-8))
preds = {}
for nu in [1.5, 2.5]:
m = gp(nu); m.fit(Xtr,ytr); p = m.predict(Xva); preds[f'GP_nu{nu}']=p; report(f'GP_nu{nu}', p)
# RBF ARD
m = make_pipeline(StandardScaler(), GaussianProcessRegressor(kernel=C(1.0)*RBF(np.ones(d))+WhiteKernel(0.1,(1e-3,10)), normalize_y=True, n_restarts_optimizer=2, random_state=0, alpha=1e-8))
m.fit(Xtr,ytr); p=m.predict(Xva); preds['GP_rbf']=p; report('GP_rbf', p)
# ExtraTrees
et = ExtraTreesRegressor(n_estimators=800, n_jobs=-1, random_state=0); et.fit(Xtr,ytr); pet=et.predict(Xva); preds['ET']=pet
# blends
for w in [0.2,0.3,0.4,0.5]:
report(f'GP25*{1-w}+ET*{w}', (1-w)*preds['GP_nu2.5']+w*pet)
EOF
python3 /tmp/bench5.py[stderr] GP_nu1.5 rmse=1.969 mae=1.476 p90=2.943 p95=3.422 max=3.989Shell command timed out. Tool interaction exceeded 300 seconds.
restart: true
<system>tool has been restarted.</system>
cd /app && cat > /tmp/bench5.py << 'EOF'
import sys, warnings, time; sys.path.insert(0,'/tmp'); warnings.filterwarnings('ignore')
from harness import evaluate, FEATURES, TARGET
from feats import engineer
import numpy as np, pandas as pd
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.ensemble import ExtraTreesRegressor
from sklearn.gaussian_process import GaussianProcessRegressor
from sklearn.gaussian_process.kernels import RBF, ConstantKernel as C, WhiteKernel, Matern
tr = pd.read_csv('/app/data/train.csv'); va = pd.read_csv('/app/data/validation.csv')
Xtr, ytr = engineer(tr[FEATURES]), tr[TARGET].values
Xva = engineer(va[FEATURES])
d = Xtr.shape[1]
def report(name, pred):
r = evaluate(va, pred)
print(f"{name:22s} rmse={r['rmse']:.3f} mae={r['mae']:.3f} p90={r['p90']:.3f} p95={r['p95']:.3f} max={r['mx']:.3f}", flush=True)
def gp(nu, wk=0.1):
return make_pipeline(StandardScaler(), GaussianProcessRegressor(
kernel=C(1.0)*Matern(length_scale=np.ones(d), nu=nu)+WhiteKernel(wk,(1e-3,10.0)),
normalize_y=True, n_restarts_optimizer=0, random_state=0, alpha=1e-8))
preds={}
t=time.time()
for nu in [1.5,2.5]:
m=gp(nu); m.fit(Xtr,ytr); p=m.predict(Xva); preds[f'GP{nu}']=p; report(f'GP_nu{nu}',p)
m=make_pipeline(StandardScaler(), GaussianProcessRegressor(kernel=C(1.0)*RBF(np.ones(d))+WhiteKernel(0.1,(1e-3,10)),normalize_y=True,n_restarts_optimizer=0,random_state=0,alpha=1e-8))
m.fit(Xtr,ytr); preds['rbf']=m.predict(Xva); report('GP_rbf',preds['rbf'])
et=ExtraTreesRegressor(n_estimators=800,n_jobs=-1,random_state=0); et.fit(Xtr,ytr); pet=et.predict(Xva); preds['ET']=pet
for w in [0.2,0.3,0.4]:
report(f'GP25*{1-w:.1f}+ET*{w}', (1-w)*preds['GP2.5']+w*pet)
print('elapsed', round(time.time()-t,1))
EOF
timeout 250 python3 /tmp/bench5.pyGP_nu1.5 rmse=1.969 mae=1.476 p90=2.943 p95=3.422 max=3.989 GP_nu2.5 rmse=2.120 mae=1.630 p90=3.097 p95=3.386 max=4.048
cd /app && cat > /tmp/feats2.py << 'EOF'
import numpy as np, pandas as pd
NU=1.5e-5
def eng_core(X):
f=X['frequency'].astype(float).values; a=X['attack_angle'].astype(float).values
c=X['chord_length'].astype(float).values; U=X['free_stream_velocity'].astype(float).values
d=X['suction_side_displacement_thickness'].astype(float).values
out={}
out['log_f']=np.log10(f); out['angle']=a; out['log_c']=np.log10(c)
out['U']=U; out['log_d']=np.log10(d); out['strouhal']=np.log10(f*d/U)
return pd.DataFrame(out,index=X.index)
EOF
cat > /tmp/t_gp.py << 'EOF'
import sys, warnings, time; sys.path.insert(0,'/tmp'); warnings.filterwarnings('ignore')
from harness import evaluate, FEATURES, TARGET
from feats2 import eng_core
import numpy as np, pandas as pd
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.gaussian_process import GaussianProcessRegressor
from sklearn.gaussian_process.kernels import ConstantKernel as C, WhiteKernel, Matern
tr=pd.read_csv('/app/data/train.csv'); va=pd.read_csv('/app/data/validation.csv')
Xtr,ytr=eng_core(tr[FEATURES]),tr[TARGET].values; Xva=eng_core(va[FEATURES]); d=Xtr.shape[1]
t=time.time()
m=make_pipeline(StandardScaler(), GaussianProcessRegressor(kernel=C(1.0)*Matern(np.ones(d),nu=1.5)+WhiteKernel(0.1,(1e-3,10)),normalize_y=True,n_restarts_optimizer=0,random_state=0,alpha=1e-8))
m.fit(Xtr,ytr); p=m.predict(Xva)
r=evaluate(va,p)
print(f"GP_core6 rmse={r['rmse']:.3f} mae={r['mae']:.3f} p90={r['p90']:.3f} p95={r['p95']:.3f} max={r['mx']:.3f} time={time.time()-t:.1f}s")
EOF
timeout 250 python3 /tmp/t_gp.pyGP_core6 rmse=1.879 mae=1.399 p90=2.967 p95=3.384 max=3.888 time=15.8s
cd /app && cat > /tmp/diag_gp.py << 'EOF'
import sys, warnings; sys.path.insert(0,'/tmp'); warnings.filterwarnings('ignore')
from harness import evaluate, FEATURES, TARGET, cond_key
from feats2 import eng_core
import numpy as np, pandas as pd
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.gaussian_process import GaussianProcessRegressor
from sklearn.gaussian_process.kernels import ConstantKernel as C, WhiteKernel, Matern
tr=pd.read_csv('/app/data/train.csv'); va=pd.read_csv('/app/data/validation.csv')
Xtr,ytr=eng_core(tr[FEATURES]),tr[TARGET].values; Xva=eng_core(va[FEATURES]); d=Xtr.shape[1]
m=make_pipeline(StandardScaler(), GaussianProcessRegressor(kernel=C(1.0)*Matern(np.ones(d),nu=1.5)+WhiteKernel(0.1,(1e-3,10)),normalize_y=True,n_restarts_optimizer=0,random_state=0,alpha=1e-8))
m.fit(Xtr,ytr); pred=m.predict(Xva); err=pred-va[TARGET].values
keys=cond_key(va); rows=[]
for k in np.unique(keys):
mk=keys==k; sub=va[mk]
rows.append((k,mk.sum(),np.sqrt(np.mean(err[mk]**2)),np.mean(err[mk]),sub['attack_angle'].mean(),sub['chord_length'].mean(),sub['free_stream_velocity'].mean()))
r=pd.DataFrame(rows,columns=['key','n','rmse','bias','angle','chord','vel']).sort_values('rmse',ascending=False)
pd.set_option('display.width',200); print(r.to_string(index=False))
EOF
timeout 120 python3 /tmp/diag_gp.pykey n rmse bias angle chord vel 71.3_0.0254_0 10 3.887856 -0.689623 0.016846 0.025399 71.354087 39.6_0.0254_8 15 3.167966 -0.611906 17.399239 0.025400 39.582282 55.5_0.0508_0 13 2.665624 2.438272 -0.002772 0.050792 55.497693 55.5_0.1016_7 16 1.863730 -1.027427 12.301458 0.101604 55.514674 71.3_0.0254_3 11 1.826047 -1.350612 4.802919 0.025397 71.304755 31.7_0.2286_2 15 1.760038 -0.573218 4.001627 0.228600 31.682930 39.6_0.1016_7 16 1.555111 -1.422113 12.301440 0.101606 39.584861 31.7_0.1524_6 16 1.546506 -0.204664 9.900994 0.152411 31.714249 39.6_0.0254_3 14 1.326242 0.215025 4.799941 0.025402 39.581086 39.6_0.0254_0 11 1.268329 0.596333 -0.008083 0.025397 39.627032 71.3_0.1016_1 12 1.122077 0.677321 3.311753 0.101572 71.263363 31.7_0.3048_0 18 1.034508 0.872812 0.002455 0.304850 31.696343 55.5_0.0508_5 12 1.005847 0.515269 8.404495 0.050814 55.542907 39.6_0.1524_0 14 0.778083 -0.169893 -0.006416 0.152387 39.622576 55.5_0.1016_4 8 0.544945 -0.021372 6.681868 0.101618 55.497174
cd /app && cat > /tmp/blend1.py << 'EOF'
import sys, warnings; sys.path.insert(0,'/tmp'); warnings.filterwarnings('ignore')
from harness import evaluate, FEATURES, TARGET
from feats2 import eng_core
from feats import engineer
import numpy as np, pandas as pd
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.ensemble import ExtraTreesRegressor, HistGradientBoostingRegressor
from sklearn.gaussian_process import GaussianProcessRegressor
from sklearn.gaussian_process.kernels import ConstantKernel as C, WhiteKernel, Matern
tr=pd.read_csv('/app/data/train.csv'); va=pd.read_csv('/app/data/validation.csv')
Xc,ytr=eng_core(tr[FEATURES]),tr[TARGET].values; Xvc=eng_core(va[FEATURES]); d=Xc.shape[1]
Xe=engineer(tr[FEATURES]); Xve=engineer(va[FEATURES])
def report(name,pred):
r=evaluate(va,pred); print(f"{name:20s} rmse={r['rmse']:.3f} mae={r['mae']:.3f} p90={r['p90']:.3f} p95={r['p95']:.3f} max={r['mx']:.3f}",flush=True)
gp=make_pipeline(StandardScaler(), GaussianProcessRegressor(kernel=C(1.0)*Matern(np.ones(d),nu=1.5)+WhiteKernel(0.1,(1e-3,10)),normalize_y=True,n_restarts_optimizer=0,random_state=0,alpha=1e-8))
gp.fit(Xc,ytr); pg=gp.predict(Xvc)
et=ExtraTreesRegressor(n_estimators=800,n_jobs=-1,random_state=0); et.fit(Xe,ytr); pe=et.predict(Xve)
hgb=HistGradientBoostingRegressor(max_iter=600,learning_rate=0.05,random_state=0); hgb.fit(Xe,ytr); ph=hgb.predict(Xve)
report('GP',pg); report('ET',pe); report('HGB',ph)
for w in [0.15,0.25,0.35]:
report(f'GP*{1-w:.2f}+ET*{w:.2f}',(1-w)*pg+w*pe)
for w in [0.2,0.3]:
report(f'GP*{1-2*w:.2f}+ET*{w}+HGB*{w}',(1-2*w)*pg+w*pe+w*ph)
np.save('/tmp/pg.npy',pg); np.save('/tmp/pe.npy',pe); np.save('/tmp/ph.npy',ph)
EOF
timeout 200 python3 /tmp/blend1.pyGP rmse=1.879 mae=1.399 p90=2.967 p95=3.384 max=3.888 ET rmse=2.167 mae=1.667 p90=3.248 p95=3.375 max=3.610 HGB rmse=2.468 mae=1.904 p90=3.656 p95=3.807 max=3.906 GP*0.85+ET*0.15 rmse=1.857 mae=1.400 p90=2.822 p95=3.360 max=3.830 GP*0.75+ET*0.25 rmse=1.856 mae=1.404 p90=2.742 p95=3.347 max=3.795 GP*0.65+ET*0.35 rmse=1.866 mae=1.414 p90=2.793 p95=3.336 max=3.762 GP*0.60+ET*0.2+HGB*0.2 rmse=1.883 mae=1.443 p90=2.774 p95=3.298 max=3.717 GP*0.40+ET*0.3+HGB*0.3 rmse=1.955 mae=1.501 p90=2.894 p95=3.289 max=3.650
cd /app && cat > /tmp/detail.py << 'EOF'
import numpy as np, pandas as pd, sys; sys.path.insert(0,'/tmp')
from harness import cond_key, TARGET
va=pd.read_csv('/app/data/validation.csv')
pg=np.load('/tmp/pg.npy'); pe=np.load('/tmp/pe.npy')
pred=0.75*pg+0.25*pe
keys=cond_key(va); y=va[TARGET].values
# show the two worst conditions detail
for key in ['71.3_0.0254_0','55.5_0.0508_0']:
m=keys==key; sub=va[m].copy(); sub['y']=y[m]; sub['pred']=pred[m]; sub['err']=pred[m]-y[m]
sub=sub.sort_values('frequency')
print('===',key,'===')
print(sub[['frequency','y','pred','err']].to_string(index=False))
print()
EOF
python3 /tmp/detail.py=== 71.3_0.0254_0 === frequency y pred err 2500.994730 132.895226 129.341869 -3.553357 3154.408908 136.451767 130.707867 -5.743900 4006.744847 138.246982 132.985800 -5.261182 5005.555701 136.731129 135.291705 -1.439424 6299.800239 135.063979 136.296115 1.232136 7990.733167 130.098567 136.463538 6.364971 9983.069023 131.128466 135.580305 4.451839 12482.966342 133.621157 133.271030 -0.350126 15994.624462 130.921043 128.885714 -2.035330 20017.248537 123.640576 125.233847 1.593271 === 55.5_0.0508_0 === frequency y pred err 799.803576 123.692781 127.790846 4.098065 1000.940246 125.136914 128.635845 3.498932 1252.056962 126.846602 129.338492 2.491891 1602.416050 128.612331 130.732087 2.119756 2001.205776 131.747337 132.776404 1.029067 2498.469195 134.511114 135.002881 0.491767 3145.228870 135.654809 136.606737 0.951928 3993.427980 136.044707 137.231034 1.186327 4995.340931 135.881701 137.015772 1.134070 6301.609782 134.316408 135.610932 1.294524 8010.498306 131.826309 133.319448 1.493139 10016.984950 129.395460 131.318815 1.923355 12515.209939 127.178549 129.380203 2.201654
cd /app && cat > /tmp/ablate.py << 'EOF'
import sys, warnings, time; sys.path.insert(0,'/tmp'); warnings.filterwarnings('ignore')
from harness import evaluate, FEATURES, TARGET
import numpy as np, pandas as pd
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.gaussian_process import GaussianProcessRegressor
from sklearn.gaussian_process.kernels import ConstantKernel as C, WhiteKernel, Matern
NU=1.5e-5
tr=pd.read_csv('/app/data/train.csv'); va=pd.read_csv('/app/data/validation.csv')
def build(X, feats):
f=X['frequency'].astype(float).values; a=X['attack_angle'].astype(float).values
c=X['chord_length'].astype(float).values; U=X['free_stream_velocity'].astype(float).values
dd=X['suction_side_displacement_thickness'].astype(float).values
b={}
b['log_f']=np.log10(f); b['angle']=a; b['log_c']=np.log10(c); b['U']=U; b['log_d']=np.log10(dd)
b['strouhal']=np.log10(f*dd/U); b['str2']=np.log10(f*dd/U)**2; b['Re_c']=np.log10(U*c/NU)
b['Re_d']=np.log10(U*dd/NU); b['logU']=np.log10(U); b['af']=a*np.log10(f); b['dc']=np.log10(dd/c)
b['sin_a']=np.sin(np.deg2rad(a)); b['mach']=U/340.0
return pd.DataFrame({k:b[k] for k in feats}, index=X.index)
def run(feats):
Xtr=build(tr[FEATURES],feats); Xva=build(va[FEATURES],feats); d=len(feats)
m=make_pipeline(StandardScaler(), GaussianProcessRegressor(kernel=C(1.0)*Matern(np.ones(d),nu=1.5)+WhiteKernel(0.1,(1e-3,10)),normalize_y=True,n_restarts_optimizer=0,random_state=0,alpha=1e-8))
m.fit(Xtr,tr[TARGET].values); p=m.predict(Xva); return p, evaluate(va,p)
sets={
'A_core': ['log_f','angle','log_c','U','log_d','strouhal'],
'B_str2': ['log_f','angle','log_c','U','log_d','strouhal','str2'],
'C_nostr': ['log_f','angle','log_c','U','log_d'],
'D_Re': ['log_f','angle','log_c','U','log_d','strouhal','Re_c'],
'E_full': ['log_f','angle','log_c','U','log_d','strouhal','str2','Re_c','dc'],
'F_int': ['log_f','angle','log_c','U','log_d','strouhal','str2','af'],
'G_sin': ['log_f','sin_a','angle','log_c','U','log_d','strouhal','str2'],
}
for name, feats in sets.items():
t=time.time(); p,r=run(feats)
np.save(f'/tmp/p_{name}.npy',p)
print(f"{name:10s} rmse={r['rmse']:.3f} mae={r['mae']:.3f} p90={r['p90']:.3f} p95={r['p95']:.3f} max={r['mx']:.3f} ({time.time()-t:.0f}s)",flush=True)
EOF
nohup timeout 280 python3 /tmp/ablate.py > /tmp/ablate.log 2>&1 &
echo "started"; sleep 5; cat /tmp/ablate.logstarted
sleep 120; cat /tmp/ablate.log
A_core rmse=1.879 mae=1.399 p90=2.967 p95=3.384 max=3.888 (16s) B_str2 rmse=1.879 mae=1.399 p90=2.967 p95=3.384 max=3.888 (26s) C_nostr rmse=2.062 mae=1.529 p90=3.368 p95=3.709 max=3.957 (9s) D_Re rmse=1.869 mae=1.377 p90=2.853 p95=3.389 max=3.906 (18s) E_full rmse=1.868 mae=1.392 p90=2.770 p95=3.303 max=3.906 (35s)
sleep 60; cat /tmp/ablate.log
A_core rmse=1.879 mae=1.399 p90=2.967 p95=3.384 max=3.888 (16s) B_str2 rmse=1.879 mae=1.399 p90=2.967 p95=3.384 max=3.888 (26s) C_nostr rmse=2.062 mae=1.529 p90=3.368 p95=3.709 max=3.957 (9s) D_Re rmse=1.869 mae=1.377 p90=2.853 p95=3.389 max=3.906 (18s) E_full rmse=1.868 mae=1.392 p90=2.770 p95=3.303 max=3.906 (35s) F_int rmse=1.932 mae=1.441 p90=3.090 p95=3.434 max=3.897 (31s)
sleep 45; cat /tmp/ablate.log
A_core rmse=1.879 mae=1.399 p90=2.967 p95=3.384 max=3.888 (16s) B_str2 rmse=1.879 mae=1.399 p90=2.967 p95=3.384 max=3.888 (26s) C_nostr rmse=2.062 mae=1.529 p90=3.368 p95=3.709 max=3.957 (9s) D_Re rmse=1.869 mae=1.377 p90=2.853 p95=3.389 max=3.906 (18s) E_full rmse=1.868 mae=1.392 p90=2.770 p95=3.303 max=3.906 (35s) F_int rmse=1.932 mae=1.441 p90=3.090 p95=3.434 max=3.897 (31s) G_sin rmse=1.879 mae=1.399 p90=2.967 p95=3.384 max=3.888 (63s)
cd /app && cat > /tmp/blend2.py << 'EOF'
import sys, warnings; sys.path.insert(0,'/tmp'); warnings.filterwarnings('ignore')
from harness import evaluate, FEATURES, TARGET, cond_key
import numpy as np, pandas as pd
va=pd.read_csv('/app/data/validation.csv'); y=va[TARGET].values
pg=np.load('/tmp/p_E_full.npy'); pe=np.load('/tmp/pe.npy'); ph=np.load('/tmp/ph.npy')
keys=cond_key(va)
def stats(pred):
err=pred-y; cr=np.array([np.sqrt(np.mean(err[keys==k]**2)) for k in np.unique(keys)])
return cr
def report(name,pred):
r=evaluate(va,pred); cr=stats(pred); nunder=(cr<=2.35).sum()
print(f"{name:22s} rmse={r['rmse']:.3f} mae={r['mae']:.3f} p90={r['p90']:.3f} p95={r['p95']:.3f} max={r['mx']:.3f} under2.35={nunder}/{len(cr)}",flush=True)
report('GP_E',pg); report('ET',pe); report('HGB',ph)
for w in [0.15,0.2,0.25,0.3,0.35]:
report(f'GP*{1-w:.2f}+ET*{w:.2f}',(1-w)*pg+w*pe)
best=0.75*pg+0.25*pe
cr=stats(best); order=np.argsort(cr)[::-1]
print('\nSorted condition RMSE (blend GP*.75+ET*.25):', np.round(np.sort(cr)[::-1],2))
EOF
python3 /tmp/blend2.pyGP_E rmse=1.868 mae=1.392 p90=2.770 p95=3.303 max=3.906 under2.35=12/15 ET rmse=2.167 mae=1.667 p90=3.248 p95=3.375 max=3.610 under2.35=11/15 HGB rmse=2.468 mae=1.904 p90=3.656 p95=3.807 max=3.906 under2.35=9/15 GP*0.85+ET*0.15 rmse=1.848 mae=1.395 p90=2.655 p95=3.287 max=3.852 under2.35=13/15 GP*0.80+ET*0.20 rmse=1.846 mae=1.397 p90=2.647 p95=3.283 max=3.834 under2.35=13/15 GP*0.75+ET*0.25 rmse=1.847 mae=1.400 p90=2.668 p95=3.280 max=3.817 under2.35=13/15 GP*0.70+ET*0.30 rmse=1.852 mae=1.405 p90=2.693 p95=3.277 max=3.800 under2.35=13/15 GP*0.65+ET*0.35 rmse=1.858 mae=1.411 p90=2.720 p95=3.276 max=3.784 under2.35=13/15 Sorted condition RMSE (blend GP*.75+ET*.25): [3.82 3.05 2.1 1.89 1.82 1.68 1.66 1.49 1.36 1.26 1.26 1.24 1.12 1.05 0.86]
cd /app && cat > /tmp/resgp.py << 'EOF'
import sys, warnings, time; sys.path.insert(0,'/tmp'); warnings.filterwarnings('ignore')
from harness import evaluate, FEATURES, TARGET, cond_key
import numpy as np, pandas as pd
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import Ridge
from sklearn.gaussian_process import GaussianProcessRegressor
from sklearn.gaussian_process.kernels import ConstantKernel as C, WhiteKernel, Matern, DotProduct
NU=1.5e-5
tr=pd.read_csv('/app/data/train.csv'); va=pd.read_csv('/app/data/validation.csv'); y=tr[TARGET].values; yv=va[TARGET].values
feats=['log_f','angle','log_c','U','log_d','strouhal','str2','Re_c','dc']
def build(X):
f=X['frequency'].astype(float).values; a=X['attack_angle'].astype(float).values
c=X['chord_length'].astype(float).values; U=X['free_stream_velocity'].astype(float).values
dd=X['suction_side_displacement_thickness'].astype(float).values
b={'log_f':np.log10(f),'angle':a,'log_c':np.log10(c),'U':U,'log_d':np.log10(dd),
'strouhal':np.log10(f*dd/U),'str2':np.log10(f*dd/U)**2,'Re_c':np.log10(U*c/NU),'dc':np.log10(dd/c)}
return pd.DataFrame(b,index=X.index)[feats]
Xtr=build(tr[FEATURES]); Xva=build(va[FEATURES]); d=len(feats)
keys=cond_key(va)
def report(name,pred):
r=evaluate(va,pred); err=pred-yv; cr=np.array([np.sqrt(np.mean(err[keys==k]**2)) for k in np.unique(keys)])
print(f"{name:22s} rmse={r['rmse']:.3f} mae={r['mae']:.3f} p90={r['p90']:.3f} p95={r['p95']:.3f} max={r['mx']:.3f} under={int((cr<=2.35).sum())}/15",flush=True)
# Plain GP constant mean (reference)
gp0=make_pipeline(StandardScaler(), GaussianProcessRegressor(kernel=C(1.0)*Matern(np.ones(d),nu=1.5)+WhiteKernel(0.1,(1e-3,10)),normalize_y=True,n_restarts_optimizer=0,random_state=0,alpha=1e-8))
gp0.fit(Xtr,y); p0=gp0.predict(Xva); report('GP_const',p0)
# Linear mean via Ridge base + GP on residuals
sc=StandardScaler().fit(Xtr); Xs=sc.transform(Xtr); Xvs=sc.transform(Xva)
for alpha in [1.0, 5.0]:
base=Ridge(alpha=alpha).fit(Xs,y); rtr=y-base.predict(Xs)
gpr=GaussianProcessRegressor(kernel=C(1.0)*Matern(np.ones(d),nu=1.5)+WhiteKernel(0.1,(1e-3,10)),normalize_y=True,n_restarts_optimizer=0,random_state=0,alpha=1e-8)
gpr.fit(Xs,rtr); pr=base.predict(Xvs)+gpr.predict(Xvs); report(f'Ridge{alpha}+GPres',pr)
# GP with DotProduct (linear) + Matern kernel
gpd=make_pipeline(StandardScaler(), GaussianProcessRegressor(kernel=C(1.0)*Matern(np.ones(d),nu=1.5)+C(1.0)*DotProduct(sigma_0=1.0)+WhiteKernel(0.1,(1e-3,10)),normalize_y=True,n_restarts_optimizer=0,random_state=0,alpha=1e-8))
t=time.time(); gpd.fit(Xtr,y); pd_=gpd.predict(Xva); report('GP_Matern+Dot',pd_)
np.save('/tmp/p_gpconst.npy',p0)
EOF
timeout 200 python3 /tmp/resgp.pyGP_const rmse=1.868 mae=1.392 p90=2.770 p95=3.303 max=3.906 under=12/15 Ridge1.0+GPres rmse=1.860 mae=1.360 p90=2.787 p95=3.335 max=3.947 under=12/15 Ridge5.0+GPres rmse=1.870 mae=1.375 p90=2.804 p95=3.334 max=3.941 under=12/15
cd /app && cat > /tmp/cv_blend.py << 'EOF'
import sys, warnings, time; sys.path.insert(0,'/tmp'); warnings.filterwarnings('ignore')
from harness import evaluate, FEATURES, TARGET, cond_key
import numpy as np, pandas as pd
from sklearn.model_selection import GroupKFold
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.ensemble import ExtraTreesRegressor
from sklearn.gaussian_process import GaussianProcessRegressor
from sklearn.gaussian_process.kernels import ConstantKernel as C, WhiteKernel, Matern
NU=1.5e-5
df=pd.concat([pd.read_csv('/app/data/train.csv'),pd.read_csv('/app/data/validation.csv')],ignore_index=True)
y=df[TARGET].values; keys=cond_key(df)
feats=['log_f','angle','log_c','U','log_d','strouhal','str2','Re_c','dc']
def build(X):
f=X['frequency'].astype(float).values; a=X['attack_angle'].astype(float).values
c=X['chord_length'].astype(float).values; U=X['free_stream_velocity'].astype(float).values
dd=X['suction_side_displacement_thickness'].astype(float).values
b={'log_f':np.log10(f),'angle':a,'log_c':np.log10(c),'U':U,'log_d':np.log10(dd),
'strouhal':np.log10(f*dd/U),'str2':np.log10(f*dd/U)**2,'Re_c':np.log10(U*c/NU),'dc':np.log10(dd/c)}
return pd.DataFrame(b,index=X.index)[feats]
X=build(df[FEATURES]); d=len(feats)
gkf=GroupKFold(n_splits=6)
oof_g=np.zeros(len(df)); oof_e=np.zeros(len(df))
t=time.time()
for tri,tei in gkf.split(X,y,groups=keys):
gp=make_pipeline(StandardScaler(), GaussianProcessRegressor(kernel=C(1.0)*Matern(np.ones(d),nu=1.5)+WhiteKernel(0.1,(1e-3,10)),normalize_y=True,n_restarts_optimizer=0,random_state=0,alpha=1e-8))
gp.fit(X.iloc[tri],y[tri]); oof_g[tei]=gp.predict(X.iloc[tei])
et=ExtraTreesRegressor(n_estimators=600,n_jobs=-1,random_state=0); et.fit(X.iloc[tri],y[tri]); oof_e[tei]=et.predict(X.iloc[tei])
print('cv time',round(time.time()-t),'s; n conditions',len(np.unique(keys)))
def report(name,pred):
err=pred-y; cr=np.array([np.sqrt(np.mean(err[keys==k]**2)) for k in np.unique(keys)])
print(f"{name:18s} rmse={np.sqrt(np.mean(err**2)):.3f} mae={np.mean(np.abs(err)):.3f} p90={np.percentile(cr,90):.3f} p95={np.percentile(cr,95):.3f} max={cr.max():.3f} under2.35={int((cr<=2.35).sum())}/{len(cr)}",flush=True)
report('GP',oof_g); report('ET',oof_e)
for w in [0.2,0.25,0.3]:
report(f'GP*{1-w:.2f}+ET*{w:.2f}',(1-w)*oof_g+w*oof_e)
np.save('/tmp/oof_g.npy',oof_g); np.save('/tmp/oof_e.npy',oof_e)
EOF
nohup timeout 280 python3 /tmp/cv_blend.py > /tmp/cv_blend.log 2>&1 &
echo started; sleep 90; cat /tmp/cv_blend.logstarted
sleep 60; cat /tmp/cv_blend.log
<system>Tool ran without output or errors</system>
sleep 30; cat /tmp/cv_blend.log
<system>Tool ran without output or errors</system>
sleep 45; cat /tmp/cv_blend.log; echo "---proc---"; pgrep -af cv_blend || echo "done"
---proc--- 590 timeout 280 python3 /tmp/cv_blend.py 592 python3 /tmp/cv_blend.py
sleep 50; cat /tmp/cv_blend.log; echo "---proc---"; pgrep -af cv_blend.py | grep -v timeout || echo "done"
cv time 259 s; n conditions 82 GP rmse=2.173 mae=1.550 p90=3.287 p95=4.042 max=5.744 under2.35=60/82 ET rmse=2.219 mae=1.647 p90=3.027 p95=3.837 max=5.551 under2.35=57/82 GP*0.80+ET*0.20 rmse=2.062 mae=1.476 p90=3.036 p95=4.062 max=4.999 under2.35=62/82 GP*0.75+ET*0.25 rmse=2.043 mae=1.464 p90=3.036 p95=4.073 max=5.014 under2.35=62/82 GP*0.70+ET*0.30 rmse=2.027 mae=1.456 p90=3.074 p95=3.945 max=5.044 under2.35=64/82 ---proc--- done
cd /app && python3 -c "
import sys; sys.path.insert(0,'/tmp'); import numpy as np, pandas as pd
from harness import cond_key, TARGET
df=pd.concat([pd.read_csv('/app/data/train.csv'),pd.read_csv('/app/data/validation.csv')],ignore_index=True)
keys=cond_key(df)
# For each condition with many points, fit smooth (poly deg 4 in log-f) and compute residual std
resids=[]
for k in np.unique(keys):
m=keys==k; sub=df[m]
if m.sum()<8: continue
lf=np.log10(sub['frequency'].values); sp=sub[TARGET].values
o=np.argsort(lf); lf=lf[o]; sp=sp[o]
coef=np.polyfit(lf,sp,min(4,len(lf)-2)); fit=np.polyval(coef,lf)
resids.append(np.std(sp-fit))
resids=np.array(resids)
print('within-condition smooth-fit residual std: mean %.3f median %.3f p90 %.3f max %.3f'%(resids.mean(),np.median(resids),np.percentile(resids,90),resids.max()))
print('This approximates irreducible label noise (dB).')
"within-condition smooth-fit residual std: mean 0.783 median 0.636 p90 1.500 max 3.040 This approximates irreducible label noise (dB).
cd /app && cat > /tmp/gptune.py << 'EOF'
import sys, warnings, time; sys.path.insert(0,'/tmp'); warnings.filterwarnings('ignore')
from harness import evaluate, FEATURES, TARGET, cond_key
import numpy as np, pandas as pd
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.gaussian_process import GaussianProcessRegressor
from sklearn.gaussian_process.kernels import ConstantKernel as C, WhiteKernel, Matern
NU=1.5e-5
tr=pd.read_csv('/app/data/train.csv'); va=pd.read_csv('/app/data/validation.csv'); y=tr[TARGET].values; yv=va[TARGET].values
feats=['log_f','angle','log_c','U','log_d','strouhal','str2','Re_c','dc']
def build(X):
f=X['frequency'].astype(float).values; a=X['attack_angle'].astype(float).values
c=X['chord_length'].astype(float).values; U=X['free_stream_velocity'].astype(float).values
dd=X['suction_side_displacement_thickness'].astype(float).values
b={'log_f':np.log10(f),'angle':a,'log_c':np.log10(c),'U':U,'log_d':np.log10(dd),
'strouhal':np.log10(f*dd/U),'str2':np.log10(f*dd/U)**2,'Re_c':np.log10(U*c/NU),'dc':np.log10(dd/c)}
return pd.DataFrame(b,index=X.index)[feats]
Xtr=build(tr[FEATURES]); Xva=build(va[FEATURES]); d=len(feats); keys=cond_key(va)
def report(name,pred):
r=evaluate(va,pred); err=pred-yv; cr=np.array([np.sqrt(np.mean(err[keys==k]**2)) for k in np.unique(keys)])
print(f"{name:26s} rmse={r['rmse']:.3f} mae={r['mae']:.3f} p90={r['p90']:.3f} p95={r['p95']:.3f} max={r['mx']:.3f} under={int((cr<=2.35).sum())}/15",flush=True)
configs={
'nu1.5_r4': (1.5,4,0.1,(1e-3,10.0)),
'nu2.5_r4': (2.5,4,0.1,(1e-3,10.0)),
'nu1.5_noisefix0.5': (1.5,0,0.5,'fixed'),
'nu1.5_noisehi': (1.5,4,0.5,(0.2,5.0)),
}
for name,(nu,rest,wk,wb) in configs.items():
t=time.time()
if wb=='fixed':
from sklearn.gaussian_process.kernels import WhiteKernel as WK
kern=C(1.0)*Matern(np.ones(d),nu=nu)+WK(wk,'fixed')
else:
kern=C(1.0)*Matern(np.ones(d),nu=nu)+WhiteKernel(wk,wb)
m=make_pipeline(StandardScaler(), GaussianProcessRegressor(kernel=kern,normalize_y=True,n_restarts_optimizer=rest,random_state=0,alpha=1e-8))
m.fit(Xtr,y); p=m.predict(Xva); report(f'{name}({time.time()-t:.0f}s)',p)
EOF
nohup timeout 290 python3 /tmp/gptune.py > /tmp/gptune.log 2>&1 &
echo startedstarted
cd /app && python3 -c "
import numpy as np, pandas as pd, sys; sys.path.insert(0,'/tmp')
from harness import TARGET
df=pd.concat([pd.read_csv('/app/data/train.csv'),pd.read_csv('/app/data/validation.csv')],ignore_index=True)
f=df['frequency'].values; U=df['free_stream_velocity'].values; dd=df['suction_side_displacement_thickness'].values
a=df['attack_angle'].values; c=df['chord_length'].values; sp=df[TARGET].values
st=np.log10(f*dd/U)
amp=10*np.log10(dd)+50*np.log10(U/340.0)
resid=sp-amp
# correlation of resid with st, and how tight the collapse is
print('corr(resid, st):', np.corrcoef(resid, st)[0,1])
# Fit resid ~ poly(st) deg 5 and check residual std
coef=np.polyfit(st,resid,6); fit=np.polyval(coef,st)
print('std of SPL-amp around poly(st):', np.std(resid-fit))
print('raw std of SPL:', np.std(sp))
# Now include angle: fit resid ~ poly(st) + angle terms
from numpy.polynomial import polynomial as P
A=np.column_stack([st**i for i in range(7)]+[a, a**2, a*st, np.log10(c)])
coef2,_,_,_=np.linalg.lstsq(A, resid, rcond=None); fit2=A@coef2
print('std SPL-amp around poly(st)+angle+chord:', np.std(resid-fit2))
"corr(resid, st): -0.559860757030127 std of SPL-amp around poly(st): 9.634334032047443 raw std of SPL: 6.759248894490382 std SPL-amp around poly(st)+angle+chord: 7.151314914405019
cat /tmp/gptune.log; echo "---"; pgrep -af gptune.py | grep -v timeout || echo done
--- 708 python3 /tmp/gptune.py
sleep 120; cat /tmp/gptune.log; echo "---"; pgrep -af gptune.py | grep -v timeout || echo done
--- 708 python3 /tmp/gptune.py
sleep 100; cat /tmp/gptune.log; echo "---"; pgrep -af gptune.py | grep -v timeout || echo done
--- 708 python3 /tmp/gptune.py
sleep 60; cat /tmp/gptune.log; echo "---"; pgrep -af gptune.py | grep -v timeout || echo done
--- done
cd /app && cat > /tmp/battery.py << 'EOF'
import sys, warnings, time; sys.path.insert(0,'/tmp'); warnings.filterwarnings('ignore')
from harness import evaluate, FEATURES, TARGET, cond_key
import numpy as np, pandas as pd
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.ensemble import ExtraTreesRegressor, HistGradientBoostingRegressor, RandomForestRegressor
from sklearn.neural_network import MLPRegressor
from sklearn.neighbors import KNeighborsRegressor
NU=1.5e-5
tr=pd.read_csv('/app/data/train.csv'); va=pd.read_csv('/app/data/validation.csv'); y=tr[TARGET].values; yv=va[TARGET].values
feats=['log_f','angle','log_c','U','log_d','strouhal','str2','Re_c','dc']
def build(X):
f=X['frequency'].astype(float).values; a=X['attack_angle'].astype(float).values
c=X['chord_length'].astype(float).values; U=X['free_stream_velocity'].astype(float).values
dd=X['suction_side_displacement_thickness'].astype(float).values
b={'log_f':np.log10(f),'angle':a,'log_c':np.log10(c),'U':U,'log_d':np.log10(dd),
'strouhal':np.log10(f*dd/U),'str2':np.log10(f*dd/U)**2,'Re_c':np.log10(U*c/NU),'dc':np.log10(dd/c)}
return pd.DataFrame(b,index=X.index)[feats]
Xtr=build(tr[FEATURES]); Xva=build(va[FEATURES]); keys=cond_key(va)
def report(name,pred):
r=evaluate(va,pred); err=pred-yv; cr=np.array([np.sqrt(np.mean(err[keys==k]**2)) for k in np.unique(keys)])
print(f"{name:20s} rmse={r['rmse']:.3f} mae={r['mae']:.3f} p90={r['p90']:.3f} p95={r['p95']:.3f} max={r['mx']:.3f} under={int((cr<=2.35).sum())}/15",flush=True)
P={}
# MLP ensemble (5 seeds)
t=time.time(); mlps=[]
for s in range(5):
m=make_pipeline(StandardScaler(), MLPRegressor(hidden_layer_sizes=(200,150,100),activation='tanh',alpha=1e-3,max_iter=4000,early_stopping=True,n_iter_no_change=40,random_state=s))
m.fit(Xtr,y); mlps.append(m.predict(Xva))
P['MLP']=np.mean(mlps,0); report(f'MLP5({time.time()-t:.0f}s)',P['MLP'])
# KNN in standardized physics space
sc=StandardScaler().fit(Xtr); Xs=sc.transform(Xtr); Xvs=sc.transform(Xva)
for k in [8,15,25]:
kn=KNeighborsRegressor(n_neighbors=k,weights='distance').fit(Xs,y); P[f'KNN{k}']=kn.predict(Xvs); report(f'KNN{k}',P[f'KNN{k}'])
# ET, HGB, RF
P['ET']=ExtraTreesRegressor(n_estimators=800,n_jobs=-1,random_state=0).fit(Xtr,y).predict(Xva); report('ET',P['ET'])
P['HGB']=HistGradientBoostingRegressor(max_iter=700,learning_rate=0.04,max_leaf_nodes=31,random_state=0).fit(Xtr,y).predict(Xva); report('HGB',P['HGB'])
for n,p in P.items(): np.save(f'/tmp/val_{n}.npy',p)
EOF
nohup timeout 200 python3 /tmp/battery.py > /tmp/battery.log 2>&1 &
echo started; sleep 75; cat /tmp/battery.logstarted MLP5(38s) rmse=7.111 mae=5.996 p90=9.116 p95=9.362 max=9.736 under=0/15 KNN8 rmse=2.653 mae=1.980 p90=3.925 p95=4.055 max=4.338 under=9/15 KNN15 rmse=3.063 mae=2.277 p90=4.218 p95=4.408 max=4.813 under=5/15 KNN25 rmse=3.299 mae=2.487 p90=4.311 p95=4.566 max=4.812 under=5/15 ET rmse=2.279 mae=1.759 p90=3.069 p95=3.551 max=4.227 under=10/15 HGB rmse=2.635 mae=2.027 p90=3.429 p95=4.085 max=5.173 under=8/15
cd /app && cat > /tmp/gpens.py << 'EOF'
import sys, warnings, time; sys.path.insert(0,'/tmp'); warnings.filterwarnings('ignore')
from harness import evaluate, FEATURES, TARGET, cond_key
import numpy as np, pandas as pd
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.gaussian_process import GaussianProcessRegressor
from sklearn.gaussian_process.kernels import ConstantKernel as C, WhiteKernel, Matern
NU=1.5e-5
tr=pd.read_csv('/app/data/train.csv'); va=pd.read_csv('/app/data/validation.csv'); y=tr[TARGET].values; yv=va[TARGET].values
def build(X,feats):
f=X['frequency'].astype(float).values; a=X['attack_angle'].astype(float).values
c=X['chord_length'].astype(float).values; U=X['free_stream_velocity'].astype(float).values
dd=X['suction_side_displacement_thickness'].astype(float).values
b={'log_f':np.log10(f),'angle':a,'log_c':np.log10(c),'U':U,'log_d':np.log10(dd),'logU':np.log10(U),
'strouhal':np.log10(f*dd/U),'str2':np.log10(f*dd/U)**2,'Re_c':np.log10(U*c/NU),'Re_d':np.log10(U*dd/NU),'dc':np.log10(dd/c),'mach':U/340.0}
return pd.DataFrame(b,index=X.index)[feats]
keys=cond_key(va)
def report(name,pred):
r=evaluate(va,pred); err=pred-yv; cr=np.array([np.sqrt(np.mean(err[keys==k]**2)) for k in np.unique(keys)])
print(f"{name:22s} rmse={r['rmse']:.3f} mae={r['mae']:.3f} p90={r['p90']:.3f} p95={r['p95']:.3f} max={r['mx']:.3f} under={int((cr<=2.35).sum())}/15",flush=True)
def fit_gp(feats,nu=1.5):
Xt=build(tr[FEATURES],feats); Xv=build(va[FEATURES],feats); d=len(feats)
m=make_pipeline(StandardScaler(), GaussianProcessRegressor(kernel=C(1.0)*Matern(np.ones(d),nu=nu)+WhiteKernel(0.1,(1e-3,10)),normalize_y=True,n_restarts_optimizer=0,random_state=0,alpha=1e-8))
m.fit(Xt,y); return m.predict(Xv)
sets={
'E': ['log_f','angle','log_c','U','log_d','strouhal','str2','Re_c','dc'],
'S': ['strouhal','str2','angle','log_c','logU','Re_d','dc'],
'M': ['log_f','angle','log_c','mach','log_d','strouhal','Re_c'],
'F': ['log_f','angle','log_c','U','log_d','strouhal','Re_c','Re_d','dc','str2'],
}
preds={}
t=time.time()
for n,fs in sets.items():
preds[n]=fit_gp(fs); report(f'GP_{n}',preds[n]); np.save(f'/tmp/val_GP_{n}.npy',preds[n])
print('time',round(time.time()-t))
# ensembles
report('ens E+S+M', (preds['E']+preds['S']+preds['M'])/3)
report('ens E+S+M+F',(preds['E']+preds['S']+preds['M']+preds['F'])/4)
report('ens E+M',(preds['E']+preds['M'])/2)
report('ens E+S',(preds['E']+preds['S'])/2)
pe=np.load('/tmp/val_ET.npy')
ens=(preds['E']+preds['S']+preds['M'])/3
for w in [0.15,0.2,0.25]:
report(f'ens*{1-w:.2f}+ET*{w}',(1-w)*ens+w*pe)
EOF
nohup timeout 200 python3 /tmp/gpens.py > /tmp/gpens.log 2>&1 &
echo started; sleep 90; cat /tmp/gpens.logstarted GP_E rmse=1.868 mae=1.392 p90=2.770 p95=3.303 max=3.906 under=12/15 GP_S rmse=2.005 mae=1.504 p90=3.144 p95=4.033 max=4.444 under=13/15 GP_M rmse=1.869 mae=1.377 p90=2.853 p95=3.389 max=3.906 under=12/15
sleep 45; cat /tmp/gpens.log; echo "---"; pgrep -af gpens.py | grep -v timeout || echo done
GP_E rmse=1.868 mae=1.392 p90=2.770 p95=3.303 max=3.906 under=12/15 GP_S rmse=2.005 mae=1.504 p90=3.144 p95=4.033 max=4.444 under=13/15 GP_M rmse=1.869 mae=1.377 p90=2.853 p95=3.389 max=3.906 under=12/15 GP_F rmse=1.868 mae=1.392 p90=2.770 p95=3.303 max=3.906 under=12/15 time 131 ens E+S+M rmse=1.805 mae=1.382 p90=2.497 p95=3.064 max=4.051 under=13/15 ens E+S+M+F rmse=1.813 mae=1.381 p90=2.537 p95=3.121 max=4.012 under=13/15 ens E+M rmse=1.867 mae=1.384 p90=2.811 p95=3.346 max=3.906 under=12/15 ens E+S rmse=1.824 mae=1.403 p90=2.586 p95=3.142 max=4.139 under=12/15 ens*0.85+ET*0.15 rmse=1.811 mae=1.388 p90=2.439 p95=3.049 max=4.039 under=13/15 ens*0.80+ET*0.2 rmse=1.819 mae=1.392 p90=2.424 p95=3.047 max=4.038 under=13/15 ens*0.75+ET*0.25 rmse=1.829 mae=1.397 p90=2.412 p95=3.047 max=4.038 under=13/15 --- done
cd /app && cat > /tmp/cv_ens.py << 'EOF'
import sys, warnings, time; sys.path.insert(0,'/tmp'); warnings.filterwarnings('ignore')
from harness import TARGET, cond_key
import numpy as np, pandas as pd
from sklearn.model_selection import GroupKFold
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.ensemble import ExtraTreesRegressor
from sklearn.gaussian_process import GaussianProcessRegressor
from sklearn.gaussian_process.kernels import ConstantKernel as C, WhiteKernel, Matern
FEATURES=["frequency","attack_angle","chord_length","free_stream_velocity","suction_side_displacement_thickness"]
NU=1.5e-5
df=pd.concat([pd.read_csv('/app/data/train.csv'),pd.read_csv('/app/data/validation.csv')],ignore_index=True)
y=df[TARGET].values; keys=cond_key(df)
def build(X,feats):
f=X['frequency'].astype(float).values; a=X['attack_angle'].astype(float).values
c=X['chord_length'].astype(float).values; U=X['free_stream_velocity'].astype(float).values
dd=X['suction_side_displacement_thickness'].astype(float).values
b={'log_f':np.log10(f),'angle':a,'log_c':np.log10(c),'U':U,'log_d':np.log10(dd),'logU':np.log10(U),
'strouhal':np.log10(f*dd/U),'str2':np.log10(f*dd/U)**2,'Re_c':np.log10(U*c/NU),'Re_d':np.log10(U*dd/NU),'dc':np.log10(dd/c),'mach':U/340.0}
return pd.DataFrame(b,index=X.index)[feats]
sets={'E':['log_f','angle','log_c','U','log_d','strouhal','str2','Re_c','dc'],
'S':['strouhal','str2','angle','log_c','logU','Re_d','dc'],
'M':['log_f','angle','log_c','mach','log_d','strouhal','Re_c']}
def mkgp(d): return make_pipeline(StandardScaler(), GaussianProcessRegressor(kernel=C(1.0)*Matern(np.ones(d),nu=1.5)+WhiteKernel(0.1,(1e-3,10)),normalize_y=True,n_restarts_optimizer=0,random_state=0,alpha=1e-8))
gkf=GroupKFold(n_splits=5)
oof={n:np.zeros(len(df)) for n in list(sets)+['ET']}
Xe=build(df[FEATURES],sets['E'])
t=time.time()
for tri,tei in gkf.split(df,y,groups=keys):
for n,fs in sets.items():
Xb=build(df[FEATURES],fs); m=mkgp(len(fs)); m.fit(Xb.iloc[tri],y[tri]); oof[n][tei]=m.predict(Xb.iloc[tei])
et=ExtraTreesRegressor(n_estimators=600,n_jobs=-1,random_state=0).fit(Xe.iloc[tri],y[tri]); oof['ET'][tei]=et.predict(Xe.iloc[tei])
print('cv time',round(time.time()-t))
def report(name,pred):
err=pred-y; cr=np.array([np.sqrt(np.mean(err[keys==k]**2)) for k in np.unique(keys)])
print(f"{name:18s} rmse={np.sqrt(np.mean(err**2)):.3f} mae={np.mean(np.abs(err)):.3f} p90={np.percentile(cr,90):.3f} p95={np.percentile(cr,95):.3f} max={cr.max():.3f} under={int((cr<=2.35).sum())}/{len(cr)}",flush=True)
ens=(oof['E']+oof['S']+oof['M'])/3
report('GP_E',oof['E']); report('GP_ens',ens)
for w in [0.15,0.2,0.25]:
report(f'ens*{1-w:.2f}+ET*{w}',(1-w)*ens+w*oof['ET'])
for n in oof: np.save(f'/tmp/cvoof_{n}.npy',oof[n])
EOF
nohup timeout 295 python3 /tmp/cv_ens.py > /tmp/cv_ens.log 2>&1 &
echo started; sleep 120; cat /tmp/cv_ens.logstarted
sleep 120; cat /tmp/cv_ens.log; echo "---"; pgrep -af cv_ens.py | grep -v timeout || echo done
--- 808 python3 /tmp/cv_ens.py
sleep 60; cat /tmp/cv_ens.log; echo "---"; pgrep -af cv_ens.py | grep -v timeout || echo done
--- done
cd /app && cat > /tmp/cv_ens2.py << 'EOF'
import sys, warnings, time; sys.path.insert(0,'/tmp'); warnings.filterwarnings('ignore')
from harness import TARGET, cond_key
import numpy as np, pandas as pd
from sklearn.model_selection import GroupKFold
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.ensemble import ExtraTreesRegressor
from sklearn.gaussian_process import GaussianProcessRegressor
from sklearn.gaussian_process.kernels import ConstantKernel as C, WhiteKernel, Matern
FEATURES=["frequency","attack_angle","chord_length","free_stream_velocity","suction_side_displacement_thickness"]
NU=1.5e-5
df=pd.concat([pd.read_csv('/app/data/train.csv'),pd.read_csv('/app/data/validation.csv')],ignore_index=True)
y=df[TARGET].values; keys=cond_key(df)
def build(X,feats):
f=X['frequency'].astype(float).values; a=X['attack_angle'].astype(float).values
c=X['chord_length'].astype(float).values; U=X['free_stream_velocity'].astype(float).values
dd=X['suction_side_displacement_thickness'].astype(float).values
b={'log_f':np.log10(f),'angle':a,'log_c':np.log10(c),'U':U,'log_d':np.log10(dd),'logU':np.log10(U),
'strouhal':np.log10(f*dd/U),'str2':np.log10(f*dd/U)**2,'Re_c':np.log10(U*c/NU),'Re_d':np.log10(U*dd/NU),'dc':np.log10(dd/c),'mach':U/340.0}
return pd.DataFrame(b,index=X.index)[feats]
sets={'E':['log_f','angle','log_c','U','log_d','strouhal','str2','Re_c','dc'],
'S':['strouhal','str2','angle','log_c','logU','Re_d','dc'],
'M':['log_f','angle','log_c','mach','log_d','strouhal','Re_c']}
Xmats={n:build(df[FEATURES],fs).values for n,fs in sets.items()}
def mkgp(d): return make_pipeline(StandardScaler(), GaussianProcessRegressor(kernel=C(1.0)*Matern(np.ones(d),nu=1.5)+WhiteKernel(0.1,(1e-3,10)),normalize_y=True,n_restarts_optimizer=0,random_state=0,alpha=1e-8))
gkf=GroupKFold(n_splits=4)
oof={n:np.zeros(len(df)) for n in list(sets)+['ET']}
t=time.time()
for tri,tei in gkf.split(df,y,groups=keys):
for n in sets:
X=Xmats[n]; m=mkgp(X.shape[1]); m.fit(X[tri],y[tri]); oof[n][tei]=m.predict(X[tei])
et=ExtraTreesRegressor(n_estimators=600,n_jobs=-1,random_state=0).fit(Xmats['E'][tri],y[tri]); oof['ET'][tei]=et.predict(Xmats['E'][tei])
print('cv time',round(time.time()-t),'nconds',len(np.unique(keys)))
def report(name,pred):
err=pred-y; cr=np.array([np.sqrt(np.mean(err[keys==k]**2)) for k in np.unique(keys)])
print(f"{name:18s} rmse={np.sqrt(np.mean(err**2)):.3f} mae={np.mean(np.abs(err)):.3f} p90={np.percentile(cr,90):.3f} p95={np.percentile(cr,95):.3f} max={cr.max():.3f} under={int((cr<=2.35).sum())}/{len(cr)}",flush=True)
ens=(oof['E']+oof['S']+oof['M'])/3
report('GP_E',oof['E']); report('GP_ens',ens)
for w in [0.15,0.2,0.25]:
report(f'ens*{1-w:.2f}+ET*{w}',(1-w)*ens+w*oof['ET'])
EOF
nohup timeout 295 python3 /tmp/cv_ens2.py > /tmp/cv_ens2.log 2>&1 &
echo started; sleep 200; cat /tmp/cv_ens2.log; echo "---"; pgrep -af cv_ens2.py | grep -v timeout || echo donestarted --- 870 python3 /tmp/cv_ens2.py
sleep 40; cat /tmp/cv_ens2.log; echo "---"; pgrep -af cv_ens2.py | grep -v timeout || echo done
--- 870 python3 /tmp/cv_ens2.py
sleep 45; cat /tmp/cv_ens2.log; echo "---"; pgrep -af cv_ens2.py | grep -v timeout || echo done
--- 870 python3 /tmp/cv_ens2.py
sleep 40; cat /tmp/cv_ens2.log; echo "---"; pgrep -af cv_ens2.py | grep -v timeout || echo done
--- done
cd /app && sed 's/n_splits=4/n_splits=3/' /tmp/cv_ens2.py > /tmp/cv_ens3.py nohup timeout 295 python3 /tmp/cv_ens3.py > /tmp/cv_ens3.log 2>&1 & echo started; sleep 220; cat /tmp/cv_ens3.log; echo "---"; pgrep -af cv_ens3.py | grep -v timeout || echo done
started cv time 189 nconds 82 GP_E rmse=2.452 mae=1.698 p90=4.021 p95=4.335 max=5.709 under=54/82 GP_ens rmse=2.416 mae=1.677 p90=3.944 p95=4.263 max=5.938 under=53/82 ens*0.85+ET*0.15 rmse=2.333 mae=1.620 p90=3.872 p95=4.166 max=5.786 under=57/82 ens*0.80+ET*0.2 rmse=2.313 mae=1.608 p90=3.862 p95=4.223 max=5.742 under=59/82 ens*0.75+ET*0.25 rmse=2.296 mae=1.599 p90=3.843 p95=4.291 max=5.703 under=59/82 --- done
cd /app && python3 -c "
import numpy as np, pandas as pd, sys, glob; sys.path.insert(0,'/tmp')
from harness import cond_key, TARGET, evaluate
va=pd.read_csv('/app/data/validation.csv'); yv=va[TARGET].values; keys=cond_key(va)
P={}
for f in glob.glob('/tmp/val_*.npy'):
P[f.split('val_')[1][:-4]]=np.load(f)
print('available:', sorted(P))
def rep(name,pred):
r=evaluate(va,pred); err=pred-yv; cr=np.array([np.sqrt(np.mean(err[keys==k]**2)) for k in np.unique(keys)])
print(f'{name:32s} rmse={r[\"rmse\"]:.3f} mae={r[\"mae\"]:.3f} p90={r[\"p90\"]:.3f} p95={r[\"p95\"]:.3f} max={r[\"mx\"]:.3f} under={int((cr<=2.35).sum())}/15')
gpens=(P['GP_E']+P['GP_S']+P['GP_M'])/3
rep('GPens(E,S,M)',gpens)
rep('GPens+ET .8/.2',0.8*gpens+0.2*P['ET'])
# add GP_F
gpens4=(P['GP_E']+P['GP_S']+P['GP_M']+P['GP_F'])/4
rep('GPens4+ET .8/.2',0.8*gpens4+0.2*P['ET'])
# add KNN8 small weight
rep('.72GPe+.18ET+.10KNN',0.72*gpens+0.18*P['ET']+0.10*P['KNN8'])
rep('.7GPe+.2ET+.1KNN',0.7*gpens+0.2*P['ET']+0.1*P['KNN8'])
rep('.75GPe+.15ET+.10KNN',0.75*gpens+0.15*P['ET']+0.10*P['KNN8'])
# ET+HGB tree blend
tree=0.6*P['ET']+0.4*P['HGB']
rep('.8GPe+.2(ET,HGB)',0.8*gpens+0.2*tree)
"available: ['ET', 'GP_E', 'GP_F', 'GP_M', 'GP_S', 'HGB', 'KNN15', 'KNN25', 'KNN8', 'MLP'] GPens(E,S,M) rmse=1.805 mae=1.382 p90=2.497 p95=3.064 max=4.051 under=13/15 GPens+ET .8/.2 rmse=1.819 mae=1.392 p90=2.424 p95=3.047 max=4.038 under=13/15 GPens4+ET .8/.2 rmse=1.819 mae=1.390 p90=2.436 p95=3.090 max=4.002 under=13/15 .72GPe+.18ET+.10KNN rmse=1.837 mae=1.397 p90=2.403 p95=3.068 max=3.994 under=13/15 .7GPe+.2ET+.1KNN rmse=1.843 mae=1.402 p90=2.413 p95=3.069 max=3.995 under=13/15 .75GPe+.15ET+.10KNN rmse=1.830 mae=1.392 p90=2.394 p95=3.066 max=3.993 under=13/15 .8GPe+.2(ET,HGB) rmse=1.831 mae=1.403 p90=2.462 p95=3.078 max=4.069 under=13/15
cd /app && cat > /tmp/biggpens.py << 'EOF'
import sys, warnings, time; sys.path.insert(0,'/tmp'); warnings.filterwarnings('ignore')
from harness import evaluate, FEATURES, TARGET, cond_key
import numpy as np, pandas as pd
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.gaussian_process import GaussianProcessRegressor
from sklearn.gaussian_process.kernels import ConstantKernel as C, WhiteKernel, Matern
NU=1.5e-5
tr=pd.read_csv('/app/data/train.csv'); va=pd.read_csv('/app/data/validation.csv'); y=tr[TARGET].values; yv=va[TARGET].values
def build(X,feats):
f=X['frequency'].astype(float).values; a=X['attack_angle'].astype(float).values
c=X['chord_length'].astype(float).values; U=X['free_stream_velocity'].astype(float).values
dd=X['suction_side_displacement_thickness'].astype(float).values
b={'log_f':np.log10(f),'angle':a,'log_c':np.log10(c),'U':U,'log_d':np.log10(dd),'logU':np.log10(U),
'strouhal':np.log10(f*dd/U),'str2':np.log10(f*dd/U)**2,'Re_c':np.log10(U*c/NU),'Re_d':np.log10(U*dd/NU),'dc':np.log10(dd/c),'mach':U/340.0}
return pd.DataFrame(b,index=X.index)[feats]
keys=cond_key(va)
def rep(name,pred):
r=evaluate(va,pred); err=pred-yv; cr=np.array([np.sqrt(np.mean(err[keys==k]**2)) for k in np.unique(keys)])
print(f'{name:26s} rmse={r["rmse"]:.3f} mae={r["mae"]:.3f} p90={r["p90"]:.3f} p95={r["p95"]:.3f} max={r["mx"]:.3f} under={int((cr<=2.35).sum())}/15',flush=True)
def gp(feats,nu=1.5,wk=0.1):
Xt=build(tr[FEATURES],feats); Xv=build(va[FEATURES],feats); d=len(feats)
m=make_pipeline(StandardScaler(), GaussianProcessRegressor(kernel=C(1.0)*Matern(np.ones(d),nu=nu)+WhiteKernel(wk,(1e-3,10)),normalize_y=True,n_restarts_optimizer=0,random_state=0,alpha=1e-8))
m.fit(Xt,y); return m.predict(Xv)
sets={
'E':['log_f','angle','log_c','U','log_d','strouhal','str2','Re_c','dc'],
'S':['strouhal','str2','angle','log_c','logU','Re_d','dc'],
'M':['log_f','angle','log_c','mach','log_d','strouhal','Re_c'],
'core':['log_f','angle','log_c','U','log_d','strouhal'],
'R':['log_f','angle','log_c','U','log_d','strouhal','Re_c','Re_d'],
'D':['strouhal','angle','log_c','U','log_d','dc','Re_c'],
}
P={}; t=time.time()
for n,fs in sets.items(): P[n]=gp(fs); np.save(f'/tmp/val_GPx_{n}.npy',P[n])
P['E25']=gp(sets['E'],nu=2.5); np.save('/tmp/val_GPx_E25.npy',P['E25'])
print('time',round(time.time()-t))
rep('ens6',sum(P[n] for n in sets)/len(sets))
rep('ens E,S,M,R,D',(P['E']+P['S']+P['M']+P['R']+P['D'])/5)
rep('ens +E25',(P['E']+P['S']+P['M']+P['R']+P['D']+P['E25'])/6)
et=np.load('/tmp/val_ET.npy'); kn=np.load('/tmp/val_KNN8.npy')
big=(P['E']+P['S']+P['M']+P['R']+P['D']+P['E25'])/6
rep('big*.8+ET*.2',0.8*big+0.2*et)
rep('big*.75+ET*.15+KNN*.10',0.75*big+0.15*et+0.10*kn)
EOF
nohup timeout 290 python3 /tmp/biggpens.py > /tmp/biggpens.log 2>&1 &
echo started; sleep 150; cat /tmp/biggpens.logstarted
sleep 70; cat /tmp/biggpens.log; echo "---"; pgrep -af biggpens.py | grep -v timeout || echo done
time 180 ens6 rmse=1.819 mae=1.393 p90=2.520 p95=3.047 max=4.014 under=12/15 ens E,S,M,R,D rmse=1.821 mae=1.399 p90=2.493 p95=2.985 max=4.043 under=12/15 ens +E25 rmse=1.828 mae=1.405 p90=2.508 p95=3.033 max=4.021 under=12/15 big*.8+ET*.2 rmse=1.836 mae=1.411 p90=2.435 p95=3.022 max=4.011 under=13/15 big*.75+ET*.15+KNN*.10 rmse=1.846 mae=1.408 p90=2.405 p95=3.043 max=3.968 under=13/15 --- done
cd /app && python3 -c "
import numpy as np, pandas as pd, sys; sys.path.insert(0,'/tmp')
from harness import cond_key, snap, VEL, CHORD
tr=pd.read_csv('/app/data/train.csv'); va=pd.read_csv('/app/data/validation.csv')
for name,df in [('train',tr),('val',va)]:
ch=snap(df['chord_length'].values,CHORD)
print(name,'chord distribution:',{round(c,4):int((ch==c).sum()) for c in CHORD})
# training conditions at smallest chord
trc=snap(tr['chord_length'].values,CHORD); m=trc==0.0254
sub=tr[m]
k=cond_key(sub)
print()
print('train conditions at chord 0.0254:', len(np.unique(k)))
vel=snap(sub['free_stream_velocity'].values,VEL)
import collections
print(' by velocity:', dict(collections.Counter(np.round(vel,1))))
print(' angle range:', round(sub['attack_angle'].min(),1), round(sub['attack_angle'].max(),1))
print(' angles present:', sorted(set(np.round(sub['attack_angle'].values,0))))
"train chord distribution: {0.0254: 174, 0.0508: 160, 0.1016: 198, 0.1524: 139, 0.2286: 178, 0.3048: 107}
val chord distribution: {0.0254: 61, 0.0508: 25, 0.1016: 52, 0.1524: 30, 0.2286: 15, 0.3048: 18}
train conditions at chord 0.0254: 12
by velocity: {31.7: 57, 39.6: 32, 55.5: 40, 71.3: 45}
angle range: -0.0 22.2
angles present: [0.0, 5.0, 9.0, 10.0, 13.0, 17.0, 22.0]cd /app && cat > /tmp/multisplit.py << 'EOF'
import sys, warnings, time, os; sys.path.insert(0,'/tmp'); warnings.filterwarnings('ignore')
from harness import TARGET, cond_key
import numpy as np, pandas as pd
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.ensemble import ExtraTreesRegressor
from sklearn.neighbors import KNeighborsRegressor
from sklearn.gaussian_process import GaussianProcessRegressor
from sklearn.gaussian_process.kernels import ConstantKernel as C, WhiteKernel, Matern
FEATURES=["frequency","attack_angle","chord_length","free_stream_velocity","suction_side_displacement_thickness"]
NU=1.5e-5
df=pd.concat([pd.read_csv('/app/data/train.csv'),pd.read_csv('/app/data/validation.csv')],ignore_index=True)
y=df[TARGET].values; keys=cond_key(df); uniq=np.unique(keys)
def build(X,feats):
f=X['frequency'].astype(float).values; a=X['attack_angle'].astype(float).values
c=X['chord_length'].astype(float).values; U=X['free_stream_velocity'].astype(float).values
dd=X['suction_side_displacement_thickness'].astype(float).values
b={'log_f':np.log10(f),'angle':a,'log_c':np.log10(c),'U':U,'log_d':np.log10(dd),'logU':np.log10(U),
'strouhal':np.log10(f*dd/U),'str2':np.log10(f*dd/U)**2,'Re_c':np.log10(U*c/NU),'Re_d':np.log10(U*dd/NU),'dc':np.log10(dd/c),'mach':U/340.0}
return pd.DataFrame(b,index=X.index)[feats].values
sets={'E':['log_f','angle','log_c','U','log_d','strouhal','str2','Re_c','dc'],
'S':['strouhal','str2','angle','log_c','logU','Re_d','dc'],
'M':['log_f','angle','log_c','mach','log_d','strouhal','Re_c']}
Xm={n:build(df[FEATURES],fs) for n,fs in sets.items()}
def mkgp(d): return make_pipeline(StandardScaler(), GaussianProcessRegressor(kernel=C(1.0)*Matern(np.ones(d),nu=1.5)+WhiteKernel(0.1,(1e-3,10)),normalize_y=True,n_restarts_optimizer=0,random_state=0,alpha=1e-8))
def run_seed(seed):
rng=np.random.RandomState(seed); test_conds=set(rng.choice(uniq,15,replace=False))
te=np.array([k in test_conds for k in keys]); tri=~te
preds={}
for n in sets:
X=Xm[n]; m=mkgp(X.shape[1]); m.fit(X[tri],y[tri]); preds[n]=m.predict(X[te])
gpens=(preds['E']+preds['S']+preds['M'])/3
et=ExtraTreesRegressor(n_estimators=600,n_jobs=-1,random_state=0).fit(Xm['E'][tri],y[tri]).predict(Xm['E'][te])
sc=StandardScaler().fit(Xm['E'][tri]); kn=KNeighborsRegressor(8,weights='distance').fit(sc.transform(Xm['E'][tri]),y[tri]).predict(sc.transform(Xm['E'][te]))
blend=0.75*gpens+0.15*et+0.10*kn
yt=y[te]; ek=keys[te]
def metrics(p):
err=p-yt; cr=np.array([np.sqrt(np.mean(err[ek==k]**2)) for k in np.unique(ek)])
return np.sqrt(np.mean(err**2)),np.mean(np.abs(err)),np.percentile(cr,90),np.percentile(cr,95),cr.max()
return metrics(gpens),metrics(blend)
seeds=[int(s) for s in sys.argv[1:]]
for s in seeds:
t=time.time(); mg,mb=run_seed(s)
line=f"seed{s} GPens rmse={mg[0]:.3f} mae={mg[1]:.3f} p90={mg[2]:.3f} p95={mg[3]:.3f} max={mg[4]:.3f} | BLEND rmse={mb[0]:.3f} mae={mb[1]:.3f} p90={mb[2]:.3f} p95={mb[3]:.3f} max={mb[4]:.3f} ({time.time()-t:.0f}s)"
print(line,flush=True); open('/tmp/multisplit_results.txt','a').write(line+'\n')
EOF
nohup timeout 290 python3 /tmp/multisplit.py 1 2 3 4 > /tmp/ms1.log 2>&1 &
echo started; sleep 200; cat /tmp/ms1.logstarted seed1 GPens rmse=2.131 mae=1.495 p90=3.502 p95=4.033 max=4.611 | BLEND rmse=2.080 mae=1.458 p90=3.080 p95=3.903 max=4.927 (84s) seed2 GPens rmse=1.680 mae=1.280 p90=2.567 p95=2.649 max=2.811 | BLEND rmse=1.694 mae=1.335 p90=2.256 p95=2.415 max=2.780 (89s)
sleep 30; cat /tmp/ms1.log; echo "=== launching more seeds ==="; cd /app && nohup timeout 295 python3 /tmp/multisplit.py 5 6 7 8 9 10 > /tmp/ms2.log 2>&1 & echo started
seed1 GPens rmse=2.131 mae=1.495 p90=3.502 p95=4.033 max=4.611 | BLEND rmse=2.080 mae=1.458 p90=3.080 p95=3.903 max=4.927 (84s) seed2 GPens rmse=1.680 mae=1.280 p90=2.567 p95=2.649 max=2.811 | BLEND rmse=1.694 mae=1.335 p90=2.256 p95=2.415 max=2.780 (89s) === launching more seeds === started
sleep 30; cat /tmp/ms1.log | tail -2; echo "---seed3,4 from ms1 should be above; ms2:---"; sleep 0; cat /tmp/ms2.log
seed1 GPens rmse=2.131 mae=1.495 p90=3.502 p95=4.033 max=4.611 | BLEND rmse=2.080 mae=1.458 p90=3.080 p95=3.903 max=4.927 (84s) seed2 GPens rmse=1.680 mae=1.280 p90=2.567 p95=2.649 max=2.811 | BLEND rmse=1.694 mae=1.335 p90=2.256 p95=2.415 max=2.780 (89s) ---seed3,4 from ms1 should be above; ms2:---
sleep 90; echo "=== cumulative results ==="; cat /tmp/multisplit_results.txt 2>/dev/null; echo "=== ms2 ==="; cat /tmp/ms2.log
=== cumulative results === seed1 GPens rmse=2.131 mae=1.495 p90=3.502 p95=4.033 max=4.611 | BLEND rmse=2.080 mae=1.458 p90=3.080 p95=3.903 max=4.927 (84s) seed2 GPens rmse=1.680 mae=1.280 p90=2.567 p95=2.649 max=2.811 | BLEND rmse=1.694 mae=1.335 p90=2.256 p95=2.415 max=2.780 (89s) seed5 GPens rmse=2.238 mae=1.607 p90=3.247 p95=3.698 max=4.282 | BLEND rmse=2.042 mae=1.489 p90=2.912 p95=3.363 max=3.795 (111s) === ms2 === seed5 GPens rmse=2.238 mae=1.607 p90=3.247 p95=3.698 max=4.282 | BLEND rmse=2.042 mae=1.489 p90=2.912 p95=3.363 max=3.795 (111s)
cd /app && cat > /tmp/cache_seeds.py << 'EOF'
import sys, warnings, time, os; sys.path.insert(0,'/tmp'); warnings.filterwarnings('ignore')
from harness import TARGET, cond_key
import numpy as np, pandas as pd
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.ensemble import ExtraTreesRegressor, HistGradientBoostingRegressor
from sklearn.neighbors import KNeighborsRegressor
from sklearn.gaussian_process import GaussianProcessRegressor
from sklearn.gaussian_process.kernels import ConstantKernel as C, WhiteKernel, Matern
FEATURES=["frequency","attack_angle","chord_length","free_stream_velocity","suction_side_displacement_thickness"]
NU=1.5e-5
df=pd.concat([pd.read_csv('/app/data/train.csv'),pd.read_csv('/app/data/validation.csv')],ignore_index=True)
y=df[TARGET].values; keys=cond_key(df); uniq=np.unique(keys)
def build(X,feats):
f=X['frequency'].astype(float).values; a=X['attack_angle'].astype(float).values
c=X['chord_length'].astype(float).values; U=X['free_stream_velocity'].astype(float).values
dd=X['suction_side_displacement_thickness'].astype(float).values
b={'log_f':np.log10(f),'angle':a,'log_c':np.log10(c),'U':U,'log_d':np.log10(dd),'logU':np.log10(U),
'strouhal':np.log10(f*dd/U),'str2':np.log10(f*dd/U)**2,'Re_c':np.log10(U*c/NU),'Re_d':np.log10(U*dd/NU),'dc':np.log10(dd/c),'mach':U/340.0}
return pd.DataFrame(b,index=X.index)[feats].values
sets={'E':['log_f','angle','log_c','U','log_d','strouhal','str2','Re_c','dc'],
'S':['strouhal','str2','angle','log_c','logU','Re_d','dc'],
'M':['log_f','angle','log_c','mach','log_d','strouhal','Re_c']}
Xm={n:build(df[FEATURES],fs) for n,fs in sets.items()}
def mkgp(d): return make_pipeline(StandardScaler(), GaussianProcessRegressor(kernel=C(1.0)*Matern(np.ones(d),nu=1.5)+WhiteKernel(0.1,(1e-3,10)),normalize_y=True,n_restarts_optimizer=0,random_state=0,alpha=1e-8))
for s in [int(x) for x in sys.argv[1:]]:
fn=f'/tmp/seedcache_{s}.npz'
if os.path.exists(fn): continue
t=time.time(); rng=np.random.RandomState(s); tc=set(rng.choice(uniq,15,replace=False))
te=np.array([k in tc for k in keys]); tri=~te
comp={}
for n in sets:
X=Xm[n]; m=mkgp(X.shape[1]); m.fit(X[tri],y[tri]); comp[n]=m.predict(X[te])
comp['ET']=ExtraTreesRegressor(n_estimators=600,n_jobs=-1,random_state=0).fit(Xm['E'][tri],y[tri]).predict(Xm['E'][te])
comp['HGB']=HistGradientBoostingRegressor(max_iter=600,learning_rate=0.05,random_state=0).fit(Xm['E'][tri],y[tri]).predict(Xm['E'][te])
sc=StandardScaler().fit(Xm['E'][tri]); comp['KNN']=KNeighborsRegressor(8,weights='distance').fit(sc.transform(Xm['E'][tri]),y[tri]).predict(sc.transform(Xm['E'][te]))
np.savez(fn, yt=y[te], ek=keys[te], **comp)
print(f'seed{s} cached ({time.time()-t:.0f}s)',flush=True)
EOF
nohup timeout 295 python3 /tmp/cache_seeds.py 1 2 3 5 6 7 8 9 10 11 12 13 14 15 > /tmp/cache.log 2>&1 &
echo started; sleep 230; cat /tmp/cache.logstarted seed1 cached (137s)
sleep 60; cat /tmp/cache.log; ls /tmp/seedcache_*.npz 2>/dev/null | wc -l
seed1 cached (137s) seed2 cached (90s) 2
command: create
path: /app/train_and_predict.py
file_text: #!/usr/bin/env python3
"""Airfoil self-noise surrogate.
Strategy
--------
The hidden evaluation holds out *complete aerodynamic condition groups*
``(attack_angle, chord_length, free_stream_velocity)``, so the model must
generalize across unseen physical regimes rather than interpolate between
nearby rows. Scoring rewards low global error *and* controlled per-condition
failures (p90/p95/max condition RMSE).
The workhorse is an ensemble of ARD-Matern Gaussian Processes built on
physics-informed coordinates (log-frequency, Strouhal number, Reynolds
numbers, ...). GPs extrapolate the smooth self-noise physics across held-out
conditions far better than trees. We average several GPs over different
feature parameterizations (diversity cancels per-condition errors) and blend
in an ExtraTrees model (sharp local frequency structure) plus a small
k-NN term (local spectral shape). All components are trained only on the
provided labeled public data.
"""
from __future__ import annotations
import warnings
from pathlib import Path
import numpy as np
import pandas as pd
warnings.filterwarnings("ignore")
from sklearn.ensemble import ExtraTreesRegressor
from sklearn.gaussian_process import GaussianProcessRegressor
from sklearn.gaussian_process.kernels import ConstantKernel as C
from sklearn.gaussian_process.kernels import Matern, WhiteKernel
from sklearn.neighbors import KNeighborsRegressor
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
DATA_DIR = Path("/app/data")
OUTPUT_DIR = Path("/app/output")
TARGET = "scaled_sound_pressure_level"
PREDICTION = "predicted_scaled_sound_pressure_level"
FEATURES = [
"frequency",
"attack_angle",
"chord_length",
"free_stream_velocity",
"suction_side_displacement_thickness",
]
NU_AIR = 1.5e-5 # kinematic viscosity of air (m^2/s)
# Diverse physics-informed coordinate systems for the GP ensemble.
FEATURE_SETS = {
"E": ["log_f", "angle", "log_c", "U", "log_d", "strouhal", "str2", "Re_c", "dc"],
"S": ["strouhal", "str2", "angle", "log_c", "logU", "Re_d", "dc"],
"M": ["log_f", "angle", "log_c", "mach", "log_d", "strouhal", "Re_c"],
}
# Blend weights (tuned via grouped holdouts that mimic the hidden split).
W_GP_ENSEMBLE = 0.75
W_EXTRATREES = 0.15
W_KNN = 0.10
def build_features(frame: pd.DataFrame, names) -> np.ndarray:
"""Construct physics-informed coordinates, guarding against bad inputs."""
f = np.asarray(frame["frequency"], dtype=float)
a = np.asarray(frame["attack_angle"], dtype=float)
c = np.asarray(frame["chord_length"], dtype=float)
U = np.asarray(frame["free_stream_velocity"], dtype=float)
d = np.asarray(frame["suction_side_displacement_thickness"], dtype=float)
# Physical quantities are strictly positive; clip to keep logs finite even
# if the hidden feature rows contain degenerate values.
eps = 1e-12
f = np.clip(f, eps, None)
c = np.clip(c, eps, None)
U = np.clip(U, eps, None)
d = np.clip(d, eps, None)
cols = {
"log_f": np.log10(f),
"angle": a,
"log_c": np.log10(c),
"U": U,
"log_d": np.log10(d),
"logU": np.log10(U),
"strouhal": np.log10(f * d / U),
"str2": np.log10(f * d / U) ** 2,
"Re_c": np.log10(U * c / NU_AIR),
"Re_d": np.log10(U * d / NU_AIR),
"dc": np.log10(d / c),
"mach": U / 340.0,
}
return np.column_stack([cols[n] for n in names])
def make_gp(n_dims: int) -> GaussianProcessRegressor:
kernel = (
C(1.0, (1e-3, 1e3))
* Matern(length_scale=np.ones(n_dims), length_scale_bounds=(1e-2, 1e5), nu=1.5)
+ WhiteKernel(0.1, (1e-3, 10.0))
)
return make_pipeline(
StandardScaler(),
GaussianProcessRegressor(
kernel=kernel,
normalize_y=True,
n_restarts_optimizer=0,
alpha=1e-8,
random_state=0,
…[truncated]Existing file was successfully overwritten at /app/train_and_predict.py
cat /tmp/cache.log; echo "cached seeds:"; ls /tmp/seedcache_*.npz 2>/dev/null | wc -l; echo "=== run final script ==="; cd /app && rm -f output/predictions.csv && time python3 train_and_predict.py 2>&1 | tail -5; echo "=== output ==="; head -5 output/predictions.csv; wc -l output/predictions.csv
seed1 cached (137s) seed2 cached (90s) cached seeds: 2 === run final script === === output === sample_id,predicted_scaled_sound_pressure_level af_val_0000,115.97816439570418 af_val_0001,124.11855900523959 af_val_0002,133.44869104184576 af_val_0003,124.00879903563536 41 output/predictions.csv [stderr] real 2m21.990s user 2m5.790s sys 0m10.700s
pgrep -af cache_seeds | grep -v timeout || echo "no cache running"; cd /app && nohup timeout 295 python3 /tmp/cache_seeds.py 3 5 6 7 8 9 10 11 12 > /tmp/cache2.log 2>&1 & echo relaunched
no cache running relaunched
cd /app && cat > /tmp/analyze.py << 'EOF'
import numpy as np, glob, sys
files=sorted(glob.glob('/tmp/seedcache_*.npz'))
seeds=[np.load(f) for f in files]
print(f'{len(seeds)} seeds:', [f.split("_")[1][:-4] for f in files])
def cond_rmse(p, yt, ek):
err=p-yt; return np.array([np.sqrt(np.mean(err[ek==k]**2)) for k in np.unique(ek)])
def eval_blend(wgp, wet, wknn):
agg=[]
for s in seeds:
gpe=(s['E']+s['S']+s['M'])/3
p=wgp*gpe+wet*s['ET']+wknn*s['KNN']
yt,ek=s['yt'],s['ek']; cr=cond_rmse(p,yt,ek); err=p-yt
agg.append((np.sqrt(np.mean(err**2)),np.mean(np.abs(err)),np.percentile(cr,90),np.percentile(cr,95),cr.max()))
agg=np.array(agg)
return agg
combos=[(1,0,0),(0.85,0.15,0),(0.8,0.2,0),(0.75,0.15,0.10),(0.7,0.2,0.10),(0.75,0.25,0),(0.65,0.25,0.10),(0.6,0.3,0.10),(0.7,0.3,0)]
print(f"{'wgp/wet/wknn':16s} {'rmse':>6}{'mae':>6}{'p90':>6}{'p95':>6}{'max':>6} | {'p90max':>7}{'p90>2.35':>9}")
for w in combos:
agg=eval_blend(*w)
m=agg.mean(0)
p90s=agg[:,2]
print(f"{str(w):16s} {m[0]:6.3f}{m[1]:6.3f}{m[2]:6.3f}{m[3]:6.3f}{m[4]:6.3f} | {p90s.max():7.3f}{int((p90s>2.35).sum()):6d}/{len(seeds)}")
EOF
sleep 120; python3 /tmp/analyze.py3 seeds: ['1', '2', '3'] wgp/wet/wknn rmse mae p90 p95 max | p90max p90>2.35 (1, 0, 0) 2.067 1.537 3.144 3.564 3.901 | 3.502 3/3 (0.85, 0.15, 0) 2.026 1.511 2.983 3.484 3.881 | 3.355 3/3 (0.8, 0.2, 0) 2.018 1.508 2.908 3.444 3.920 | 3.281 2/3 (0.75, 0.15, 0.1) 2.038 1.531 2.868 3.416 3.985 | 3.268 2/3 (0.7, 0.2, 0.1) 2.036 1.531 2.819 3.401 4.027 | 3.196 2/3 (0.75, 0.25, 0) 2.013 1.507 2.835 3.407 3.961 | 3.210 2/3 (0.65, 0.25, 0.1) 2.037 1.533 2.772 3.388 4.071 | 3.127 2/3 (0.6, 0.3, 0.1) 2.041 1.535 2.734 3.378 4.116 | 3.062 2/3 (0.7, 0.3, 0) 2.012 1.508 2.768 3.372 4.003 | 3.142 2/3
cat /tmp/cache2.log; echo "cached:"; ls /tmp/seedcache_*.npz 2>/dev/null | wc -l; pgrep -af cache_seeds | grep -v timeout >/dev/null && echo "cache running" || echo "cache idle"
seed3 cached (103s) cached: 3 cache running
sleep 220; cat /tmp/cache2.log; echo "cached:"; ls /tmp/seedcache_*.npz 2>/dev/null | wc -l
seed3 cached (103s) seed5 cached (91s) cached: 4
pkill -f cache_seeds 2>/dev/null; sleep 2; cd /app && sed '/comp\[.HGB.\]/d' /tmp/cache_seeds.py > /tmp/cache_lean.py && nohup timeout 295 python3 /tmp/cache_lean.py 6 7 8 9 10 11 12 13 > /tmp/cache3.log 2>&1 & echo "relaunched lean cacher"; sleep 3; pgrep -af cache_lean | grep -v timeout || echo none
relaunched lean cacher 1253 python3 /tmp/cache_lean.py 6 7 8 9 10 11 12 13
cd /app && cat > /tmp/analyze2.py << 'EOF'
import numpy as np, glob
files=sorted(glob.glob('/tmp/seedcache_*.npz'), key=lambda f:int(f.split('_')[1][:-4]))
seeds=[np.load(f) for f in files]
print(f'{len(seeds)} seeds:', [f.split("_")[1][:-4] for f in files])
def cr(p,yt,ek):
e=p-yt; return np.array([np.sqrt(np.mean(e[ek==k]**2)) for k in np.unique(ek)])
def ev(wgp,wet,wknn):
a=[]
for s in seeds:
gpe=(s['E']+s['S']+s['M'])/3; p=wgp*gpe+wet*s['ET']+wknn*s['KNN']
yt,ek=s['yt'],s['ek']; c=cr(p,yt,ek); e=p-yt
a.append((np.sqrt(np.mean(e**2)),np.mean(np.abs(e)),np.percentile(c,90),np.percentile(c,95),c.max()))
return np.array(a)
combos=[(1,0,0),(0.8,0.2,0),(0.7,0.3,0),(0.6,0.4,0),(0.5,0.5,0),(0.65,0.25,0.1),(0.6,0.3,0.1),(0.55,0.35,0.1),(0.5,0.4,0.1)]
print(f"{'w(gp,et,knn)':18s}{'rmse':>6}{'mae':>6}{'p90':>6}{'p95':>6}{'max':>6} | worst: {'rmse':>5}{'mae':>5}{'p90':>5}{'p95':>5}{'max':>5}")
for w in combos:
a=ev(*w); m=a.mean(0); mx=a.max(0)
print(f"{str(w):18s}{m[0]:6.3f}{m[1]:6.3f}{m[2]:6.3f}{m[3]:6.3f}{m[4]:6.3f} | {mx[0]:5.2f}{mx[1]:5.2f}{mx[2]:5.2f}{mx[3]:5.2f}{mx[4]:5.2f}")
EOF
python3 /tmp/analyze2.py4 seeds: ['1', '2', '3', '5'] w(gp,et,knn) rmse mae p90 p95 max | worst: rmse mae p90 p95 max (1, 0, 0) 2.110 1.554 3.170 3.598 3.996 | 2.39 1.83 3.50 4.03 4.61 (0.8, 0.2, 0) 2.036 1.510 2.934 3.436 3.885 | 2.32 1.77 3.28 3.93 4.80 (0.7, 0.3, 0) 2.017 1.502 2.813 3.348 3.889 | 2.31 1.76 3.14 3.83 4.90 (0.6, 0.4, 0) 2.010 1.501 2.740 3.271 3.901 | 2.30 1.75 3.02 3.77 5.01 (0.5, 0.5, 0) 2.015 1.507 2.691 3.230 3.923 | 2.31 1.74 2.91 3.72 5.12 (0.65, 0.25, 0.1) 2.024 1.516 2.784 3.348 3.942 | 2.33 1.78 3.13 3.84 5.03 (0.6, 0.3, 0.1) 2.022 1.516 2.750 3.324 3.947 | 2.32 1.78 3.06 3.82 5.09 (0.55, 0.35, 0.1) 2.022 1.519 2.729 3.303 3.955 | 2.33 1.77 3.00 3.79 5.15 (0.5, 0.4, 0.1) 2.026 1.522 2.710 3.285 3.964 | 2.33 1.77 2.94 3.77 5.20
cd /app && python3 -c "
import numpy as np, pandas as pd, sys; sys.path.insert(0,'/tmp')
from harness import cond_key, TARGET
va=pd.read_csv('/app/data/validation.csv'); yv=va[TARGET].values; keys=cond_key(va)
E=np.load('/tmp/val_GP_E.npy'); S=np.load('/tmp/val_GP_S.npy'); M=np.load('/tmp/val_GP_M.npy')
ET=np.load('/tmp/val_ET.npy'); KNN=np.load('/tmp/val_KNN8.npy')
gpe=(E+S+M)/3
def rep(w):
p=w[0]*gpe+w[1]*ET+w[2]*KNN; e=p-yv
cr=np.array([np.sqrt(np.mean(e[keys==k]**2)) for k in np.unique(keys)])
print(f'{str(w):18s} rmse={np.sqrt(np.mean(e**2)):.3f} mae={np.mean(np.abs(e)):.3f} p90={np.percentile(cr,90):.3f} p95={np.percentile(cr,95):.3f} max={cr.max():.3f} under={int((cr<=2.35).sum())}/15')
for w in [(1,0,0),(0.8,0.2,0),(0.7,0.3,0),(0.6,0.4,0),(0.5,0.5,0),(0.65,0.25,0.1),(0.6,0.3,0.1),(0.55,0.35,0.1),(0.5,0.4,0.1)]:
rep(w)
"(1, 0, 0) rmse=1.805 mae=1.382 p90=2.497 p95=3.064 max=4.051 under=13/15 (0.8, 0.2, 0) rmse=1.819 mae=1.392 p90=2.424 p95=3.047 max=4.038 under=13/15 (0.7, 0.3, 0) rmse=1.843 mae=1.406 p90=2.403 p95=3.047 max=4.040 under=13/15 (0.6, 0.4, 0) rmse=1.878 mae=1.433 p90=2.459 p95=3.053 max=4.049 under=13/15 (0.5, 0.5, 0) rmse=1.923 mae=1.467 p90=2.530 p95=3.065 max=4.064 under=12/15 (0.65, 0.25, 0.1) rmse=1.859 mae=1.414 p90=2.439 p95=3.073 max=3.999 under=13/15 (0.6, 0.3, 0.1) rmse=1.878 mae=1.428 p90=2.467 p95=3.079 max=4.005 under=13/15 (0.55, 0.35, 0.1) rmse=1.900 mae=1.443 p90=2.495 p95=3.085 max=4.012 under=13/15 (0.5, 0.4, 0.1) rmse=1.923 mae=1.460 p90=2.524 p95=3.094 max=4.021 under=13/15
cd /app && echo "=== VAL split ===" && python3 -c "
import numpy as np, pandas as pd, sys; sys.path.insert(0,'/tmp')
from harness import cond_key, TARGET
va=pd.read_csv('/app/data/validation.csv'); yv=va[TARGET].values; keys=cond_key(va)
gpe=(np.load('/tmp/val_GP_E.npy')+np.load('/tmp/val_GP_S.npy')+np.load('/tmp/val_GP_M.npy'))/3
ET=np.load('/tmp/val_ET.npy'); HGB=np.load('/tmp/val_HGB.npy')
def rep(name,p):
e=p-yv; cr=np.array([np.sqrt(np.mean(e[keys==k]**2)) for k in np.unique(keys)])
print(f'{name:22s} rmse={np.sqrt(np.mean(e**2)):.3f} mae={np.mean(np.abs(e)):.3f} p90={np.percentile(cr,90):.3f} p95={np.percentile(cr,95):.3f} max={cr.max():.3f}')
rep('0.7GP+0.3ET', 0.7*gpe+0.3*ET)
rep('0.7GP+0.3(.6ET+.4HGB)', 0.7*gpe+0.3*(0.6*ET+0.4*HGB))
rep('0.7GP+0.15ET+0.15HGB', 0.7*gpe+0.15*ET+0.15*HGB)
"
echo "=== multi-seed (1,2,3 have HGB) ===" && python3 -c "
import numpy as np, glob
seeds=[np.load(f) for f in ['/tmp/seedcache_1.npz','/tmp/seedcache_2.npz','/tmp/seedcache_3.npz']]
def cr(p,yt,ek):
e=p-yt; return np.array([np.sqrt(np.mean(e[ek==k]**2)) for k in np.unique(ek)])
def ev(fn):
a=[]
for s in seeds:
gpe=(s['E']+s['S']+s['M'])/3; p=fn(s,gpe); yt,ek=s['yt'],s['ek']; c=cr(p,yt,ek); e=p-yt
a.append((np.sqrt(np.mean(e**2)),np.percentile(c,90),np.percentile(c,95),c.max()))
return np.array(a).mean(0), np.array(a).max(0)
for name,fn in [('0.7GP+0.3ET',lambda s,g:0.7*g+0.3*s['ET']),
('0.7GP+0.3(.6ET+.4HGB)',lambda s,g:0.7*g+0.3*(0.6*s['ET']+0.4*s['HGB']))]:
m,mx=ev(fn); print(f'{name:24s} mean rmse={m[0]:.3f} p90={m[1]:.3f} p95={m[2]:.3f} max={m[3]:.3f} | worst p90={mx[1]:.3f} max={mx[3]:.3f}')
"=== VAL split === 0.7GP+0.3ET rmse=1.843 mae=1.406 p90=2.403 p95=3.047 max=4.040 0.7GP+0.3(.6ET+.4HGB) rmse=1.860 mae=1.425 p90=2.460 p95=3.098 max=4.089 0.7GP+0.15ET+0.15HGB rmse=1.867 mae=1.432 p90=2.476 p95=3.114 max=4.106 === multi-seed (1,2,3 have HGB) === 0.7GP+0.3ET mean rmse=2.012 p90=2.768 p95=3.372 max=4.003 | worst p90=3.142 max=4.901 0.7GP+0.3(.6ET+.4HGB) mean rmse=1.988 p90=2.802 p95=3.334 max=3.817 | worst p90=3.140 max=4.592
command: str_replace path: /app/train_and_predict.py old_str: # Blend weights (tuned via grouped holdouts that mimic the hidden split). W_GP_ENSEMBLE = 0.75 W_EXTRATREES = 0.15 W_KNN = 0.10 new_str: # Blend weights (tuned on the provided grouped validation split *and* many # random grouped holdouts that mimic the hidden condition-level split). The # GP ensemble carries the smooth cross-condition physics; ExtraTrees sharpens # local frequency structure and curbs worst-case per-condition errors. W_GP_ENSEMBLE = 0.70 W_EXTRATREES = 0.30
The file /app/train_and_predict.py has been edited successfully.
command: str_replace
path: /app/train_and_predict.py
old_str: # ---- k-NN in standardized physics space (local spectral structure) ----
try:
scaler = StandardScaler().fit(X_et_train)
knn = KNeighborsRegressor(n_neighbors=8, weights="distance")
knn.fit(scaler.transform(X_et_train), y)
knn_pred = knn.predict(scaler.transform(X_et_eval))
if not np.all(np.isfinite(knn_pred)):
knn_pred = et_pred
except Exception:
knn_pred = et_pred
# ---- Weighted blend ----
if gp_preds:
gp_ensemble = np.mean(gp_preds, axis=0)
predictions = (
W_GP_ENSEMBLE * gp_ensemble + W_EXTRATREES * et_pred + W_KNN * knn_pred
)
else:
# GP unavailable: fall back to a robust tree/knn blend.
predictions = 0.8 * et_pred + 0.2 * knn_pred
new_str: # ---- Weighted blend ----
if gp_preds:
gp_ensemble = np.mean(gp_preds, axis=0)
predictions = W_GP_ENSEMBLE * gp_ensemble + W_EXTRATREES * et_pred
else:
# GP unavailable for every parameterization: fall back to ExtraTrees.
predictions = et_predThe file /app/train_and_predict.py has been edited successfully.
command: str_replace path: /app/train_and_predict.py old_str: from sklearn.gaussian_process.kernels import Matern, WhiteKernel from sklearn.neighbors import KNeighborsRegressor from sklearn.pipeline import make_pipeline new_str: from sklearn.gaussian_process.kernels import Matern, WhiteKernel from sklearn.pipeline import make_pipeline
The file /app/train_and_predict.py has been edited successfully.
cat /tmp/cache3.log 2>/dev/null; echo "total cached:"; ls /tmp/seedcache_*.npz 2>/dev/null | wc -l; echo "pausing cache for clean timing"; pkill -f cache_lean 2>/dev/null; sleep 2; cd /app && rm -f output/predictions.csv && time python3 train_and_predict.py 2>&1 | tail -3; echo "exit=$?"; echo "=== validate output ==="; python3 -c "
import pandas as pd, numpy as np
tf=pd.read_csv('/app/data/test_features.csv'); out=pd.read_csv('/app/output/predictions.csv')
print('cols:', list(out.columns))
print('n_out:', len(out), 'n_test:', len(tf))
print('ids match set:', set(out['sample_id'])==set(tf['sample_id'].astype(str)))
print('ids match order:', list(out['sample_id'])==list(tf['sample_id'].astype(str)))
print('all finite:', np.isfinite(out['predicted_scaled_sound_pressure_level']).all())
print('unique preds:', out['predicted_scaled_sound_pressure_level'].nunique())
print('range:', out['predicted_scaled_sound_pressure_level'].min(), out['predicted_scaled_sound_pressure_level'].max())
"total cached: 4 pausing cache for clean timing exit=0 === validate output === cols: ['sample_id', 'predicted_scaled_sound_pressure_level'] n_out: 40 n_test: 40 ids match set: True ids match order: True all finite: True unique preds: 40 range: 114.86677223350918 138.22069695482062 [stderr] real 2m17.268s user 2m2.130s sys 0m9.820s
cd /app && echo "=== VAL ===" && python3 -c "
import numpy as np, pandas as pd, sys; sys.path.insert(0,'/tmp')
from harness import cond_key, TARGET
va=pd.read_csv('/app/data/validation.csv'); yv=va[TARGET].values; keys=cond_key(va)
E=np.load('/tmp/val_GP_E.npy');S=np.load('/tmp/val_GP_S.npy');M=np.load('/tmp/val_GP_M.npy')
ET=np.load('/tmp/val_ET.npy');HGB=np.load('/tmp/val_HGB.npy');KNN=np.load('/tmp/val_KNN8.npy')
gpe=(E+S+M)/3
def rep(name,p):
e=p-yv; cr=np.array([np.sqrt(np.mean(e[keys==k]**2)) for k in np.unique(keys)])
print(f'{name:28s} rmse={np.sqrt(np.mean(e**2)):.3f} mae={np.mean(np.abs(e)):.3f} p90={np.percentile(cr,90):.3f} p95={np.percentile(cr,95):.3f} max={cr.max():.3f}')
rep('0.7gpe+0.3ET (current)', 0.7*gpe+0.3*ET)
rep('median(E,S,M,ET)', np.median(np.vstack([E,S,M,ET]),0))
rep('median(gpe,ET,HGB)', np.median(np.vstack([gpe,ET,HGB]),0))
rep('median(E,S,M,ET,HGB)', np.median(np.vstack([E,S,M,ET,HGB]),0))
rep('0.5*mean(gpe,ET)+0.5*med', 0.5*(0.7*gpe+0.3*ET)+0.5*np.median(np.vstack([E,S,M,ET]),0))
"
echo "=== multi-seed(1,2,3) ===" && python3 -c "
import numpy as np
seeds=[np.load(f) for f in ['/tmp/seedcache_1.npz','/tmp/seedcache_2.npz','/tmp/seedcache_3.npz','/tmp/seedcache_5.npz']]
def cr(p,yt,ek):
e=p-yt; return np.array([np.sqrt(np.mean(e[ek==k]**2)) for k in np.unique(ek)])
def ev(fn):
a=[]
for s in seeds:
gpe=(s['E']+s['S']+s['M'])/3; p=fn(s,gpe); yt,ek=s['yt'],s['ek']; c=cr(p,yt,ek); e=p-yt
a.append((np.sqrt(np.mean(e**2)),np.percentile(c,90),np.percentile(c,95),c.max()))
import numpy as np2; A=np2.array(a); return A.mean(0),A.max(0)
for name,fn in [('0.7gpe+0.3ET',lambda s,g:0.7*g+0.3*s['ET']),
('median(E,S,M,ET)',lambda s,g:np.median(np.vstack([s['E'],s['S'],s['M'],s['ET']]),0))]:
m,mx=ev(fn); print(f'{name:22s} mean rmse={m[0]:.3f} p90={m[1]:.3f} p95={m[2]:.3f} max={m[3]:.3f} | worst p90={mx[1]:.3f} max={mx[3]:.3f}')
"=== VAL === 0.7gpe+0.3ET (current) rmse=1.843 mae=1.406 p90=2.403 p95=3.047 max=4.040 median(E,S,M,ET) rmse=1.809 mae=1.385 p90=2.478 p95=3.145 max=3.935 median(gpe,ET,HGB) rmse=2.074 mae=1.577 p90=2.713 p95=3.212 max=4.223 median(E,S,M,ET,HGB) rmse=1.865 mae=1.419 p90=2.468 p95=3.079 max=4.025 0.5*mean(gpe,ET)+0.5*med rmse=1.819 mae=1.393 p90=2.422 p95=3.093 max=3.983 === multi-seed(1,2,3) === 0.7gpe+0.3ET mean rmse=2.017 p90=2.813 p95=3.348 max=3.889 | worst p90=3.142 max=4.901 median(E,S,M,ET) mean rmse=2.081 p90=3.086 p95=3.575 max=3.975 | worst p90=3.431 max=4.788
cd /app && cat > /tmp/noise.py << 'EOF'
import sys, warnings, time; sys.path.insert(0,'/tmp'); warnings.filterwarnings('ignore')
from harness import evaluate, FEATURES, TARGET, cond_key
import numpy as np, pandas as pd
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.gaussian_process import GaussianProcessRegressor
from sklearn.gaussian_process.kernels import ConstantKernel as C, WhiteKernel, Matern
NU=1.5e-5
tr=pd.read_csv('/app/data/train.csv'); va=pd.read_csv('/app/data/validation.csv'); y=tr[TARGET].values; yv=va[TARGET].values
def build(X,feats):
f=X['frequency'].astype(float).values;a=X['attack_angle'].astype(float).values;c=X['chord_length'].astype(float).values
U=X['free_stream_velocity'].astype(float).values;dd=X['suction_side_displacement_thickness'].astype(float).values
b={'log_f':np.log10(f),'angle':a,'log_c':np.log10(c),'U':U,'log_d':np.log10(dd),'logU':np.log10(U),
'strouhal':np.log10(f*dd/U),'str2':np.log10(f*dd/U)**2,'Re_c':np.log10(U*c/NU),'Re_d':np.log10(U*dd/NU),'dc':np.log10(dd/c),'mach':U/340.0}
return pd.DataFrame(b,index=X.index)[feats].values
sets={'E':['log_f','angle','log_c','U','log_d','strouhal','str2','Re_c','dc'],
'S':['strouhal','str2','angle','log_c','logU','Re_d','dc'],
'M':['log_f','angle','log_c','mach','log_d','strouhal','Re_c']}
keys=cond_key(va); ET=np.load('/tmp/val_ET.npy')
def fit(feats,wk_mode):
Xt=build(tr[FEATURES],feats);Xv=build(va[FEATURES],feats);d=len(feats)
if wk_mode=='opt': wk=WhiteKernel(0.1,(1e-3,10))
else: wk=WhiteKernel(wk_mode,'fixed')
m=make_pipeline(StandardScaler(),GaussianProcessRegressor(kernel=C(1.0)*Matern(np.ones(d),nu=1.5)+wk,normalize_y=True,n_restarts_optimizer=0,random_state=0,alpha=1e-8))
m.fit(Xt,y);
gp=m.named_steps['gaussianprocessregressor']
return m.predict(Xv), gp.kernel_
def rep(name,p):
e=p-yv; cr=np.array([np.sqrt(np.mean(e[keys==k]**2)) for k in np.unique(keys)])
print(f'{name:18s} rmse={np.sqrt(np.mean(e**2)):.3f} mae={np.mean(np.abs(e)):.3f} p90={np.percentile(cr,90):.3f} p95={np.percentile(cr,95):.3f} max={cr.max():.3f}',flush=True)
for mode in ['opt',0.3,0.6,1.0]:
ps=[];
for n,fs in sets.items():
p,k=fit(fs,mode); ps.append(p)
gpe=np.mean(ps,0)
rep(f'GPe noise={mode}', gpe); rep(f' blend 0.7/0.3', 0.7*gpe+0.3*ET)
EOF
nohup timeout 295 python3 /tmp/noise.py > /tmp/noise.log 2>&1 & echo started; sleep 220; cat /tmp/noise.logstarted GPe noise=opt rmse=1.805 mae=1.382 p90=2.497 p95=3.064 max=4.051 blend 0.7/0.3 rmse=1.843 mae=1.406 p90=2.403 p95=3.047 max=4.040 GPe noise=0.3 rmse=2.277 mae=1.717 p90=3.369 p95=3.704 max=3.941 blend 0.7/0.3 rmse=2.228 mae=1.701 p90=3.177 p95=3.485 max=4.023 GPe noise=0.6 rmse=2.501 mae=1.914 p90=3.726 p95=3.988 max=4.039 blend 0.7/0.3 rmse=2.378 mae=1.825 p90=3.422 p95=3.677 max=4.089
cd /app && nohup timeout 295 python3 /tmp/cache_lean.py 6 7 8 9 10 11 12 > /tmp/cache4.log 2>&1 & echo "caching relaunched"; echo "=== final script review ==="; cat -n train_and_predict.py | sed -n '1,60p'
caching relaunched
=== final script review ===
1 #!/usr/bin/env python3
2 """Airfoil self-noise surrogate.
3
4 Strategy
5 --------
6 The hidden evaluation holds out *complete aerodynamic condition groups*
7 ``(attack_angle, chord_length, free_stream_velocity)``, so the model must
8 generalize across unseen physical regimes rather than interpolate between
9 nearby rows. Scoring rewards low global error *and* controlled per-condition
10 failures (p90/p95/max condition RMSE).
11
12 The workhorse is an ensemble of ARD-Matern Gaussian Processes built on
13 physics-informed coordinates (log-frequency, Strouhal number, Reynolds
14 numbers, ...). GPs extrapolate the smooth self-noise physics across held-out
15 conditions far better than trees. We average several GPs over different
16 feature parameterizations (diversity cancels per-condition errors) and blend
17 in an ExtraTrees model (sharp local frequency structure) plus a small
18 k-NN term (local spectral shape). All components are trained only on the
19 provided labeled public data.
20 """
21
22 from __future__ import annotations
23
24 import warnings
25 from pathlib import Path
26
27 import numpy as np
28 import pandas as pd
29
30 warnings.filterwarnings("ignore")
31
32 from sklearn.ensemble import ExtraTreesRegressor
33 from sklearn.gaussian_process import GaussianProcessRegressor
34 from sklearn.gaussian_process.kernels import ConstantKernel as C
35 from sklearn.gaussian_process.kernels import Matern, WhiteKernel
36 from sklearn.pipeline import make_pipeline
37 from sklearn.preprocessing import StandardScaler
38
39 DATA_DIR = Path("/app/data")
40 OUTPUT_DIR = Path("/app/output")
41 TARGET = "scaled_sound_pressure_level"
42 PREDICTION = "predicted_scaled_sound_pressure_level"
43 FEATURES = [
44 "frequency",
45 "attack_angle",
46 "chord_length",
47 "free_stream_velocity",
48 "suction_side_displacement_thickness",
49 ]
50 NU_AIR = 1.5e-5 # kinematic viscosity of air (m^2/s)
51
52 # Diverse physics-informed coordinate systems for the GP ensemble.
53 FEATURE_SETS = {
54 "E": ["log_f", "angle", "log_c", "U", "log_d", "strouhal", "str2", "Re_c", "dc"],
55 "S": ["strouhal", "str2", "angle", "log_c", "logU", "Re_d", "dc"],
56 "M": ["log_f", "angle", "log_c", "mach", "log_d", "strouhal", "Re_c"],
57 }
58
59 # Blend weights (tuned on the provided grouped validation split *and* many
60 # random grouped holdouts that mimic the hidden condition-level split). Thecommand: str_replace path: /app/train_and_predict.py old_str: The workhorse is an ensemble of ARD-Matern Gaussian Processes built on physics-informed coordinates (log-frequency, Strouhal number, Reynolds numbers, ...). GPs extrapolate the smooth self-noise physics across held-out conditions far better than trees. We average several GPs over different feature parameterizations (diversity cancels per-condition errors) and blend in an ExtraTrees model (sharp local frequency structure) plus a small k-NN term (local spectral shape). All components are trained only on the provided labeled public data. """ new_str: The workhorse is an ensemble of ARD-Matern Gaussian Processes built on physics-informed coordinates (log-frequency, Strouhal number, Reynolds numbers, ...). GPs extrapolate the smooth self-noise physics across held-out conditions far better than trees. We average several GPs over different feature parameterizations (diversity cancels per-condition errors) and blend in an ExtraTrees model that sharpens local frequency structure and curbs the worst per-condition failures. All components are trained only on the provided labeled public data; no external labels or data are used. """
The file /app/train_and_predict.py has been edited successfully.
cd /app && sed -n '60,200p' train_and_predict.py
# random grouped holdouts that mimic the hidden condition-level split). The
# GP ensemble carries the smooth cross-condition physics; ExtraTrees sharpens
# local frequency structure and curbs worst-case per-condition errors.
W_GP_ENSEMBLE = 0.70
W_EXTRATREES = 0.30
def build_features(frame: pd.DataFrame, names) -> np.ndarray:
"""Construct physics-informed coordinates, guarding against bad inputs."""
f = np.asarray(frame["frequency"], dtype=float)
a = np.asarray(frame["attack_angle"], dtype=float)
c = np.asarray(frame["chord_length"], dtype=float)
U = np.asarray(frame["free_stream_velocity"], dtype=float)
d = np.asarray(frame["suction_side_displacement_thickness"], dtype=float)
# Physical quantities are strictly positive; clip to keep logs finite even
# if the hidden feature rows contain degenerate values.
eps = 1e-12
f = np.clip(f, eps, None)
c = np.clip(c, eps, None)
U = np.clip(U, eps, None)
d = np.clip(d, eps, None)
cols = {
"log_f": np.log10(f),
"angle": a,
"log_c": np.log10(c),
"U": U,
"log_d": np.log10(d),
"logU": np.log10(U),
"strouhal": np.log10(f * d / U),
"str2": np.log10(f * d / U) ** 2,
"Re_c": np.log10(U * c / NU_AIR),
"Re_d": np.log10(U * d / NU_AIR),
"dc": np.log10(d / c),
"mach": U / 340.0,
}
return np.column_stack([cols[n] for n in names])
def make_gp(n_dims: int) -> GaussianProcessRegressor:
kernel = (
C(1.0, (1e-3, 1e3))
* Matern(length_scale=np.ones(n_dims), length_scale_bounds=(1e-2, 1e5), nu=1.5)
+ WhiteKernel(0.1, (1e-3, 10.0))
)
return make_pipeline(
StandardScaler(),
GaussianProcessRegressor(
kernel=kernel,
normalize_y=True,
n_restarts_optimizer=0,
alpha=1e-8,
random_state=0,
),
)
def load_labeled_data() -> pd.DataFrame:
parts = [pd.read_csv(DATA_DIR / "train.csv")]
validation_path = DATA_DIR / "validation.csv"
if validation_path.exists():
parts.append(pd.read_csv(validation_path))
data = pd.concat(parts, ignore_index=True)
data = data.dropna(subset=FEATURES + [TARGET]).reset_index(drop=True)
return data
def load_evaluation_features() -> pd.DataFrame:
test_path = DATA_DIR / "test_features.csv"
if test_path.exists():
return pd.read_csv(test_path)
validation = pd.read_csv(DATA_DIR / "validation.csv")
return validation.drop(columns=[TARGET])
def main() -> None:
labeled = load_labeled_data()
evaluation = load_evaluation_features()
y = labeled[TARGET].to_numpy(dtype=float)
# Guaranteed fallback model (never fails) used if a component errors out.
X_et_train = build_features(labeled, FEATURE_SETS["E"])
X_et_eval = build_features(evaluation, FEATURE_SETS["E"])
extratrees = ExtraTreesRegressor(n_estimators=800, n_jobs=-1, random_state=0)
extratrees.fit(X_et_train, y)
et_pred = extratrees.predict(X_et_eval)
# ---- Gaussian Process ensemble over diverse feature parameterizations ----
gp_preds = []
for names in FEATURE_SETS.values():
try:
Xtr = build_features(labeled, names)
Xev = build_features(evaluation, names)
gp = make_gp(Xtr.shape[1])
gp.fit(Xtr, y)
p = gp.predict(Xev)
if np.all(np.isfinite(p)):
gp_preds.append(p)
except Exception:
continue
# ---- Weighted blend ----
if gp_preds:
gp_ensemble = np.mean(gp_preds, axis=0)
predictions = W_GP_ENSEMBLE * gp_ensemble + W_EXTRATREES * et_pred
else:
# GP unavailable for every parameterization: fall back to ExtraTrees.
predictions = et_pred
# Final safety net: replace any non-finite value with the ExtraTrees output,
# then with the global mean if even that is unavailable.
predictions = np.asarray(predic
…[truncated]cd /app && cat > /tmp/holdsize.py << 'EOF'
import sys, warnings, time; sys.path.insert(0,'/tmp'); warnings.filterwarnings('ignore')
from harness import TARGET, cond_key
import numpy as np, pandas as pd
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.ensemble import ExtraTreesRegressor
from sklearn.gaussian_process import GaussianProcessRegressor
from sklearn.gaussian_process.kernels import ConstantKernel as C, WhiteKernel, Matern
FEATURES=["frequency","attack_angle","chord_length","free_stream_velocity","suction_side_displacement_thickness"]
NU=1.5e-5
df=pd.concat([pd.read_csv('/app/data/train.csv'),pd.read_csv('/app/data/validation.csv')],ignore_index=True)
y=df[TARGET].values; keys=cond_key(df); uniq=np.unique(keys)
def build(X,feats):
f=X['frequency'].astype(float).values;a=X['attack_angle'].astype(float).values;c=X['chord_length'].astype(float).values
U=X['free_stream_velocity'].astype(float).values;dd=X['suction_side_displacement_thickness'].astype(float).values
b={'log_f':np.log10(f),'angle':a,'log_c':np.log10(c),'U':U,'log_d':np.log10(dd),'logU':np.log10(U),
'strouhal':np.log10(f*dd/U),'str2':np.log10(f*dd/U)**2,'Re_c':np.log10(U*c/NU),'Re_d':np.log10(U*dd/NU),'dc':np.log10(dd/c),'mach':U/340.0}
return pd.DataFrame(b,index=X.index)[feats].values
sets={'E':['log_f','angle','log_c','U','log_d','strouhal','str2','Re_c','dc'],'S':['strouhal','str2','angle','log_c','logU','Re_d','dc'],'M':['log_f','angle','log_c','mach','log_d','strouhal','Re_c']}
Xm={n:build(df[FEATURES],fs) for n,fs in sets.items()}
def mkgp(d): return make_pipeline(StandardScaler(),GaussianProcessRegressor(kernel=C(1.0)*Matern(np.ones(d),nu=1.5)+WhiteKernel(0.1,(1e-3,10)),normalize_y=True,n_restarts_optimizer=0,random_state=0,alpha=1e-8))
def one(seed,nhold):
rng=np.random.RandomState(seed); tc=set(rng.choice(uniq,nhold,replace=False))
te=np.array([k in tc for k in keys]); tri=~te
ps=[mkgp(Xm[n].shape[1]).fit(Xm[n][tri],y[tri]).predict(Xm[n][te]) for n in sets]
gpe=np.mean(ps,0); et=ExtraTreesRegressor(n_estimators=600,n_jobs=-1,random_state=0).fit(Xm['E'][tri],y[tri]).predict(Xm['E'][te])
p=0.7*gpe+0.3*et; yt=y[te]; ek=keys[te]; e=p-yt
cr=np.array([np.sqrt(np.mean(e[ek==k]**2)) for k in np.unique(ek)])
return np.sqrt(np.mean(e**2)),np.mean(np.abs(e)),np.percentile(cr,90),np.percentile(cr,95),cr.max()
for nhold in [15,10,6]:
rs=[one(s,nhold) for s in [1,2,3]]; a=np.array(rs)
m=a.mean(0); print(f'hold={nhold:2d} (train {82-nhold} conds): rmse={m[0]:.3f} mae={m[1]:.3f} p90={m[2]:.3f} p95={m[3]:.3f} max={m[4]:.3f} per-seed p90={np.round(a[:,2],2)}',flush=True)
EOF
nohup timeout 295 python3 /tmp/holdsize.py > /tmp/holdsize.log 2>&1 & echo started; sleep 280; cat /tmp/holdsize.logstarted
pkill -f cache_lean 2>/dev/null; sleep 1; echo "cached seeds:"; ls /tmp/seedcache_*.npz 2>/dev/null | wc -l; cat /tmp/cache4.log 2>/dev/null; echo "=== holdsize (waiting) ==="; sleep 60; cat /tmp/holdsize.log; pgrep -af holdsize | grep -v timeout || echo "holdsize done"
cached seeds: 5 seed6 cached (167s) === holdsize (waiting) === holdsize done
cd /app && sed -i 's/for nhold in \[15,10,6\]:/for nhold in [6,10]:/' /tmp/holdsize.py && nohup timeout 295 python3 /tmp/holdsize.py > /tmp/holdsize2.log 2>&1 & echo started; sleep 250; cat /tmp/holdsize2.log; pgrep -af holdsize | grep -v timeout || echo done
started 1463 python3 /tmp/holdsize.py
sleep 45; cat /tmp/holdsize2.log; pgrep -af holdsize | grep -v timeout || echo done
done
cd /app && cat > /tmp/hs3.py << 'EOF'
import sys, warnings, time; sys.path.insert(0,'/tmp'); warnings.filterwarnings('ignore')
from harness import TARGET, cond_key
import numpy as np, pandas as pd
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.ensemble import ExtraTreesRegressor
from sklearn.gaussian_process import GaussianProcessRegressor
from sklearn.gaussian_process.kernels import ConstantKernel as C, WhiteKernel, Matern
FEATURES=["frequency","attack_angle","chord_length","free_stream_velocity","suction_side_displacement_thickness"]
NU=1.5e-5
df=pd.concat([pd.read_csv('/app/data/train.csv'),pd.read_csv('/app/data/validation.csv')],ignore_index=True)
y=df[TARGET].values; keys=cond_key(df); uniq=np.unique(keys)
def build(X,feats):
f=X['frequency'].astype(float).values;a=X['attack_angle'].astype(float).values;c=X['chord_length'].astype(float).values
U=X['free_stream_velocity'].astype(float).values;dd=X['suction_side_displacement_thickness'].astype(float).values
b={'log_f':np.log10(f),'angle':a,'log_c':np.log10(c),'U':U,'log_d':np.log10(dd),'logU':np.log10(U),
'strouhal':np.log10(f*dd/U),'str2':np.log10(f*dd/U)**2,'Re_c':np.log10(U*c/NU),'Re_d':np.log10(U*dd/NU),'dc':np.log10(dd/c),'mach':U/340.0}
return pd.DataFrame(b,index=X.index)[feats].values
sets={'E':['log_f','angle','log_c','U','log_d','strouhal','str2','Re_c','dc'],'S':['strouhal','str2','angle','log_c','logU','Re_d','dc'],'M':['log_f','angle','log_c','mach','log_d','strouhal','Re_c']}
Xm={n:build(df[FEATURES],fs) for n,fs in sets.items()}
def mkgp(d): return make_pipeline(StandardScaler(),GaussianProcessRegressor(kernel=C(1.0)*Matern(np.ones(d),nu=1.5)+WhiteKernel(0.1,(1e-3,10)),normalize_y=True,n_restarts_optimizer=0,random_state=0,alpha=1e-8))
NHOLD=6
for s in [1,2,3,4]:
rng=np.random.RandomState(s); tc=set(rng.choice(uniq,NHOLD,replace=False))
te=np.array([k in tc for k in keys]); tri=~te
ps=[mkgp(Xm[n].shape[1]).fit(Xm[n][tri],y[tri]).predict(Xm[n][te]) for n in sets]
gpe=np.mean(ps,0); et=ExtraTreesRegressor(n_estimators=600,n_jobs=-1,random_state=0).fit(Xm['E'][tri],y[tri]).predict(Xm['E'][te])
p=0.7*gpe+0.3*et; yt=y[te]; ek=keys[te]; e=p-yt
cr=np.array([np.sqrt(np.mean(e[ek==k]**2)) for k in np.unique(ek)])
print(f'hold6 seed{s}: rmse={np.sqrt(np.mean(e**2)):.3f} mae={np.mean(np.abs(e)):.3f} p90={np.percentile(cr,90):.3f} p95={np.percentile(cr,95):.3f} max={cr.max():.3f} nconds={len(cr)}',flush=True)
EOF
nohup timeout 295 python3 /tmp/hs3.py > /tmp/hs3.log 2>&1 & echo started; sleep 200; cat /tmp/hs3.logstarted hold6 seed1: rmse=2.439 mae=1.586 p90=3.585 p95=4.348 max=5.112 nconds=6
sleep 30; echo "=== hs3 ==="; cat /tmp/hs3.log; echo "=== ET tuning on val ==="; cd /app && timeout 120 python3 -c "
import sys, warnings; sys.path.insert(0,'/tmp'); warnings.filterwarnings('ignore')
from harness import FEATURES, TARGET, cond_key
import numpy as np, pandas as pd
from sklearn.ensemble import ExtraTreesRegressor, RandomForestRegressor
tr=pd.read_csv('/app/data/train.csv'); va=pd.read_csv('/app/data/validation.csv'); y=tr[TARGET].values; yv=va[TARGET].values
NU=1.5e-5
def build(X,feats):
f=X['frequency'].astype(float).values;a=X['attack_angle'].astype(float).values;c=X['chord_length'].astype(float).values
U=X['free_stream_velocity'].astype(float).values;dd=X['suction_side_displacement_thickness'].astype(float).values
b={'log_f':np.log10(f),'angle':a,'log_c':np.log10(c),'U':U,'log_d':np.log10(dd),'strouhal':np.log10(f*dd/U),'str2':np.log10(f*dd/U)**2,'Re_c':np.log10(U*c/NU),'dc':np.log10(dd/c)}
return pd.DataFrame(b,index=X.index)[feats].values
E=['log_f','angle','log_c','U','log_d','strouhal','str2','Re_c','dc']
Xt=build(tr[FEATURES],E); Xv=build(va[FEATURES],E); keys=cond_key(va)
gpe=(np.load('/tmp/val_GP_E.npy')+np.load('/tmp/val_GP_S.npy')+np.load('/tmp/val_GP_M.npy'))/3
def rep(name,p):
e=p-yv; cr=np.array([np.sqrt(np.mean(e[keys==k]**2)) for k in np.unique(keys)])
print(f'{name:30s} rmse={np.sqrt(np.mean(e**2)):.3f} p90={np.percentile(cr,90):.3f} p95={np.percentile(cr,95):.3f} max={cr.max():.3f}')
for cfg in [dict(n_estimators=800),dict(n_estimators=800,min_samples_leaf=2),dict(n_estimators=800,min_samples_leaf=3),dict(n_estimators=800,max_features=0.7)]:
et=ExtraTreesRegressor(n_jobs=-1,random_state=0,**cfg).fit(Xt,y).predict(Xv)
rep(f'blend ET{cfg}', 0.7*gpe+0.3*et)
rf=RandomForestRegressor(n_estimators=800,n_jobs=-1,random_state=0).fit(Xt,y).predict(Xv)
rep('blend RF', 0.7*gpe+0.3*rf)
"=== hs3 ===
hold6 seed1: rmse=2.439 mae=1.586 p90=3.585 p95=4.348 max=5.112 nconds=6
hold6 seed2: rmse=1.708 mae=1.337 p90=2.330 p95=2.462 max=2.594 nconds=6
=== ET tuning on val ===
blend ET{'n_estimators': 800} rmse=1.843 p90=2.403 p95=3.047 max=4.040
blend ET{'n_estimators': 800, 'min_samples_leaf': 2} rmse=1.855 p90=2.425 p95=3.084 max=4.059
blend ET{'n_estimators': 800, 'min_samples_leaf': 3} rmse=1.865 p90=2.432 p95=3.105 max=4.082
blend ET{'n_estimators': 800, 'max_features': 0.7} rmse=1.844 p90=2.406 p95=3.046 max=4.027
blend RF rmse=1.920 p90=2.533 p95=3.134 max=3.946cd /app && python3 -c "
import numpy as np, pandas as pd, sys; sys.path.insert(0,'/tmp')
from harness import cond_key, TARGET
va=pd.read_csv('/app/data/validation.csv'); yv=va[TARGET].values; keys=cond_key(va)
gpe=(np.load('/tmp/val_GP_E.npy')+np.load('/tmp/val_GP_S.npy')+np.load('/tmp/val_GP_M.npy'))/3
ET=np.load('/tmp/val_ET.npy')
p=0.7*gpe+0.3*ET; e=p-yv; ae=np.abs(e)
print('=== Interpretation 2: per-row |error| (if hidden tuples are unique per row) ===')
print(f' RMSE={np.sqrt(np.mean(e**2)):.3f} MAE={np.mean(ae):.3f} |err| p90={np.percentile(ae,90):.3f} p95={np.percentile(ae,95):.3f} max={ae.max():.3f}')
print('=== Interpretation 1: grouped by snapped condition (~15 groups) ===')
cr=np.array([np.sqrt(np.mean(e[keys==k]**2)) for k in np.unique(keys)])
print(f' cond p90={np.percentile(cr,90):.3f} p95={np.percentile(cr,95):.3f} max={cr.max():.3f}')
"=== Interpretation 2: per-row |error| (if hidden tuples are unique per row) === RMSE=1.843 MAE=1.406 |err| p90=2.848 p95=3.425 max=6.429 === Interpretation 1: grouped by snapped condition (~15 groups) === cond p90=2.403 p95=3.047 max=4.040
cd /app && python3 -c "
import numpy as np, pandas as pd, sys; sys.path.insert(0,'/tmp')
from harness import cond_key, TARGET
va=pd.read_csv('/app/data/validation.csv'); yv=va[TARGET].values; keys=cond_key(va)
E=np.load('/tmp/val_GP_E.npy');S=np.load('/tmp/val_GP_S.npy');M=np.load('/tmp/val_GP_M.npy')
gpe=(E+S+M)/3; ET=np.load('/tmp/val_ET.npy'); KNN=np.load('/tmp/val_KNN8.npy')
def ae_stats(name,p):
e=p-yv; ae=np.abs(e)
print(f'{name:26s} |err| p90={np.percentile(ae,90):.3f} p95={np.percentile(ae,95):.3f} max={ae.max():.3f} (rows|err|>5: {(ae>5).sum()}, >4.75:{(ae>4.75).sum()})')
ae_stats('GPe', gpe)
ae_stats('0.7GPe+0.3ET', 0.7*gpe+0.3*ET)
ae_stats('0.7GPe+0.2ET+0.1KNN', 0.7*gpe+0.2*ET+0.1*KNN)
ae_stats('0.65GPe+0.25ET+0.1KNN', 0.65*gpe+0.25*ET+0.1*KNN)
# worst rows for the main blend
p=0.7*gpe+0.3*ET; e=p-yv
idx=np.argsort(np.abs(e))[::-1][:6]
sub=va.iloc[idx].copy(); sub['pred']=p[idx]; sub['err']=e[idx]
print(sub[['frequency','attack_angle','chord_length','free_stream_velocity','scaled_sound_pressure_level','pred','err']].to_string(index=False))
"GPe |err| p90=2.649 p95=3.774 max=6.635 (rows|err|>5: 4, >4.75:5) 0.7GPe+0.3ET |err| p90=2.848 p95=3.425 max=6.429 (rows|err|>5: 4, >4.75:6) 0.7GPe+0.2ET+0.1KNN |err| p90=2.847 p95=3.354 max=6.333 (rows|err|>5: 5, >4.75:5) 0.65GPe+0.25ET+0.1KNN |err| p90=2.957 p95=3.462 max=6.299 (rows|err|>5: 5, >4.75:5) frequency attack_angle chord_length free_stream_velocity scaled_sound_pressure_level pred err 7990.733167 0.030525 0.025390 71.368089 130.098567 136.527183 6.428616 3154.408908 0.019006 0.025430 71.402443 136.451767 130.473233 -5.978535 4006.744847 0.028491 0.025424 71.406950 138.246982 132.501662 -5.745320 1001.658142 17.393079 0.025430 39.552101 138.661407 133.145030 -5.516377 499.364466 17.428098 0.025404 39.596497 118.166382 123.163391 4.997010 9983.069023 0.022077 0.025379 71.341366 131.128466 135.917987 4.789521
pkill -f cache_lean 2>/dev/null; pkill -f hs3 2>/dev/null; pkill -f holdsize 2>/dev/null; sleep 2; echo "stray procs:"; pgrep -af 'python3 /tmp' | grep -v timeout || echo "none"; cd /app && rm -f output/predictions.csv && echo "=== clean run ===" && time python3 train_and_predict.py && echo "=== validation ===" && python3 -c "
import pandas as pd, numpy as np
tf=pd.read_csv('/app/data/test_features.csv'); out=pd.read_csv('/app/output/predictions.csv')
assert list(out.columns)==['sample_id','predicted_scaled_sound_pressure_level'], out.columns
assert len(out)==len(tf)==40
assert list(out['sample_id'])==list(tf['sample_id'].astype(str)), 'id mismatch'
v=out['predicted_scaled_sound_pressure_level'].values
assert np.isfinite(v).all()
assert out['sample_id'].is_unique
print('OK: columns, alignment, uniqueness, finiteness all pass')
print('pred range [%.2f, %.2f], nunique=%d'%(v.min(),v.max(),out['predicted_scaled_sound_pressure_level'].nunique()))
"stray procs: none === clean run === === validation === OK: columns, alignment, uniqueness, finiteness all pass pred range [114.87, 138.22], nunique=40 [stderr] real 2m23.743s user 2m6.650s sys 0m11.160s
cd /app && cp data/test_features.csv /tmp/tf_backup.csv && md5sum data/test_features.csv > /tmp/tf.md5
# Build a synthetic hidden-like test set: all 201 validation feature rows, new sample_ids, no target col
python3 -c "
import pandas as pd
va=pd.read_csv('data/validation.csv').drop(columns=['scaled_sound_pressure_level'])
va['sample_id']=['af_test_%04d'%i for i in range(len(va))]
va.to_csv('data/test_features.csv', index=False)
print('synthetic hidden rows:', len(va))
"
rm -f output/predictions.csv && python3 train_and_predict.py && python3 -c "
import pandas as pd, numpy as np
tf=pd.read_csv('data/test_features.csv'); out=pd.read_csv('output/predictions.csv')
assert list(out.columns)==['sample_id','predicted_scaled_sound_pressure_level']
assert len(out)==len(tf)
assert list(out['sample_id'])==list(tf['sample_id'].astype(str))
assert np.isfinite(out['predicted_scaled_sound_pressure_level']).all()
print('SYNTHETIC HIDDEN RUN OK: %d rows, aligned, finite, nunique=%d'%(len(out), out['predicted_scaled_sound_pressure_level'].nunique()))
"
# restore original input exactly
cp /tmp/tf_backup.csv data/test_features.csv && md5sum -c /tmp/tf.md5 && echo "INPUT RESTORED (checksum verified)"synthetic hidden rows: 201 SYNTHETIC HIDDEN RUN OK: 201 rows, aligned, finite, nunique=201 data/test_features.csv: OK INPUT RESTORED (checksum verified)
cd /app && rm -f output/predictions.csv && python3 train_and_predict.py && echo "=== final predictions (original placeholder input) ===" && head -4 output/predictions.csv && wc -l output/predictions.csv && md5sum -c /tmp/tf.md5 && echo "input still original"
=== final predictions (original placeholder input) === sample_id,predicted_scaled_sound_pressure_level af_val_0000,115.96871560752423 af_val_0001,124.1248782456239 af_val_0002,133.46018874552283 41 output/predictions.csv data/test_features.csv: OK input still original
cd /app && python3 -c "
import numpy as np, glob, pandas as pd, sys; sys.path.insert(0,'/tmp')
from harness import cond_key, TARGET
# multi-seed holdouts
files=sorted(glob.glob('/tmp/seedcache_*.npz'), key=lambda f:int(f.split('_')[1][:-4]))
seeds=[np.load(f) for f in files]
sd=[f.split('_')[1][:-4] for f in files]
# val split
va=pd.read_csv('/app/data/validation.csv'); yv=va[TARGET].values; vk=cond_key(va)
vE=np.load('/tmp/val_GP_E.npy');vS=np.load('/tmp/val_GP_S.npy');vM=np.load('/tmp/val_GP_M.npy');vET=np.load('/tmp/val_ET.npy')
def cr(p,yt,ek):
e=p-yt; return np.array([np.sqrt(np.mean(e[ek==k]**2)) for k in np.unique(ek)])
print(f'multi-seed holdouts: {sd}')
print(f\"{'w_et':>5} {'val_p90':>8} {'ms_mean_p90':>12} {'ms_worst_p90':>13} {'ms_mean_max':>12}\")
for wet in [0.20,0.25,0.30,0.35,0.40]:
wgp=1-wet
# val
vp=wgp*(vE+vS+vM)/3+wet*vET; vcr=cr(vp,yv,vk); vp90=np.percentile(vcr,90)
# multiseed
p90s=[]; maxs=[]
for s in seeds:
p=wgp*(s['E']+s['S']+s['M'])/3+wet*s['ET']; c=cr(p,s['yt'],s['ek'])
p90s.append(np.percentile(c,90)); maxs.append(c.max())
print(f'{wet:5.2f} {vp90:8.3f} {np.mean(p90s):12.3f} {np.max(p90s):13.3f} {np.mean(maxs):12.3f}')
"multi-seed holdouts: ['1', '2', '3', '5', '6'] w_et val_p90 ms_mean_p90 ms_worst_p90 ms_mean_max 0.20 2.424 2.799 3.281 3.785 0.25 2.412 2.745 3.210 3.769 0.30 2.403 2.699 3.142 3.756 0.35 2.427 2.668 3.078 3.745 0.40 2.459 2.641 3.018 3.737
command: str_replace path: /app/train_and_predict.py old_str: W_GP_ENSEMBLE = 0.70 W_EXTRATREES = 0.30 new_str: W_GP_ENSEMBLE = 0.65 W_EXTRATREES = 0.35
The file /app/train_and_predict.py has been edited successfully.
cd /app && rm -f output/predictions.csv && time python3 train_and_predict.py && python3 -c "
import pandas as pd, numpy as np
tf=pd.read_csv('data/test_features.csv'); out=pd.read_csv('output/predictions.csv')
assert list(out.columns)==['sample_id','predicted_scaled_sound_pressure_level']
assert len(out)==len(tf) and list(out['sample_id'])==list(tf['sample_id'].astype(str))
assert np.isfinite(out['predicted_scaled_sound_pressure_level']).all() and out['sample_id'].is_unique
print('FINAL OUTPUT OK:', len(out), 'rows, aligned, finite, unique')
print(out.head(3).to_string(index=False))
" && md5sum -c /tmp/tf.md5FINAL OUTPUT OK: 40 rows, aligned, finite, unique sample_id predicted_scaled_sound_pressure_level af_val_0000 115.959267 af_val_0001 124.131197 af_val_0002 133.471686 data/test_features.csv: OK [stderr] real 2m21.079s user 2m5.190s sys 0m10.520s
cd /app && python3 -c "
import numpy as np, pandas as pd, sys; sys.path.insert(0,'/tmp')
from harness import cond_key, TARGET
va=pd.read_csv('/app/data/validation.csv'); yv=va[TARGET].values; keys=cond_key(va)
gpe=(np.load('/tmp/val_GP_E.npy')+np.load('/tmp/val_GP_S.npy')+np.load('/tmp/val_GP_M.npy'))/3
ET=np.load('/tmp/val_ET.npy')
p=0.65*gpe+0.35*ET; e=p-yv
cr=np.array([np.sqrt(np.mean(e[keys==k]**2)) for k in np.unique(keys)])
print('FINAL model (0.65 GPens + 0.35 ET) on train->validation grouped holdout:')
print(' targets: rmse<=2.25 mae<=1.55 p90<=2.35 p95<=4.75 max<=5.60')
print(' achieved: rmse=%.3f mae=%.3f p90=%.3f p95=%.3f max=%.3f'%(
np.sqrt(np.mean(e**2)), np.mean(np.abs(e)), np.percentile(cr,90), np.percentile(cr,95), cr.max()))
print(' conditions with RMSE<=2.35: %d/%d'%(int((cr<=2.35).sum()), len(cr)))
"FINAL model (0.65 GPens + 0.35 ET) on train->validation grouped holdout: targets: rmse<=2.25 mae<=1.55 p90<=2.35 p95<=4.75 max<=5.60 achieved: rmse=1.859 mae=1.418 p90=2.427 p95=3.050 max=4.044 conditions with RMSE<=2.35: 13/15
cd /app && python3 -m py_compile train_and_predict.py && echo "compiles OK" && echo "=== full script ===" && cat train_and_predict.py
compiles OK
=== full script ===
#!/usr/bin/env python3
"""Airfoil self-noise surrogate.
Strategy
--------
The hidden evaluation holds out *complete aerodynamic condition groups*
``(attack_angle, chord_length, free_stream_velocity)``, so the model must
generalize across unseen physical regimes rather than interpolate between
nearby rows. Scoring rewards low global error *and* controlled per-condition
failures (p90/p95/max condition RMSE).
The workhorse is an ensemble of ARD-Matern Gaussian Processes built on
physics-informed coordinates (log-frequency, Strouhal number, Reynolds
numbers, ...). GPs extrapolate the smooth self-noise physics across held-out
conditions far better than trees. We average several GPs over different
feature parameterizations (diversity cancels per-condition errors) and blend
in an ExtraTrees model that sharpens local frequency structure and curbs the
worst per-condition failures. All components are trained only on the provided
labeled public data; no external labels or data are used.
"""
from __future__ import annotations
import warnings
from pathlib import Path
import numpy as np
import pandas as pd
warnings.filterwarnings("ignore")
from sklearn.ensemble import ExtraTreesRegressor
from sklearn.gaussian_process import GaussianProcessRegressor
from sklearn.gaussian_process.kernels import ConstantKernel as C
from sklearn.gaussian_process.kernels import Matern, WhiteKernel
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
DATA_DIR = Path("/app/data")
OUTPUT_DIR = Path("/app/output")
TARGET = "scaled_sound_pressure_level"
PREDICTION = "predicted_scaled_sound_pressure_level"
FEATURES = [
"frequency",
"attack_angle",
"chord_length",
"free_stream_velocity",
"suction_side_displacement_thickness",
]
NU_AIR = 1.5e-5 # kinematic viscosity of air (m^2/s)
# Diverse physics-informed coordinate systems for the GP ensemble.
FEATURE_SETS = {
"E": ["log_f", "angle", "log_c", "U", "log_d", "strouhal", "str2", "Re_c", "dc"],
"S": ["strouhal", "str2", "angle", "log_c", "logU", "Re_d", "dc"],
"M": ["log_f", "angle", "log_c", "mach", "log_d", "strouhal", "Re_c"],
}
# Blend weights (tuned on the provided grouped validation split *and* many
# random grouped holdouts that mimic the hidden condition-level split). The
# GP ensemble carries the smooth cross-condition physics; ExtraTrees sharpens
# local frequency structure and curbs worst-case per-condition errors.
W_GP_ENSEMBLE = 0.65
W_EXTRATREES = 0.35
def build_features(frame: pd.DataFrame, names) -> np.ndarray:
"""Construct physics-informed coordinates, guarding against bad inputs."""
f = np.asarray(frame["frequency"], dtype=float)
a = np.asarray(frame["attack_angle"], dtype=float)
c = np.asarray(frame["chord_length"], dtype=float)
U = np.asarray(frame["free_stream_velocity"], dtype=float)
d = np.asarray(frame["suction_side_displacement_thickness"], dtype=float)
# Physical quantities are strictly positive; clip to keep logs finite even
# if the hidden feature rows contain degenerate values.
eps = 1e-12
f = np.clip(f, eps, None)
c = np.clip(c, eps, None)
U = np.clip(U, eps, None)
d = np.clip(d, eps, None)
cols = {
"log_f": np.log10(f),
"angle": a,
"log_c": np.log10(c),
"U": U,
"log_d": np.log10(d),
"logU": np.log10(U),
"strouhal": np.log10(f * d / U),
"str2": np.log10(f * d / U) ** 2,
"Re_c": np.log10(U * c / NU_AIR),
"Re_d": np.log10(U * d / NU_AIR),
"dc": np.log10(d / c),
"mach": U / 340.0,
}
return np.column_stack([cols[n] for n in names])
def make_gp(n_dims: int) -> GaussianProcessRegressor:
kernel = (
C(1.0, (1e-3, 1e3))
* Matern(length_scale=np.ones(n_dims), length_scale_bounds=(1e-2, 1e5), nu=1.5)
+ WhiteKernel(0.1, (1e-3, 10.0))
)
return make_pipeline(
StandardScaler(),
Ga
…[truncated]cd /app && echo "=== deliverables ===" && ls -la output/ && echo "" && echo "=== predictions.csv validation ===" && python3 -c "
import pandas as pd, numpy as np
out=pd.read_csv('output/predictions.csv')
tf=pd.read_csv('data/test_features.csv')
print('columns exact:', list(out.columns)==['sample_id','predicted_scaled_sound_pressure_level'])
print('one row per test sample_id:', len(out)==len(tf) and set(out.sample_id.astype(str))==set(tf.sample_id.astype(str)))
print('numeric & finite:', np.isfinite(out.predicted_scaled_sound_pressure_level).all())
print('not constant:', out.predicted_scaled_sound_pressure_level.nunique()>1)
print('value range: [%.2f, %.2f]'%(out.predicted_scaled_sound_pressure_level.min(), out.predicted_scaled_sound_pressure_level.max()))
" && echo "" && echo "=== inputs pristine (train/val/test) ===" && md5sum -c /tmp/tf.md5 && git -C /app status --short 2>/dev/null || echo "(no git)"=== deliverables === total 10 drwxrws--- 2 h2tagent h2tagent 4096 Jun 21 07:25 . drwxrws--- 5 h2tagent h2tagent 4096 Jun 21 07:25 .. -rw-r--r-- 1 h2tagent h2tagent 1274 Jun 21 07:25 predictions.csv === predictions.csv validation === columns exact: True one row per test sample_id: True numeric & finite: True not constant: True value range: [114.86, 138.22] === inputs pristine (train/val/test) === data/test_features.csv: OK (no git)
No file changes, the agent left the workspace untouched.
# SyncVals normalized verifier summary # task: airfoil-self-noise # attempt: 10 status: completed raw_score: 0.43 # reward rule: continuous: score 0.43 < 0.5 # => reward = 0.0 (FAIL) # # Note: these tasks score via an external hidden grader; no pytest/verifier # block is embedded in the agent transcript. The block below is the last # sizable tool-output captured in the run (supporting evidence). ---------------------------------------------------------------------- === deliverables === total 10 drwxrws--- 2 h2tagent h2tagent 4096 Jun 21 07:25 . drwxrws--- 5 h2tagent h2tagent 4096 Jun 21 07:25 .. -rw-r--r-- 1 h2tagent h2tagent 1274 Jun 21 07:25 predictions.csv === predictions.csv validation === columns exact: True one row per test sample_id: True numeric & finite: True not constant: True value range: [114.86, 138.22] === inputs pristine (train/val/test) === data/test_features.csv: OK (no git)
Reproduce this trial: git checkout 2f94510 && PYTHONPATH=src python3 scripts/build_site.py , then open trial/trial_f74276b1e9d940fd. Re-running the agent live requires EVAL_PLATFORM_ENABLE_OAUTH_SMOKE=1 and is non-deterministic.
Trial trial_f74276b1e9d940fd · verifier authoritative; classifier explanatory.