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 && ls -la data/ && echo "---TRAIN---" && head -5 data/train.csv && echo "..." && wc -l data/train.csv && echo "---VALIDATION---" && head -5 data/validation.csv && wc -l data/validation.csv && echo "---TEST FEATURES---" && head -5 data/test_features.csv && wc -l data/test_features.csv
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--- 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 ... 957 data/train.csv ---VALIDATION--- 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 202 data/validation.csv ---TEST FEATURES--- 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 41 data/test_features.csv
cd /app && python3 --version && echo "---" && pip list 2>/dev/null | grep -iE "scikit|numpy|pandas|scipy|xgboost|lightgbm|torch|tensorflow|catboost" && echo "---CPU---" && nproc && echo "---MEM---" && free -h 2>/dev/null | head -2
Python 3.12.3
---
numpy 1.26.4
pandas 2.2.2
scikit-learn 1.4.2
scipy 1.17.1
---CPU---
1
---MEM---
total used free shared buff/cache available
Mem: 11Gi 91Mi 10Gi 0B 30Mi 10Gicd /app && ls -la && echo "---output---" && ls -la output/ 2>/dev/null && echo "---root---" && ls / && echo "---check write perms---" && touch /app/output/testwrite 2>&1 && echo "can write output" && rm -f /app/output/testwrite
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 ---output--- total 8 drwxrws--- 2 h2tagent h2tagent 4096 Jun 21 05:20 . drwxrws--- 4 h2tagent h2tagent 4096 Jun 21 05:20 .. ---root--- app bin boot container_info.json data dataplane_uds dev etc grader home lib lib64 logs lost+found mcp_server media mnt opt proc process_api repo root run sbin srv sys tests tmp usr var workdir workspace ---check write perms--- can write output
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')
print('TRAIN shape', train.shape)
print('VAL shape', val.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)
--- 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')
for name, df in [('TRAIN', train), ('VAL', val)]:
print('===', name, '===')
for c in df.columns:
if c=='sample_id': continue
print(f'{c:40s} min={df[c].min():.5g} max={df[c].max():.5g} n_unique={df[c].nunique()}')
print()
"=== TRAIN === frequency min=199.66 max=20026 n_unique=956 attack_angle min=-0.035 max=22.235 n_unique=956 chord_length min=0.02537 max=0.30516 n_unique=956 free_stream_velocity min=31.652 max=71.407 n_unique=956 suction_side_displacement_thickness min=0.00041129 max=0.058556 n_unique=956 scaled_sound_pressure_level min=104.94 max=140.29 n_unique=956 === VAL === frequency min=199.66 max=20017 n_unique=201 attack_angle min=-0.034726 max=17.435 n_unique=201 chord_length min=0.02537 max=0.30516 n_unique=201 free_stream_velocity min=31.653 max=71.407 n_unique=201 suction_side_displacement_thickness min=0.00039969 max=0.040928 n_unique=201 scaled_sound_pressure_level min=109.79 max=138.66 n_unique=201
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')
alld = pd.concat([train,val])
# Original UCI canonical values
print('chord_length sorted unique-ish (rounded 3):')
print(np.sort(alld['chord_length'].round(3).unique()))
print()
print('free_stream_velocity rounded 0:')
print(np.sort(alld['free_stream_velocity'].round(0).unique()))
print()
print('Check clustering of chord_length - histogram of rounded to 3 decimals:')
print(alld['chord_length'].round(3).value_counts().sort_index())
print()
print('free_stream_velocity rounded 1:')
print(alld['free_stream_velocity'].round(1).value_counts().sort_index())
"chord_length sorted unique-ish (rounded 3): [0.025 0.051 0.101 0.102 0.152 0.153 0.228 0.229 0.304 0.305] free_stream_velocity rounded 0: [32. 40. 55. 56. 71.] Check clustering of chord_length - histogram of rounded to 3 decimals: chord_length 0.025 235 0.051 185 0.101 50 0.102 200 0.152 123 0.153 46 0.228 74 0.229 119 0.304 24 0.305 101 Name: count, dtype: int64 free_stream_velocity rounded 1: free_stream_velocity 31.7 224 39.5 73 39.6 241 39.7 57 55.4 71 55.5 85 55.6 66 71.2 124 71.3 106 71.4 110 Name: count, dtype: int64
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')
alld = pd.concat([train,val])
print('attack_angle rounded 1 value counts:')
print(alld['attack_angle'].round(1).value_counts().sort_index())
print()
print('n unique rounded:', alld['attack_angle'].round(1).nunique())
"attack_angle rounded 1 value counts: attack_angle 0.0 228 1.5 31 2.0 56 3.0 30 3.3 50 4.0 78 4.2 21 4.8 38 5.3 15 5.4 13 6.7 33 7.2 33 7.3 48 8.4 34 8.9 32 9.5 40 9.9 48 11.2 28 12.3 64 12.6 32 12.7 34 15.4 49 15.6 30 17.4 63 19.7 14 22.2 15 Name: count, dtype: int64 n unique rounded: 26
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')
train['src']='train'; val['src']='val'
alld = pd.concat([train,val], ignore_index=True)
# Snap chord and velocity to canonical grids
chords = np.array([0.0254,0.0508,0.1016,0.1524,0.2286,0.3048])
vels = np.array([31.7,39.6,55.5,71.3])
def snap(x, grid):
return grid[np.argmin(np.abs(grid[None,:]-np.asarray(x)[:,None]),axis=1)]
alld['chord_s']=snap(alld['chord_length'].values, chords)
alld['vel_s']=snap(alld['free_stream_velocity'].values, vels)
# For angle, cluster: sort unique and group within tolerance
print('Check residual of chord snap (max abs rel):', np.max(np.abs(alld['chord_length']-alld['chord_s'])/alld['chord_s']))
print('Check residual of vel snap (max abs):', np.max(np.abs(alld['free_stream_velocity']-alld['vel_s'])))
# thickness per (chord_s, vel_s) - is it determined also by angle? check variance
print()
print('Number of (chord_s, vel_s) combos:', alld.groupby(['chord_s','vel_s']).ngroups)
"Check residual of chord snap (max abs rel): 0.0011999999980314066 Check residual of vel snap (max abs): 0.10694985240000676 Number of (chord_s, vel_s) combos: 24
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')
alld = pd.concat([train,val], ignore_index=True)
a = np.sort(alld['attack_angle'].values)
# find gaps
gaps = np.diff(a)
# print values where gap > 0.2 to identify clusters
clusters=[[a[0]]]
for i in range(1,len(a)):
if a[i]-a[i-1] > 0.3:
clusters.append([])
clusters[-1].append(a[i])
print('num clusters with gap>0.3:', len(clusters))
for c in clusters:
print(f' center~{np.mean(c):7.3f} n={len(c):3d} range=[{min(c):.3f},{max(c):.3f}] width={max(c)-min(c):.3f}')
"num clusters with gap>0.3: 19 center~ -0.002 n=228 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.188 n= 80 range=[2.965,3.335] width=0.370 center~ 4.043 n= 99 range=[3.965,4.235] width=0.270 center~ 4.804 n= 38 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= 33 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= 34 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.901 n= 48 range=[9.865,9.935] width=0.070 center~ 11.203 n= 28 range=[11.165,11.235] width=0.070 center~ 12.480 n=130 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= 63 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 && python3 -c "
import pandas as pd
import numpy as np
train = pd.read_csv('data/train.csv')
val = pd.read_csv('data/validation.csv')
train['src']='train'; val['src']='val'
alld = pd.concat([train,val], ignore_index=True)
chords = np.array([0.0254,0.0508,0.1016,0.1524,0.2286,0.3048])
vels = np.array([31.7,39.6,55.5,71.3])
def snap(x, grid):
return grid[np.argmin(np.abs(grid[None,:]-np.asarray(x)[:,None]),axis=1)]
alld['chord_s']=snap(alld['chord_length'].values, chords)
alld['vel_s']=snap(alld['free_stream_velocity'].values, vels)
# snap angle by rounding to nearest 0.1 then cluster centers
a = alld['attack_angle'].values
order = np.argsort(a)
asort = a[order]
grp = np.zeros(len(a),dtype=int)
g=0
for i in range(1,len(asort)):
if asort[i]-asort[i-1] > 0.09:
g+=1
grp[order[i]]=g
alld['angle_g']=grp
print('num angle groups (gap>0.09):', alld['angle_g'].nunique())
# condition = (chord_s, vel_s, angle_g)
alld['cond']=alld['chord_s'].astype(str)+'_'+alld['vel_s'].astype(str)+'_'+alld['angle_g'].astype(str)
print('num conditions total:', alld['cond'].nunique())
# within-condition thickness spread
g = alld.groupby('cond')['suction_side_displacement_thickness']
rel_spread = ((g.max()-g.min())/g.mean())
print('within-cond thickness rel spread: median={:.4f} p95={:.4f} max={:.4f}'.format(rel_spread.median(), rel_spread.quantile(.95), rel_spread.max()))
# overlap train/val
tc = set(alld[alld.src=='train']['cond']); vc=set(alld[alld.src=='val']['cond'])
print('train conds:', len(tc), 'val conds:', len(vc), 'overlap:', len(tc&vc))
print('val-only conds:', len(vc-tc))
"num angle groups (gap>0.09): 23 num conditions total: 82 within-cond thickness rel spread: median=0.0049 p95=0.0050 max=0.0050 train conds: 67 val conds: 15 overlap: 0 val-only conds: 15
cd /app && cat > /tmp/harness.py << 'EOF'
import pandas as pd, numpy as np
from sklearn.model_selection import GroupKFold
DATA='/app/data'
TARGET='scaled_sound_pressure_level'
FEATURES=["frequency","attack_angle","chord_length","free_stream_velocity","suction_side_displacement_thickness"]
def load():
tr=pd.read_csv(f'{DATA}/train.csv'); va=pd.read_csv(f'{DATA}/validation.csv')
return tr, va
def recover_conditions(df):
chords=np.array([0.0254,0.0508,0.1016,0.1524,0.2286,0.3048])
vels=np.array([31.7,39.6,55.5,71.3])
def snap(x,grid): return grid[np.argmin(np.abs(grid[None,:]-np.asarray(x)[:,None]),axis=1)]
cs=snap(df['chord_length'].values,chords)
vs=snap(df['free_stream_velocity'].values,vels)
a=df['attack_angle'].values
order=np.argsort(a); asort=a[order]; grp=np.zeros(len(a),dtype=int); g=0
for i in range(1,len(asort)):
if asort[i]-asort[i-1]>0.09: g+=1
grp[order[i]]=g
cond=[f'{c:.4f}_{v:.1f}_{gg}' for c,v,gg in zip(cs,vs,grp)]
return np.array(cond)
def per_condition_rmse(y_true,y_pred,cond):
df=pd.DataFrame({'y':y_true,'p':y_pred,'c':cond})
out=df.groupby('c').apply(lambda d: np.sqrt(np.mean((d['y']-d['p'])**2)))
return out
def summarize(y_true,y_pred,cond,label=''):
rmse=np.sqrt(np.mean((y_true-y_pred)**2))
mae=np.mean(np.abs(y_true-y_pred))
pc=per_condition_rmse(y_true,y_pred,cond)
p90=np.percentile(pc,90); p95=np.percentile(pc,95); mx=pc.max()
print(f'{label:22s} RMSE={rmse:.3f} MAE={mae:.3f} | condRMSE p90={p90:.3f} p95={p95:.3f} max={mx:.3f} (nconds={len(pc)})')
return dict(rmse=rmse,mae=mae,p90=p90,p95=p95,mx=mx)
def cv_eval(make_model, X, y, groups, nsplits=8, label=''):
gkf=GroupKFold(n_splits=nsplits)
oof=np.zeros(len(y))
for tr,te in gkf.split(X,y,groups):
m=make_model()
m.fit(X.iloc[tr],y.iloc[tr])
oof[te]=m.predict(X.iloc[te])
return summarize(y.values,oof,groups,label=label)
EOF
echo "harness written"harness written
cd /app && python3 -c "
import sys; sys.path.insert(0,'/tmp')
from harness import *
import numpy as np, pandas as pd
tr,va=load()
alld=pd.concat([tr,va],ignore_index=True)
cond=recover_conditions(alld)
print('total conds', len(set(cond)), 'rows', len(alld))
X=alld[FEATURES]; y=alld[TARGET]
from sklearn.base import BaseEstimator, TransformerMixin
from sklearn.linear_model import Ridge
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
# Starter features
class AF(BaseEstimator,TransformerMixin):
def fit(self,X,y=None): return self
def transform(self,X):
f=pd.DataFrame(X,columns=FEATURES).astype(float)
lf=np.log10(f['frequency']); lt=np.log10(f['suction_side_displacement_thickness'])
o=pd.DataFrame(index=f.index)
o['frequency']=f['frequency']; o['attack_angle']=f['attack_angle']; o['chord_length']=f['chord_length']
o['free_stream_velocity']=f['free_stream_velocity']; o['thk']=f['suction_side_displacement_thickness']
o['lf']=lf; o['lt']=lt; o['fv']=lf*f['free_stream_velocity']; o['fa']=lf*f['attack_angle']
o['av']=f['attack_angle']*f['free_stream_velocity']; o['ta']=lt*f['attack_angle']; o['cv']=f['chord_length']*f['free_stream_velocity']
return o.to_numpy(float)
cv_eval(lambda: make_pipeline(AF(),StandardScaler(),Ridge(alpha=10.0)), X,y,cond,label='Starter Ridge')
from sklearn.ensemble import HistGradientBoostingRegressor, RandomForestRegressor, ExtraTreesRegressor
cv_eval(lambda: HistGradientBoostingRegressor(random_state=0), X,y,cond,label='HGB default')
cv_eval(lambda: RandomForestRegressor(n_estimators=300,random_state=0,n_jobs=-1), X,y,cond,label='RF default')
cv_eval(lambda: ExtraTreesRegressor(n_estimators=300,random_state=0,n_jobs=-1), X,y,cond,label='ET default')
from sklearn.neighbors import KNeighborsRegressor
" 2>&1 | grep -v Warningtotal conds 82 rows 1157 Starter Ridge RMSE=5.189 MAE=4.023 | condRMSE p90=7.357 p95=8.297 max=10.651 (nconds=82) HGB default RMSE=3.028 MAE=2.345 | condRMSE p90=4.458 p95=4.607 max=6.054 (nconds=82) RF default RMSE=3.194 MAE=2.436 | condRMSE p90=4.465 p95=5.332 max=6.913 (nconds=82) ET default RMSE=2.875 MAE=2.188 | condRMSE p90=4.211 p95=4.444 max=6.633 (nconds=82)
cd /app && python3 -c "
import sys; sys.path.insert(0,'/tmp')
from harness import *
import numpy as np, pandas as pd
tr,va=load()
chords=np.array([0.0254,0.0508,0.1016,0.1524,0.2286,0.3048])
vels=np.array([31.7,39.6,55.5,71.3])
def snap(x,grid): return grid[np.argmin(np.abs(grid[None,:]-np.asarray(x)[:,None]),axis=1)]
for col,grid in [('chord_length',chords),('free_stream_velocity',vels)]:
t=set(np.round(snap(tr[col].values,grid),4)); v=set(np.round(snap(va[col].values,grid),4))
print(col,'val-only snapped:', sorted(v-t))
# angle: round to 0.1 after clustering is messy; check raw nearest
ta=np.sort(tr['attack_angle'].round(1).unique()); vaa=np.sort(va['attack_angle'].round(1).unique())
print('train angles(round1):',ta)
print('val angles(round1):',vaa)
# for each val angle, min dist to a train angle
for x in vaa:
d=np.min(np.abs(ta-x))
if d>0.15: print(f' val angle {x} min dist to train {d:.2f} (possibly unseen)')
print('frequency range train:',tr.frequency.min(),tr.frequency.max(),'val:',va.frequency.min(),va.frequency.max())
"chord_length val-only snapped: [] free_stream_velocity val-only snapped: [] train angles(round1): [ 0. 1.5 2. 3. 3.3 4. 4.2 4.8 5.3 5.4 6.7 7.2 7.3 8.4 8.9 9.5 9.9 11.2 12.3 12.6 12.7 15.4 15.6 17.4 19.7 22.2] val angles(round1): [ 0. 3.3 4. 4.8 6.7 8.4 9.9 12.3 17.4] frequency range train: 199.660257517 20025.5820397 val: 199.661620239 20017.2485366
cd /app && cat > /tmp/feats.py << 'EOF'
import numpy as np, pandas as pd
FEATURES=["frequency","attack_angle","chord_length","free_stream_velocity","suction_side_displacement_thickness"]
NU=1.5e-5; C=340.0
def phys(X):
f=pd.DataFrame(X,columns=FEATURES).astype(float).reset_index(drop=True)
freq=f['frequency']; ang=f['attack_angle']; chord=f['chord_length']
vel=f['free_stream_velocity']; thk=f['suction_side_displacement_thickness']
o=pd.DataFrame(index=f.index)
lf=np.log10(freq); lt=np.log10(thk); lv=np.log10(vel); lc=np.log10(chord)
o['lf']=lf; o['lt']=lt; o['lv']=lv; o['lc']=lc
o['ang']=ang
o['logSt']=lf+lt-lv # Strouhal number
o['logRe_c']=lv+lc # Reynolds (chord)
o['logRe_d']=lv+lt # Reynolds (thickness)
o['mach']=vel/C
o['ang_sin']=np.sin(np.radians(ang))
o['freq']=freq; o['chord']=chord; o['vel']=vel; o['thk']=thk
o['logSt2']=o['logSt']**2
o['lf_ang']=lf*ang
o['lf_vel']=lf*vel
return o
PHYS_COLS=None
EOF
python3 -c "
import sys; sys.path.insert(0,'/tmp')
from harness import *
from feats import phys
import numpy as np, pandas as pd
tr,va=load(); alld=pd.concat([tr,va],ignore_index=True)
cond=recover_conditions(alld)
X=alld[FEATURES]; y=alld[TARGET]
from sklearn.ensemble import HistGradientBoostingRegressor, ExtraTreesRegressor, RandomForestRegressor
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import FunctionTransformer
def mk(est): return make_pipeline(FunctionTransformer(phys), est)
cv_eval(lambda: mk(HistGradientBoostingRegressor(random_state=0)), X,y,cond,label='HGB phys')
cv_eval(lambda: mk(ExtraTreesRegressor(n_estimators=400,random_state=0,n_jobs=-1)), X,y,cond,label='ET phys')
cv_eval(lambda: mk(RandomForestRegressor(n_estimators=400,random_state=0,n_jobs=-1)), X,y,cond,label='RF phys')
" 2>&1 | grep -v WarningHGB phys RMSE=2.652 MAE=1.980 | condRMSE p90=3.720 p95=4.884 max=6.671 (nconds=82) ET phys RMSE=2.399 MAE=1.789 | condRMSE p90=3.250 p95=3.902 max=6.338 (nconds=82) RF phys RMSE=2.729 MAE=2.054 | condRMSE p90=3.927 p95=4.697 max=6.114 (nconds=82)
cd /app && python3 -c "
import sys; sys.path.insert(0,'/tmp')
from harness import *
from feats import phys
import numpy as np, pandas as pd
from sklearn.ensemble import ExtraTreesRegressor
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import FunctionTransformer
from sklearn.model_selection import GroupKFold
tr,va=load(); alld=pd.concat([tr,va],ignore_index=True)
cond=recover_conditions(alld)
X=alld[FEATURES]; y=alld[TARGET]
gkf=GroupKFold(n_splits=8); oof=np.zeros(len(y))
for t,e in gkf.split(X,y,cond):
m=make_pipeline(FunctionTransformer(phys),ExtraTreesRegressor(n_estimators=400,random_state=0,n_jobs=-1))
m.fit(X.iloc[t],y.iloc[t]); oof[e]=m.predict(X.iloc[e])
pc=per_condition_rmse(y.values,oof,cond).sort_values(ascending=False)
print('Worst 10 conditions:'); print(pc.head(10))
# characterize worst conditions
worst=pc.head(6).index
for w in worst:
sub=alld[cond==w]
print(f'cond {w}: n={len(sub)} ang={sub.attack_angle.mean():.1f} chord={sub.chord_length.mean():.4f} vel={sub.free_stream_velocity.mean():.1f} thk={sub.suction_side_displacement_thickness.mean():.4f} SPLrange=[{sub[TARGET].min():.1f},{sub[TARGET].max():.1f}]')
" 2>&1 | grep -v WarningWorst 10 conditions: c 0.0508_71.3_21 6.337690 0.1524_39.6_17 5.774169 0.0254_55.5_20 4.448915 0.0254_71.3_20 4.149962 0.0508_39.6_15 3.902572 0.0254_71.3_0 3.897698 0.0254_71.3_17 3.545345 0.2286_71.3_10 3.536361 0.1016_71.3_19 3.254602 0.0254_39.6_22 3.204861 dtype: float64 cond 0.0508_71.3_21: n=14 ang=19.7 chord=0.0508 vel=71.3 thk=0.0341 SPLrange=[112.0,128.2] cond 0.1524_39.6_17: n=16 ang=12.6 chord=0.1524 vel=39.6 thk=0.0584 SPLrange=[105.4,119.0] cond 0.0254_55.5_20: n=16 ang=17.4 chord=0.0254 vel=55.5 thk=0.0166 SPLrange=[111.3,131.1] cond 0.0254_71.3_20: n=17 ang=17.4 chord=0.0254 vel=71.3 thk=0.0161 SPLrange=[112.5,126.7] cond 0.0508_39.6_15: n=14 ang=11.2 chord=0.0508 vel=39.6 thk=0.0150 SPLrange=[116.1,134.6] cond 0.0254_71.3_0: n=10 ang=0.0 chord=0.0254 vel=71.4 thk=0.0004 SPLrange=[123.6,138.2]
cd /app && timeout 280 python3 -c "
import sys; sys.path.insert(0,'/tmp')
from harness import *
from feats import phys
import numpy as np, pandas as pd
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import FunctionTransformer, StandardScaler
from sklearn.ensemble import ExtraTreesRegressor, HistGradientBoostingRegressor
from sklearn.gaussian_process import GaussianProcessRegressor
from sklearn.gaussian_process.kernels import RBF, ConstantKernel, WhiteKernel, Matern
tr,va=load(); alld=pd.concat([tr,va],ignore_index=True)
cond=recover_conditions(alld); X=alld[FEATURES]; y=alld[TARGET]
# GP
def mk_gp():
k=ConstantKernel(50.0)*Matern(length_scale=np.ones(18),nu=1.5)+WhiteKernel(1.0)
return make_pipeline(FunctionTransformer(phys),StandardScaler(),GaussianProcessRegressor(kernel=k,normalize_y=True,alpha=1e-6,n_restarts_optimizer=0))
cv_eval(mk_gp, X,y,cond,label='GP Matern1.5',nsplits=8)
# tuned ET
for msl in [1,2,3]:
cv_eval(lambda msl=msl: make_pipeline(FunctionTransformer(phys),ExtraTreesRegressor(n_estimators=600,min_samples_leaf=msl,random_state=0,n_jobs=-1)), X,y,cond,label=f'ET msl={msl}')
# tuned HGB
cv_eval(lambda: make_pipeline(FunctionTransformer(phys),HistGradientBoostingRegressor(max_iter=500,learning_rate=0.05,max_leaf_nodes=31,l2_regularization=1.0,random_state=0)), X,y,cond,label='HGB tuned')
" 2>&1 | grep -v WarningTraceback (most recent call last):
File "<string>", line 18, in <module>
File "/tmp/harness.py", line 44, in cv_eval
m.fit(X.iloc[tr],y.iloc[tr])
File "/usr/local/lib/python3.12/dist-packages/sklearn/base.py", line 1474, in wrapper
return fit_method(estimator, *args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/dist-packages/sklearn/pipeline.py", line 475, in fit
self._final_estimator.fit(Xt, y, **last_step_params["fit"])
File "/usr/local/lib/python3.12/dist-packages/sklearn/base.py", line 1474, in wrapper
return fit_method(estimator, *args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/dist-packages/sklearn/gaussian_process/_gpr.py", line 307, in fit
self._constrained_optimization(
File "/usr/local/lib/python3.12/dist-packages/sklearn/gaussian_process/_gpr.py", line 656, in _constrained_optimization
opt_res = scipy.optimize.minimize(
^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/dist-packages/scipy/optimize/_minimize.py", line 784, in minimize
res = _minimize_lbfgsb(fun, x0, args, jac, bounds,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/dist-packages/scipy/optimize/_lbfgsb_py.py", line 413, in _minimize_lbfgsb
sf = _prepare_scalar_function(fun, x0, jac=jac, args=args, epsilon=eps,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/dist-packages/scipy/optimize/_optimize.py", line 310, in _prepare_scalar_function
sf = ScalarFunction(fun, x0, args, grad, hess,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/dist-packages/scipy/optimize/_differentiable_functions.py", line 283, in __init__
self._update_fun()
File "/usr/local/lib/python3.12/dist-packages/scipy/optimize/_differentiable_functions.py", line 362, in _update_fun
fx = self._wrapped_fun(self.x)
^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/dist-packages/scipy/_lib/_util.py", line 603, in __call__
fx = self.f(np.copy(x), *self.args)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/dist-packages/scipy/optimize/_optimize.py", line 80, in __call__
self._compute_if_needed(x, *args)
File "/usr/local/lib/python3.12/dist-packages/scipy/optimize/_optimize.py", line 74, in _compute_if_needed
fg = self.fun(x, *args)
^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/dist-packages/sklearn/gaussian_process/_gpr.py", line 297, in obj_func
lml, grad = self.log_marginal_likelihood(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/dist-packages/sklearn/gaussian_process/_gpr.py", line 580, in log_marginal_likelihood
K, K_gradient = kernel(self.X_train_, eval_gradient=True)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/dist-packages/sklearn/gaussian_process/kernels.py", line 867, in __call__
K1, K1_gradient = self.k1(X, Y, eval_gradient=True)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/dist-packages/sklearn/gaussian_process/kernels.py", line 966, in __call__
K2, K2_gradient = self.k2(X, Y, eval_gradient=True)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/dist-packages/sklearn/gaussian_process/kernels.py", line 1714, in __call__
length_scale = _check_length_scale(X, self.length_scale)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/dist-packages/sklearn/gaussian_process/kernels.py", line 48, in _check_length_scale
raise ValueError(
ValueError: Anisotropic kernel must have the same number of dimensions as data (18!=17)cd /app && timeout 290 python3 -c "
import sys; sys.path.insert(0,'/tmp')
from harness import *
from feats import phys
import numpy as np, pandas as pd
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import FunctionTransformer, StandardScaler
from sklearn.ensemble import ExtraTreesRegressor, HistGradientBoostingRegressor
from sklearn.gaussian_process import GaussianProcessRegressor
from sklearn.gaussian_process.kernels import RBF, ConstantKernel, WhiteKernel, Matern
tr,va=load(); alld=pd.concat([tr,va],ignore_index=True)
cond=recover_conditions(alld); X=alld[FEATURES]; y=alld[TARGET]
nf=phys(X).shape[1]; print('n phys feats',nf)
def mk_gp(nu):
k=ConstantKernel(50.0)*Matern(length_scale=np.ones(nf),nu=nu)+WhiteKernel(1.0)
return make_pipeline(FunctionTransformer(phys),StandardScaler(),GaussianProcessRegressor(kernel=k,normalize_y=True,alpha=1e-6,n_restarts_optimizer=0))
cv_eval(lambda: mk_gp(1.5), X,y,cond,label='GP Matern1.5',nsplits=8)
cv_eval(lambda: mk_gp(2.5), X,y,cond,label='GP Matern2.5',nsplits=8)
for msl in [1,2,3]:
cv_eval(lambda msl=msl: make_pipeline(FunctionTransformer(phys),ExtraTreesRegressor(n_estimators=600,min_samples_leaf=msl,random_state=0,n_jobs=-1)), X,y,cond,label=f'ET msl={msl}')
cv_eval(lambda: make_pipeline(FunctionTransformer(phys),HistGradientBoostingRegressor(max_iter=500,learning_rate=0.05,max_leaf_nodes=31,l2_regularization=1.0,random_state=0)), X,y,cond,label='HGB tuned')
" 2>&1 | grep -v Warning[stderr] Terminated
cd /app && timeout 200 python3 -c "
import sys,time; sys.path.insert(0,'/tmp')
from harness import *
from feats import phys
import numpy as np, pandas as pd
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import FunctionTransformer, StandardScaler
from sklearn.gaussian_process import GaussianProcessRegressor
from sklearn.gaussian_process.kernels import ConstantKernel, WhiteKernel, Matern
tr,va=load(); alld=pd.concat([tr,va],ignore_index=True)
cond=recover_conditions(alld); X=alld[FEATURES]; y=alld[TARGET]
nf=phys(X).shape[1]
t0=time.time()
def mk_gp(nu):
k=ConstantKernel(50.0)*Matern(length_scale=np.ones(nf),nu=nu)+WhiteKernel(1.0)
return make_pipeline(FunctionTransformer(phys),StandardScaler(),GaussianProcessRegressor(kernel=k,normalize_y=True,alpha=1e-6,n_restarts_optimizer=0))
cv_eval(lambda: mk_gp(1.5), X,y,cond,label='GP Matern1.5 (4fold)',nsplits=4)
print('time', time.time()-t0)
" 2>&1 | grep -v Warning[stderr] Terminated
cd /app && timeout 250 python3 -c "
import sys,time; sys.path.insert(0,'/tmp')
from harness import *
from feats import phys
import numpy as np, pandas as pd
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import FunctionTransformer
from sklearn.ensemble import ExtraTreesRegressor, HistGradientBoostingRegressor
tr,va=load(); alld=pd.concat([tr,va],ignore_index=True)
cond=recover_conditions(alld); X=alld[FEATURES]; y=alld[TARGET]
for msl in [1,2,3,5]:
cv_eval(lambda msl=msl: make_pipeline(FunctionTransformer(phys),ExtraTreesRegressor(n_estimators=600,min_samples_leaf=msl,random_state=0,n_jobs=-1)), X,y,cond,label=f'ET msl={msl}')
for lr,mi in [(0.05,400),(0.03,800),(0.08,300)]:
cv_eval(lambda lr=lr,mi=mi: make_pipeline(FunctionTransformer(phys),HistGradientBoostingRegressor(max_iter=mi,learning_rate=lr,max_leaf_nodes=31,l2_regularization=1.0,random_state=0)), X,y,cond,label=f'HGB lr={lr} mi={mi}')
" 2>&1 | grep -v WarningET msl=1 RMSE=2.402 MAE=1.789 | condRMSE p90=3.197 p95=3.906 max=6.355 (nconds=82) ET msl=2 RMSE=2.455 MAE=1.822 | condRMSE p90=3.326 p95=3.855 max=6.323 (nconds=82) ET msl=3 RMSE=2.529 MAE=1.884 | condRMSE p90=3.352 p95=3.985 max=6.456 (nconds=82) ET msl=5 RMSE=2.669 MAE=1.993 | condRMSE p90=3.711 p95=4.332 max=6.498 (nconds=82) HGB lr=0.05 mi=400 RMSE=2.611 MAE=1.960 | condRMSE p90=3.868 p95=4.741 max=6.244 (nconds=82) HGB lr=0.03 mi=800 RMSE=2.613 MAE=1.953 | condRMSE p90=4.017 p95=4.793 max=6.328 (nconds=82) HGB lr=0.08 mi=300 RMSE=2.599 MAE=1.938 | condRMSE p90=3.935 p95=4.789 max=6.438 (nconds=82)
cd /app && timeout 250 python3 -c "
import sys; sys.path.insert(0,'/tmp')
from harness import *
from feats import phys
import numpy as np, pandas as pd
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import FunctionTransformer, StandardScaler
from sklearn.neural_network import MLPRegressor
from sklearn.ensemble import ExtraTreesRegressor, HistGradientBoostingRegressor
from sklearn.model_selection import GroupKFold
tr,va=load(); alld=pd.concat([tr,va],ignore_index=True)
cond=recover_conditions(alld); X=alld[FEATURES]; y=alld[TARGET]
def mk_mlp(hls,alpha):
return make_pipeline(FunctionTransformer(phys),StandardScaler(),
MLPRegressor(hidden_layer_sizes=hls,activation='tanh',alpha=alpha,max_iter=2000,random_state=0,early_stopping=False))
cv_eval(lambda: mk_mlp((64,64),1e-3), X,y,cond,label='MLP 64x64 a=1e-3')
cv_eval(lambda: mk_mlp((128,64),1e-2), X,y,cond,label='MLP 128x64 a=1e-2')
cv_eval(lambda: mk_mlp((100,100,50),1e-3), X,y,cond,label='MLP 100x100x50')
" 2>&1 | grep -v WarningMLP 64x64 a=1e-3 RMSE=4.197 MAE=3.074 | condRMSE p90=5.929 p95=7.021 max=8.921 (nconds=82) MLP 128x64 a=1e-2 RMSE=4.239 MAE=3.115 | condRMSE p90=6.554 p95=7.057 max=9.041 (nconds=82) MLP 100x100x50 RMSE=6.316 MAE=5.071 | condRMSE p90=8.364 p95=9.028 max=9.761 (nconds=82)
cd /app && timeout 250 python3 -c "
import sys; sys.path.insert(0,'/tmp')
from harness import *
from feats import phys
import numpy as np, pandas as pd
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import FunctionTransformer
from sklearn.ensemble import ExtraTreesRegressor, HistGradientBoostingRegressor, RandomForestRegressor
from sklearn.model_selection import GroupKFold
tr,va=load(); alld=pd.concat([tr,va],ignore_index=True)
cond=recover_conditions(alld); X=alld[FEATURES]; y=alld[TARGET]
def models():
return {
'et': make_pipeline(FunctionTransformer(phys),ExtraTreesRegressor(n_estimators=600,min_samples_leaf=1,random_state=0,n_jobs=-1)),
'hgb': make_pipeline(FunctionTransformer(phys),HistGradientBoostingRegressor(max_iter=400,learning_rate=0.05,max_leaf_nodes=31,l2_regularization=1.0,random_state=0)),
'rf': make_pipeline(FunctionTransformer(phys),RandomForestRegressor(n_estimators=500,min_samples_leaf=1,random_state=0,n_jobs=-1)),
}
gkf=GroupKFold(n_splits=8)
preds={k:np.zeros(len(y)) for k in models()}
for t,e in gkf.split(X,y,cond):
ms=models()
for k,m in ms.items():
m.fit(X.iloc[t],y.iloc[t]); preds[k][e]=m.predict(X.iloc[e])
for k in preds: summarize(y.values,preds[k],cond,label=k)
import itertools
for combo in [('et','hgb'),('et','rf'),('et','hgb','rf'),('hgb','rf')]:
avg=np.mean([preds[k] for k in combo],axis=0)
summarize(y.values,avg,cond,label='avg:'+'+'.join(combo))
# weighted et heavy
for w in [0.6,0.7]:
avg=w*preds['et']+(1-w)*preds['hgb']
summarize(y.values,avg,cond,label=f'et{w}+hgb{1-w:.1f}')
" 2>&1 | grep -v Warninget RMSE=2.402 MAE=1.789 | condRMSE p90=3.197 p95=3.906 max=6.355 (nconds=82) hgb RMSE=2.611 MAE=1.960 | condRMSE p90=3.868 p95=4.741 max=6.244 (nconds=82) rf RMSE=2.730 MAE=2.054 | condRMSE p90=3.970 p95=4.725 max=6.097 (nconds=82) avg:et+hgb RMSE=2.413 MAE=1.796 | condRMSE p90=3.426 p95=4.235 max=6.178 (nconds=82) avg:et+rf RMSE=2.519 MAE=1.883 | condRMSE p90=3.661 p95=4.307 max=6.171 (nconds=82) avg:et+hgb+rf RMSE=2.491 MAE=1.861 | condRMSE p90=3.594 p95=4.325 max=6.138 (nconds=82) avg:hgb+rf RMSE=2.608 MAE=1.955 | condRMSE p90=3.790 p95=4.721 max=6.136 (nconds=82) et0.6+hgb0.4 RMSE=2.396 MAE=1.781 | condRMSE p90=3.313 p95=4.019 max=6.195 (nconds=82) et0.7+hgb0.3 RMSE=2.385 MAE=1.774 | condRMSE p90=3.308 p95=3.821 max=6.220 (nconds=82)
cd /app && python3 -c "
import sys; sys.path.insert(0,'/tmp')
from harness import *
import numpy as np, pandas as pd
tr,va=load(); alld=pd.concat([tr,va],ignore_index=True)
cond=recover_conditions(alld); alld['cond']=cond
alld['lf']=np.log10(alld['frequency'])
resids=[]
for c,d in alld.groupby('cond'):
if len(d)<6: continue
d=d.sort_values('lf')
# fit poly deg 3 in lf
for deg in [3]:
coef=np.polyfit(d['lf'],d[TARGET],deg)
pred=np.polyval(coef,d['lf'])
resids.append((d[TARGET]-pred).values)
r=np.concatenate(resids)
print(f'Within-condition poly3 residual: std={r.std():.3f} mae={np.mean(np.abs(r)):.3f} max|r|={np.max(np.abs(r)):.3f}')
# deg4
resids=[]
for c,d in alld.groupby('cond'):
if len(d)<7: continue
d=d.sort_values('lf')
coef=np.polyfit(d['lf'],d[TARGET],4)
pred=np.polyval(coef,d['lf'])
resids.append((d[TARGET]-pred).values)
r=np.concatenate(resids)
print(f'Within-condition poly4 residual: std={r.std():.3f} mae={np.mean(np.abs(r)):.3f}')
print('n rows per condition: min/med/max', alld.groupby('cond').size().min(), int(alld.groupby('cond').size().median()), alld.groupby('cond').size().max())
"Within-condition poly3 residual: std=1.234 mae=0.815 max|r|=8.960 Within-condition poly4 residual: std=1.014 mae=0.672 n rows per condition: min/med/max 8 14 18
cd /app && timeout 200 python3 -c "
import sys; sys.path.insert(0,'/tmp')
from harness import *
from feats import phys
import numpy as np, pandas as pd
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import FunctionTransformer
from sklearn.ensemble import ExtraTreesRegressor, HistGradientBoostingRegressor
tr,va=load()
condv=recover_conditions(va)
Xtr,ytr=tr[FEATURES],tr[TARGET]; Xva,yva=va[FEATURES],va[TARGET]
print('val conditions:', len(set(condv)))
et=make_pipeline(FunctionTransformer(phys),ExtraTreesRegressor(n_estimators=600,min_samples_leaf=1,random_state=0,n_jobs=-1))
hgb=make_pipeline(FunctionTransformer(phys),HistGradientBoostingRegressor(max_iter=400,learning_rate=0.05,max_leaf_nodes=31,l2_regularization=1.0,random_state=0))
et.fit(Xtr,ytr); hgb.fit(Xtr,ytr)
pe=et.predict(Xva); ph=hgb.predict(Xva)
summarize(yva.values,pe,condv,label='ET val')
summarize(yva.values,ph,condv,label='HGB val')
summarize(yva.values,0.7*pe+0.3*ph,condv,label='0.7ET+0.3HGB val')
summarize(yva.values,0.5*pe+0.5*ph,condv,label='0.5ET+0.5HGB val')
" 2>&1 | grep -v Warningval conditions: 15 ET val RMSE=2.223 MAE=1.764 | condRMSE p90=3.010 p95=3.185 max=3.499 (nconds=15) HGB val RMSE=2.868 MAE=2.200 | condRMSE p90=3.826 p95=4.574 max=6.292 (nconds=15) 0.7ET+0.3HGB val RMSE=2.311 MAE=1.830 | condRMSE p90=3.129 p95=3.434 max=3.918 (nconds=15) 0.5ET+0.5HGB val RMSE=2.426 MAE=1.907 | condRMSE p90=3.232 p95=3.700 max=4.456 (nconds=15)
cd /app && timeout 250 python3 -c "
import sys; sys.path.insert(0,'/tmp')
from harness import *
from feats import phys
import numpy as np, pandas as pd
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import FunctionTransformer, StandardScaler
from sklearn.kernel_ridge import KernelRidge
from sklearn.neighbors import KNeighborsRegressor
tr,va=load(); alld=pd.concat([tr,va],ignore_index=True)
cond=recover_conditions(alld); X=alld[FEATURES]; y=alld[TARGET]
for g in [0.05,0.1,0.2,0.3]:
for a in [0.1,0.5,1.0]:
cv_eval(lambda g=g,a=a: make_pipeline(FunctionTransformer(phys),StandardScaler(),KernelRidge(kernel='rbf',gamma=g,alpha=a)), X,y,cond,label=f'KRR g={g} a={a}')
" 2>&1 | grep -v WarningKRR g=0.05 a=0.1 RMSE=4.871 MAE=3.309 | condRMSE p90=6.375 p95=9.026 max=19.823 (nconds=82) KRR g=0.05 a=0.5 RMSE=6.375 MAE=4.345 | condRMSE p90=9.261 p95=12.085 max=23.682 (nconds=82) KRR g=0.05 a=1.0 RMSE=7.490 MAE=5.117 | condRMSE p90=11.279 p95=14.229 max=27.958 (nconds=82) KRR g=0.1 a=0.1 RMSE=8.926 MAE=5.728 | condRMSE p90=13.351 p95=17.210 max=33.121 (nconds=82) KRR g=0.1 a=0.5 RMSE=10.900 MAE=7.239 | condRMSE p90=18.194 p95=21.448 max=35.737 (nconds=82) KRR g=0.1 a=1.0 RMSE=12.456 MAE=8.396 | condRMSE p90=21.187 p95=24.584 max=41.261 (nconds=82) KRR g=0.2 a=0.1 RMSE=18.789 MAE=12.452 | condRMSE p90=30.903 p95=39.128 max=59.598 (nconds=82) KRR g=0.2 a=0.5 RMSE=21.037 MAE=14.420 | condRMSE p90=35.825 p95=43.210 max=60.317 (nconds=82) KRR g=0.2 a=1.0 RMSE=22.950 MAE=16.145 | condRMSE p90=39.337 p95=46.131 max=63.041 (nconds=82) KRR g=0.3 a=0.1 RMSE=28.947 MAE=20.889 | condRMSE p90=49.941 p95=56.590 max=76.418 (nconds=82) KRR g=0.3 a=0.5 RMSE=31.446 MAE=23.432 | condRMSE p90=52.856 p95=59.462 max=77.944 (nconds=82) KRR g=0.3 a=1.0 RMSE=33.764 MAE=25.914 | condRMSE p90=56.514 p95=61.927 max=82.950 (nconds=82)
cd /app && timeout 250 python3 -c "
import sys; sys.path.insert(0,'/tmp')
from harness import *
from feats import phys
import numpy as np, pandas as pd
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import FunctionTransformer
from sklearn.ensemble import ExtraTreesRegressor
from sklearn.model_selection import GroupKFold
tr,va=load(); alld=pd.concat([tr,va],ignore_index=True)
cond=recover_conditions(alld); X=alld[FEATURES]; y=alld[TARGET]
gkf=GroupKFold(n_splits=8); oof=np.zeros(len(y))
for t,e in gkf.split(X,y,cond):
m=make_pipeline(FunctionTransformer(phys),ExtraTreesRegressor(n_estimators=400,random_state=0,n_jobs=-1))
m.fit(X.iloc[t],y.iloc[t]); oof[e]=m.predict(X.iloc[e])
res=y.values-oof
d=alld.copy(); d['res']=res; d['lf']=np.log10(d['frequency'])
# residual vs features (binned)
for col in ['lf','attack_angle','chord_length','free_stream_velocity','suction_side_displacement_thickness']:
q=pd.qcut(d[col],6,duplicates='drop')
g=d.groupby(q,observed=True)['res'].agg(['mean','std','count'])
print('---',col); print(g.round(3))
print('overall res mean',res.mean().round(3),'std',res.std().round(3))
# feature importance
m=make_pipeline(FunctionTransformer(phys),ExtraTreesRegressor(n_estimators=400,random_state=0,n_jobs=-1)); m.fit(X,y)
imp=m.named_steps['extratreesregressor'].feature_importances_
names=list(phys(X).columns)
for n,i in sorted(zip(names,imp),key=lambda t:-t[1]): print(f' {n:10s} {i:.3f}')
" 2>&1 | grep -v Warning--- lf
mean std count
lf
(2.299, 2.699] -0.260 3.281 193
(2.699, 2.999] 0.210 2.332 193
(2.999, 3.205] -0.033 2.213 193
(3.205, 3.498] -0.072 1.911 192
(3.498, 3.699] 0.079 2.237 193
(3.699, 4.302] -0.413 2.157 193
--- attack_angle
mean std count
attack_angle
(-0.036000000000000004, 0.0306] 0.105 1.747 193
(0.0306, 3.329] -0.142 1.384 193
(3.329, 6.707] 0.041 2.001 193
(6.707, 9.524] 0.173 2.273 192
(9.524, 12.685] 0.366 3.032 193
(12.685, 22.235] -1.030 3.181 193
--- chord_length
mean std count
chord_length
(0.02527, 0.02543] -0.247 2.543 193
(0.02543, 0.05085] -0.287 3.084 193
(0.05085, 0.1017] 0.133 2.217 193
(0.1017, 0.1525] -0.004 2.351 192
(0.1525, 0.2287] -0.087 2.215 193
(0.2287, 0.3052] 0.003 1.794 193
--- free_stream_velocity
mean std count
free_stream_velocity
(31.651, 31.743] -0.124 1.833 193
(31.743, 39.583] 0.034 2.362 193
(39.583, 39.659] -0.254 2.488 193
(39.659, 55.566] -0.297 2.151 192
(55.566, 71.264] 0.447 2.236 193
(71.264, 71.407] -0.295 3.081 193
--- suction_side_displacement_thickness
mean std count
suction_side_displacement_thickness
(-0.0006000000000000001, 0.00173] -0.096 1.976 193
(0.00173, 0.00347] -0.064 1.379 193
(0.00347, 0.0051] 0.309 1.770 193
(0.0051, 0.0124] 0.149 2.460 192
(0.0124, 0.0253] -0.211 2.900 193
(0.0253, 0.0586] -0.575 3.288 193
overall res mean -0.081 std 2.398
logSt 0.335
logSt2 0.128
lf 0.090
freq 0.068
lc 0.062
chord 0.053
ang_sin 0.040
thk 0.038
ang 0.035
lf_ang 0.034
lt 0.032
logRe_c 0.028
logRe_d 0.018
lf_vel 0.016
lv 0.008
mach 0.007
vel 0.007cd /app && timeout 260 python3 -c "
import sys; sys.path.insert(0,'/tmp')
from harness import *
from feats import phys
import numpy as np, pandas as pd
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import FunctionTransformer, StandardScaler
from sklearn.kernel_ridge import KernelRidge
from sklearn.compose import TransformedTargetRegressor
tr,va=load(); alld=pd.concat([tr,va],ignore_index=True)
cond=recover_conditions(alld); X=alld[FEATURES]; y=alld[TARGET]
ymean=y.mean()
def ctr(est): return TransformedTargetRegressor(regressor=est, func=lambda y:y-ymean, inverse_func=lambda y:y+ymean)
for g in [0.02,0.05,0.1]:
for a in [0.1,0.3,1.0]:
cv_eval(lambda g=g,a=a: ctr(make_pipeline(FunctionTransformer(phys),StandardScaler(),KernelRidge(kernel='rbf',gamma=g,alpha=a))), X,y,cond,label=f'KRRc g={g} a={a}')
" 2>&1 | grep -v WarningKRRc g=0.02 a=0.1 RMSE=2.701 MAE=2.038 | condRMSE p90=3.779 p95=4.733 max=6.742 (nconds=82) KRRc g=0.02 a=0.3 RMSE=2.887 MAE=2.161 | condRMSE p90=3.999 p95=5.272 max=6.539 (nconds=82) KRRc g=0.02 a=1.0 RMSE=3.191 MAE=2.367 | condRMSE p90=4.497 p95=5.936 max=7.015 (nconds=82) KRRc g=0.05 a=0.1 RMSE=2.537 MAE=1.917 | condRMSE p90=3.532 p95=4.281 max=6.481 (nconds=82) KRRc g=0.05 a=0.3 RMSE=2.629 MAE=2.000 | condRMSE p90=3.766 p95=4.351 max=6.168 (nconds=82) KRRc g=0.05 a=1.0 RMSE=2.865 MAE=2.164 | condRMSE p90=4.219 p95=5.114 max=5.885 (nconds=82) KRRc g=0.1 a=0.1 RMSE=2.639 MAE=2.001 | condRMSE p90=3.934 p95=4.179 max=6.194 (nconds=82) KRRc g=0.1 a=0.3 RMSE=2.667 MAE=2.035 | condRMSE p90=3.983 p95=4.375 max=6.455 (nconds=82) KRRc g=0.1 a=1.0 RMSE=2.834 MAE=2.141 | condRMSE p90=4.496 p95=4.860 max=6.911 (nconds=82)
cd /app && cat >> /tmp/harness.py << 'EOF'
def fit_predict_val(make_model, Xtr,ytr,Xva):
m=make_model(); m.fit(Xtr,ytr); return m.predict(Xva)
EOF
timeout 280 python3 -c "
import sys; sys.path.insert(0,'/tmp')
from harness import *
from feats import phys
import numpy as np, pandas as pd
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import FunctionTransformer, StandardScaler
from sklearn.ensemble import ExtraTreesRegressor
tr,va=load(); alld=pd.concat([tr,va],ignore_index=True)
cond=recover_conditions(alld); X=alld[FEATURES]; y=alld[TARGET]
condv=recover_conditions(va); Xtr,ytr=tr[FEATURES],tr[TARGET]; Xva,yva=va[FEATURES],va[TARGET]
for mf in [0.5,0.7,1.0,'sqrt']:
lab=f'ET mf={mf}'
r=cv_eval(lambda mf=mf: make_pipeline(FunctionTransformer(phys),ExtraTreesRegressor(n_estimators=500,max_features=mf,random_state=0,n_jobs=-1)), X,y,cond,label='CV '+lab)
m=make_pipeline(FunctionTransformer(phys),ExtraTreesRegressor(n_estimators=500,max_features=mf,random_state=0,n_jobs=-1)); m.fit(Xtr,ytr)
summarize(yva.values,m.predict(Xva),condv,label='VAL '+lab)
print()
" 2>&1 | grep -v WarningCV ET mf=0.5 RMSE=2.483 MAE=1.845 | condRMSE p90=3.437 p95=3.834 max=6.215 (nconds=82) VAL ET mf=0.5 RMSE=2.334 MAE=1.822 | condRMSE p90=3.342 p95=3.370 max=3.423 (nconds=15) CV ET mf=0.7 RMSE=2.448 MAE=1.819 | condRMSE p90=3.270 p95=3.963 max=6.350 (nconds=82) VAL ET mf=0.7 RMSE=2.232 MAE=1.736 | condRMSE p90=3.159 p95=3.319 max=3.531 (nconds=15) CV ET mf=1.0 RMSE=2.400 MAE=1.789 | condRMSE p90=3.270 p95=3.898 max=6.333 (nconds=82) VAL ET mf=1.0 RMSE=2.224 MAE=1.760 | condRMSE p90=3.026 p95=3.176 max=3.451 (nconds=15) CV ET mf=sqrt RMSE=2.757 MAE=2.076 | condRMSE p90=3.732 p95=3.941 max=6.632 (nconds=82) VAL ET mf=sqrt RMSE=2.609 MAE=2.065 | condRMSE p90=3.524 p95=3.673 max=3.786 (nconds=15)
cd /app && timeout 280 python3 -c "
import sys; sys.path.insert(0,'/tmp')
from harness import *
from feats import phys
import numpy as np, pandas as pd
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import FunctionTransformer, StandardScaler
from sklearn.neighbors import KNeighborsRegressor
tr,va=load(); alld=pd.concat([tr,va],ignore_index=True)
cond=recover_conditions(alld); X=alld[FEATURES]; y=alld[TARGET]
# weighted phys: scale some features up
class WPhys(FunctionTransformer):
pass
def wphys(X):
o=phys(X)
return o
for k in [5,8,12,20]:
for w in ['distance']:
cv_eval(lambda k=k,w=w: make_pipeline(FunctionTransformer(phys),StandardScaler(),KNeighborsRegressor(n_neighbors=k,weights=w)), X,y,cond,label=f'KNN k={k} {w}')
" 2>&1 | grep -v WarningKNN k=5 distance RMSE=3.451 MAE=2.619 | condRMSE p90=5.212 p95=5.992 max=7.434 (nconds=82) KNN k=8 distance RMSE=3.303 MAE=2.511 | condRMSE p90=4.706 p95=5.456 max=6.875 (nconds=82) KNN k=12 distance RMSE=3.250 MAE=2.473 | condRMSE p90=4.519 p95=5.467 max=6.701 (nconds=82) KNN k=20 distance RMSE=3.381 MAE=2.562 | condRMSE p90=5.076 p95=5.603 max=7.284 (nconds=82)
cd /app && timeout 290 python3 -c "
import sys; sys.path.insert(0,'/tmp')
from harness import *
from feats import phys
import numpy as np, pandas as pd
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import FunctionTransformer, StandardScaler, SplineTransformer
from sklearn.ensemble import HistGradientBoostingRegressor, ExtraTreesRegressor
from sklearn.linear_model import Ridge
from sklearn.base import BaseEstimator, RegressorMixin, clone
tr,va=load(); alld=pd.concat([tr,va],ignore_index=True)
cond=recover_conditions(alld); X=alld[FEATURES]; y=alld[TARGET]
# HGB absolute loss
for mi,lr in [(600,0.03),(800,0.02)]:
cv_eval(lambda mi=mi,lr=lr: make_pipeline(FunctionTransformer(phys),HistGradientBoostingRegressor(loss='absolute_error',max_iter=mi,learning_rate=lr,max_leaf_nodes=31,l2_regularization=1.0,random_state=0)), X,y,cond,label=f'HGB-abs mi={mi} lr={lr}')
# Hybrid: smooth spline ridge + ET residual
class Hybrid(BaseEstimator,RegressorMixin):
def __init__(self, stage1, stage2): self.stage1=stage1; self.stage2=stage2
def fit(self,X,y):
self.s1=clone(self.stage1).fit(X,y); r=y-self.s1.predict(X)
self.s2=clone(self.stage2).fit(X,r); return self
def predict(self,X): return self.s1.predict(X)+self.s2.predict(X)
def smooth():
return make_pipeline(FunctionTransformer(phys),SplineTransformer(n_knots=6,degree=3),Ridge(alpha=1.0))
cv_eval(smooth, X,y,cond,label='Smooth spline ridge')
et=lambda: make_pipeline(FunctionTransformer(phys),ExtraTreesRegressor(n_estimators=500,random_state=0,n_jobs=-1))
cv_eval(lambda: Hybrid(smooth(), et()), X,y,cond,label='Hybrid smooth+ET')
" 2>&1 | grep -v WarningHGB-abs mi=600 lr=0.03 RMSE=2.682 MAE=1.978 | condRMSE p90=4.220 p95=4.972 max=5.457 (nconds=82) HGB-abs mi=800 lr=0.02 RMSE=2.709 MAE=2.006 | condRMSE p90=4.403 p95=4.751 max=5.504 (nconds=82) Smooth spline ridge RMSE=3.550 MAE=2.627 | condRMSE p90=5.316 p95=6.282 max=8.035 (nconds=82) Hybrid smooth+ET RMSE=2.237 MAE=1.643 | condRMSE p90=3.232 p95=4.116 max=6.090 (nconds=82)
cd /app && cat > /tmp/hybrid.py << 'EOF'
import numpy as np, pandas as pd
from sklearn.base import BaseEstimator, RegressorMixin, clone
class Hybrid(BaseEstimator,RegressorMixin):
def __init__(self, stage1, stage2): self.stage1=stage1; self.stage2=stage2
def fit(self,X,y):
self.s1=clone(self.stage1).fit(X,y); r=y-self.s1.predict(X)
self.s2=clone(self.stage2).fit(X,r); return self
def predict(self,X): return self.s1.predict(X)+self.s2.predict(X)
EOF
timeout 290 python3 -c "
import sys; sys.path.insert(0,'/tmp')
from harness import *
from feats import phys
from hybrid import Hybrid
import numpy as np, pandas as pd
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import FunctionTransformer, StandardScaler, SplineTransformer
from sklearn.ensemble import ExtraTreesRegressor, HistGradientBoostingRegressor
from sklearn.linear_model import Ridge
tr,va=load(); alld=pd.concat([tr,va],ignore_index=True)
cond=recover_conditions(alld); X=alld[FEATURES]; y=alld[TARGET]
condv=recover_conditions(va); Xtr,ytr=tr[FEATURES],tr[TARGET]; Xva,yva=va[FEATURES],va[TARGET]
def smooth(nk,al):
return make_pipeline(FunctionTransformer(phys),SplineTransformer(n_knots=nk,degree=3),Ridge(alpha=al))
et=lambda: make_pipeline(FunctionTransformer(phys),ExtraTreesRegressor(n_estimators=500,random_state=0,n_jobs=-1))
for nk,al in [(5,1.0),(6,1.0),(8,1.0),(6,0.3),(6,3.0),(10,1.0)]:
mk=lambda nk=nk,al=al: Hybrid(smooth(nk,al), et())
cv_eval(mk, X,y,cond,label=f'CV Hyb nk={nk} al={al}')
m=mk(); m.fit(Xtr,ytr); summarize(yva.values,m.predict(Xva),condv,label=f'VAL Hyb nk={nk} al={al}')
print()
" 2>&1 | grep -v WarningCV Hyb nk=5 al=1.0 RMSE=2.251 MAE=1.640 | condRMSE p90=3.335 p95=3.977 max=6.134 (nconds=82) VAL Hyb nk=5 al=1.0 RMSE=2.208 MAE=1.675 | condRMSE p90=3.440 p95=3.691 max=4.178 (nconds=15) CV Hyb nk=6 al=1.0 RMSE=2.237 MAE=1.643 | condRMSE p90=3.232 p95=4.116 max=6.090 (nconds=82) VAL Hyb nk=6 al=1.0 RMSE=2.167 MAE=1.647 | condRMSE p90=3.369 p95=3.671 max=4.102 (nconds=15) CV Hyb nk=8 al=1.0 RMSE=2.363 MAE=1.742 | condRMSE p90=3.667 p95=4.169 max=7.106 (nconds=82) VAL Hyb nk=8 al=1.0 RMSE=2.367 MAE=1.795 | condRMSE p90=3.819 p95=4.060 max=4.202 (nconds=15) CV Hyb nk=6 al=0.3 RMSE=2.210 MAE=1.634 | condRMSE p90=3.378 p95=3.896 max=5.335 (nconds=82) VAL Hyb nk=6 al=0.3 RMSE=2.198 MAE=1.685 | condRMSE p90=3.149 p95=3.652 max=4.543 (nconds=15) CV Hyb nk=6 al=3.0 RMSE=2.253 MAE=1.638 | condRMSE p90=3.175 p95=3.920 max=6.724 (nconds=82) VAL Hyb nk=6 al=3.0 RMSE=2.185 MAE=1.651 | condRMSE p90=3.627 p95=3.956 max=3.960 (nconds=15) CV Hyb nk=10 al=1.0 RMSE=2.454 MAE=1.773 | condRMSE p90=3.597 p95=4.072 max=7.859 (nconds=82) VAL Hyb nk=10 al=1.0 RMSE=2.531 MAE=1.886 | condRMSE p90=3.694 p95=4.278 max=5.206 (nconds=15)
cd /app && timeout 200 python3 -c "
import sys; sys.path.insert(0,'/tmp')
from harness import *
from feats import phys
from hybrid import Hybrid
import numpy as np, pandas as pd
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import FunctionTransformer, SplineTransformer
from sklearn.ensemble import ExtraTreesRegressor
from sklearn.linear_model import Ridge
from sklearn.model_selection import GroupKFold
tr,va=load(); alld=pd.concat([tr,va],ignore_index=True)
cond=recover_conditions(alld); X=alld[FEATURES]; y=alld[TARGET]
def smooth(): return make_pipeline(FunctionTransformer(phys),SplineTransformer(n_knots=6,degree=3),Ridge(alpha=0.3))
et=lambda: make_pipeline(FunctionTransformer(phys),ExtraTreesRegressor(n_estimators=500,random_state=0,n_jobs=-1))
gkf=GroupKFold(n_splits=8); oof=np.zeros(len(y))
for t,e in gkf.split(X,y,cond):
m=Hybrid(smooth(),et()); m.fit(X.iloc[t],y.iloc[t]); oof[e]=m.predict(X.iloc[e])
pc=per_condition_rmse(y.values,oof,cond).sort_values()
print('percentiles of per-cond RMSE: p50=%.2f p75=%.2f p90=%.2f p95=%.2f max=%.2f'%(pc.median(),pc.quantile(.75),pc.quantile(.9),pc.quantile(.95),pc.max()))
print('frac conds <=2.35:', (pc<=2.35).mean())
print('Worst 8:');
for c,v in pc.tail(8).items():
sub=alld[cond==c]; print(f' {c}: RMSE={v:.2f} ang={sub.attack_angle.mean():.1f} chord={sub.chord_length.mean():.4f} vel={sub.free_stream_velocity.mean():.0f}')
" 2>&1 | grep -v Warningpercentiles of per-cond RMSE: p50=1.74 p75=2.41 p90=3.38 p95=3.90 max=5.34 frac conds <=2.35: 0.7317073170731707 Worst 8: 0.1016_39.6_19: RMSE=3.48 ang=15.6 chord=0.1016 vel=40 0.0254_31.7_20: RMSE=3.59 ang=17.4 chord=0.0254 vel=32 0.2286_71.3_10: RMSE=3.86 ang=7.3 chord=0.2286 vel=71 0.0254_39.6_22: RMSE=3.90 ang=22.2 chord=0.0254 vel=40 0.0254_55.5_20: RMSE=4.33 ang=17.4 chord=0.0254 vel=55 0.0254_71.3_20: RMSE=4.34 ang=17.4 chord=0.0254 vel=71 0.0254_71.3_0: RMSE=4.37 ang=0.0 chord=0.0254 vel=71 0.0508_71.3_21: RMSE=5.34 ang=19.7 chord=0.0508 vel=71
cd /app && cat > /tmp/feats2.py << 'EOF'
import numpy as np, pandas as pd
FEATURES=["frequency","attack_angle","chord_length","free_stream_velocity","suction_side_displacement_thickness"]
NU=1.5e-5; C=340.0
def phys2(X):
f=pd.DataFrame(X,columns=FEATURES).astype(float).reset_index(drop=True)
freq=f['frequency']; ang=f['attack_angle']; chord=f['chord_length']
vel=f['free_stream_velocity']; thk=f['suction_side_displacement_thickness']
o=pd.DataFrame(index=f.index)
lf=np.log10(freq); lt=np.log10(thk); lv=np.log10(vel); lc=np.log10(chord)
logSt=lf+lt-lv
o['lf']=lf; o['lt']=lt; o['lv']=lv; o['lc']=lc; o['ang']=ang
o['logSt']=logSt; o['logRe_c']=lv+lc; o['logRe_d']=lv+lt; o['mach']=vel/C
o['ang_sin']=np.sin(np.radians(ang))
o['logSt_ang']=logSt*ang
o['logSt_lv']=logSt*lv
o['logSt_lc']=logSt*lc
o['lf_ang']=lf*ang
o['ang2']=ang**2
return o
EOF
timeout 295 python3 -c "
import sys; sys.path.insert(0,'/tmp')
from harness import *
from feats2 import phys2
from hybrid import Hybrid
import numpy as np, pandas as pd
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import FunctionTransformer, SplineTransformer
from sklearn.ensemble import ExtraTreesRegressor, HistGradientBoostingRegressor
from sklearn.linear_model import Ridge
tr,va=load(); alld=pd.concat([tr,va],ignore_index=True)
cond=recover_conditions(alld); X=alld[FEATURES]; y=alld[TARGET]
condv=recover_conditions(va); Xtr,ytr=tr[FEATURES],tr[TARGET]; Xva,yva=va[FEATURES],va[TARGET]
def smooth(al=0.3,nk=6): return make_pipeline(FunctionTransformer(phys2),SplineTransformer(n_knots=nk,degree=3),Ridge(alpha=al))
et=lambda: make_pipeline(FunctionTransformer(phys2),ExtraTreesRegressor(n_estimators=500,random_state=0,n_jobs=-1))
# Hybrid with enriched feats
for al in [0.3,1.0]:
mk=lambda al=al: Hybrid(smooth(al),et())
cv_eval(mk,X,y,cond,label=f'CV Hyb2 al={al}')
m=mk(); m.fit(Xtr,ytr); summarize(yva.values,m.predict(Xva),condv,label=f'VAL Hyb2 al={al}'); print()
" 2>&1 | grep -v WarningCV Hyb2 al=0.3 RMSE=2.262 MAE=1.673 | condRMSE p90=3.188 p95=4.199 max=4.806 (nconds=82) VAL Hyb2 al=0.3 RMSE=2.166 MAE=1.675 | condRMSE p90=2.920 p95=3.220 max=3.875 (nconds=15) CV Hyb2 al=1.0 RMSE=2.304 MAE=1.710 | condRMSE p90=3.289 p95=4.308 max=5.012 (nconds=82) VAL Hyb2 al=1.0 RMSE=2.221 MAE=1.701 | condRMSE p90=3.068 p95=3.381 max=3.968 (nconds=15)
cd /app && cat > /tmp/blend.py << 'EOF'
import sys; sys.path.insert(0,'/tmp')
from harness import *
from feats import phys
from feats2 import phys2
from hybrid import Hybrid
import numpy as np, pandas as pd
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import FunctionTransformer, SplineTransformer
from sklearn.ensemble import ExtraTreesRegressor, HistGradientBoostingRegressor
from sklearn.linear_model import Ridge
from sklearn.model_selection import GroupKFold
tr,va=load(); alld=pd.concat([tr,va],ignore_index=True)
cond=recover_conditions(alld); X=alld[FEATURES]; y=alld[TARGET]
condv=recover_conditions(va); Xtr,ytr=tr[FEATURES],tr[TARGET]; Xva,yva=va[FEATURES],va[TARGET]
def sm(feat,al,nk=6): return make_pipeline(FunctionTransformer(feat),SplineTransformer(n_knots=nk,degree=3),Ridge(alpha=al))
def et(feat,seed=0,msl=1): return make_pipeline(FunctionTransformer(feat),ExtraTreesRegressor(n_estimators=500,min_samples_leaf=msl,random_state=seed,n_jobs=-1))
def hgb(feat): return make_pipeline(FunctionTransformer(feat),HistGradientBoostingRegressor(loss='absolute_error',max_iter=600,learning_rate=0.03,max_leaf_nodes=31,l2_regularization=1.0,random_state=0))
MODELS={
'A': lambda: Hybrid(sm(phys2,0.3), et(phys2,0)),
'B': lambda: Hybrid(sm(phys,0.3), et(phys,0)),
'C': lambda: et(phys,0),
'D': lambda: Hybrid(sm(phys2,0.3), et(phys,0)),
'E': lambda: Hybrid(sm(phys2,0.3), hgb(phys2)),
'F': lambda: Hybrid(sm(phys,1.0), et(phys2,0)),
}
def compute():
gkf=GroupKFold(n_splits=8)
oof={k:np.zeros(len(y)) for k in MODELS}
valp={k:np.zeros(len(yva)) for k in MODELS}
for k,mk in MODELS.items():
for t,e in gkf.split(X,y,cond):
m=mk(); m.fit(X.iloc[t],y.iloc[t]); oof[k][e]=m.predict(X.iloc[e])
m=mk(); m.fit(Xtr,ytr); valp[k]=m.predict(Xva)
return oof,valp
if __name__=='__main__':
oof,valp=compute()
np.savez('/tmp/preds.npz', y=y.values, cond=cond, yva=yva.values, condv=condv,
**{f'oof_{k}':oof[k] for k in MODELS}, **{f'val_{k}':valp[k] for k in MODELS})
for k in MODELS:
summarize(y.values,oof[k],cond,label=f'CV {k}')
summarize(yva.values,valp[k],condv,label=f'VAL {k}')
print()
EOF
timeout 295 python3 /tmp/blend.py 2>&1 | grep -v WarningCV A RMSE=2.262 MAE=1.673 | condRMSE p90=3.188 p95=4.199 max=4.806 (nconds=82) VAL A RMSE=2.166 MAE=1.675 | condRMSE p90=2.920 p95=3.220 max=3.875 (nconds=15) CV B RMSE=2.210 MAE=1.634 | condRMSE p90=3.378 p95=3.896 max=5.335 (nconds=82) VAL B RMSE=2.198 MAE=1.685 | condRMSE p90=3.149 p95=3.652 max=4.543 (nconds=15) CV C RMSE=2.400 MAE=1.789 | condRMSE p90=3.270 p95=3.898 max=6.333 (nconds=82) VAL C RMSE=2.224 MAE=1.760 | condRMSE p90=3.026 p95=3.176 max=3.451 (nconds=15) CV D RMSE=2.383 MAE=1.794 | condRMSE p90=3.336 p95=4.141 max=5.155 (nconds=82) VAL D RMSE=2.317 MAE=1.788 | condRMSE p90=3.425 p95=3.608 max=3.737 (nconds=15) CV E RMSE=2.439 MAE=1.822 | condRMSE p90=3.642 p95=4.420 max=5.483 (nconds=82) VAL E RMSE=2.360 MAE=1.838 | condRMSE p90=3.481 p95=3.663 max=4.075 (nconds=15) CV F RMSE=2.226 MAE=1.594 | condRMSE p90=3.014 p95=4.043 max=6.299 (nconds=82) VAL F RMSE=2.271 MAE=1.639 | condRMSE p90=3.944 p95=4.696 max=4.795 (nconds=15)
cd /app && timeout 250 python3 -c "
import numpy as np, itertools, sys; sys.path.insert(0,'/tmp')
from harness import per_condition_rmse
d=np.load('/tmp/preds.npz',allow_pickle=True)
keys=['A','B','C','D','E','F']
y=d['y']; cond=d['cond']; yva=d['yva']; condv=d['condv']
oof=np.vstack([d[f'oof_{k}'] for k in keys])
val=np.vstack([d[f'val_{k}'] for k in keys])
def metr(pred,yy,cc):
rmse=np.sqrt(np.mean((yy-pred)**2)); mae=np.mean(np.abs(yy-pred))
pc=per_condition_rmse(yy,pred,cc)
return rmse,mae,np.percentile(pc,90),np.percentile(pc,95),pc.max()
# search simplex weights in steps
best=[]
grid=np.arange(0,1.01,0.2)
import itertools
for w in itertools.product(grid,repeat=len(keys)):
s=sum(w)
if abs(s-1)>1e-6: continue
w=np.array(w)
po=w@oof
r,m,p90,p95,mx=metr(po,y,cond)
# objective: must-pass margins, combine p90 and mae
score=max(r/2.25,m/1.55,p90/2.35,p95/4.75,mx/5.60)
best.append((score,w,r,m,p90,p95,mx))
best.sort(key=lambda t:t[0])
print('Top CV blends (by worst-normalized-metric):')
for score,w,r,m,p90,p95,mx in best[:8]:
pv=w@val
rv,mv,p90v,p95v,mxv=metr(pv,yva,condv)
ws=','.join(f'{k}:{ww:.1f}' for k,ww in zip(keys,w) if ww>0)
print(f' [{ws}] CV r={r:.2f} m={m:.2f} p90={p90:.2f} p95={p95:.2f} mx={mx:.2f} score={score:.3f} || VAL r={rv:.2f} m={mv:.2f} p90={p90v:.2f} mx={mxv:.2f}')
" 2>&1 | grep -v WarningTop CV blends (by worst-normalized-metric): [B:0.4,C:0.2,E:0.2,F:0.2] CV r=2.12 m=1.56 p90=2.72 p95=3.90 mx=5.30 score=1.158 || VAL r=2.13 m=1.65 p90=3.03 mx=4.13 [A:0.2,B:0.4,C:0.2,F:0.2] CV r=2.10 m=1.55 p90=2.78 p95=3.75 mx=5.40 score=1.182 || VAL r=2.10 m=1.62 p90=2.96 mx=4.09 [B:0.2,C:0.2,D:0.2,F:0.4] CV r=2.12 m=1.56 p90=2.80 p95=3.80 mx=5.58 score=1.189 || VAL r=2.13 m=1.63 p90=3.10 mx=4.11 [B:0.2,C:0.2,E:0.2,F:0.4] CV r=2.13 m=1.56 p90=2.80 p95=3.92 mx=5.51 score=1.190 || VAL r=2.13 m=1.64 p90=3.10 mx=4.15 [B:0.4,C:0.2,D:0.2,F:0.2] CV r=2.11 m=1.56 p90=2.80 p95=3.79 mx=5.38 score=1.192 || VAL r=2.13 m=1.64 p90=3.02 mx=4.08 [B:0.4,C:0.2,F:0.4] CV r=2.13 m=1.55 p90=2.81 p95=3.80 mx=5.86 score=1.194 || VAL r=2.14 m=1.62 p90=3.23 mx=4.28 [B:0.2,C:0.2,F:0.6] CV r=2.14 m=1.55 p90=2.82 p95=3.81 mx=6.06 score=1.201 || VAL r=2.16 m=1.62 p90=3.45 mx=4.32 [A:0.2,C:0.4,F:0.4] CV r=2.14 m=1.57 p90=2.82 p95=3.72 mx=5.81 score=1.202 || VAL r=2.11 m=1.63 p90=3.05 mx=3.89
cd /app && timeout 200 python3 -c "
import numpy as np, sys; sys.path.insert(0,'/tmp')
from harness import per_condition_rmse, load, recover_conditions, FEATURES, TARGET
d=np.load('/tmp/preds.npz',allow_pickle=True)
keys=['A','B','C','D','E','F']
y=d['y']; cond=d['cond']
oof=np.vstack([d[f'oof_{k}'] for k in keys])
# best blend weights from search
w=np.array([0,0.4,0.2,0,0.2,0.2]) # B,C,E,F
po=w@oof
tr,va=load(); alld=__import__('pandas').concat([tr,va],ignore_index=True)
lf=np.log10(alld['frequency'].values)
def metr(pred):
rmse=np.sqrt(np.mean((y-pred)**2)); mae=np.mean(np.abs(y-pred))
pc=per_condition_rmse(y,pred,cond);
return f'RMSE={rmse:.3f} MAE={mae:.3f} p90={np.percentile(pc,90):.3f} p95={np.percentile(pc,95):.3f} max={pc.max():.3f} frac<=2.35={np.mean(pc<=2.35):.2f}'
print('raw blend :', metr(po))
import pandas as pd
for deg in [2,3,4]:
sm=po.copy()
df=pd.DataFrame({'lf':lf,'p':po,'c':cond})
for c,idx in df.groupby('c').groups.items():
idx=np.array(idx);
if len(idx)<deg+2: continue
x=lf[idx]; coef=np.polyfit(x,po[idx],deg); sm[idx]=np.polyval(coef,x)
print(f'smooth d{deg}:', metr(sm))
" 2>&1 | grep -v Warningraw blend : RMSE=2.117 MAE=1.560 p90=2.721 p95=3.904 max=5.305 frac<=2.35=0.73 smooth d2: RMSE=2.419 MAE=1.823 p90=3.629 p95=4.274 max=5.244 frac<=2.35=0.66 smooth d3: RMSE=2.263 MAE=1.678 p90=3.570 p95=4.220 max=5.229 frac<=2.35=0.71 smooth d4: RMSE=2.206 MAE=1.628 p90=3.428 p95=4.139 max=5.307 frac<=2.35=0.72
cd /app && timeout 120 python3 -c "
import numpy as np, pandas as pd, sys; sys.path.insert(0,'/tmp')
from harness import load, recover_conditions, TARGET
tr,va=load(); alld=pd.concat([tr,va],ignore_index=True)
cond=recover_conditions(alld); alld['cond']=cond
alld['lf']=np.log10(alld['frequency'])
# model-free noise: second difference along sorted freq; Var(2nd diff)=6*sigma^2 if smooth
sig=[];
perc=[]
for c,d in alld.groupby('cond'):
if len(d)<5: continue
d=d.sort_values('lf'); v=d[TARGET].values
dd=v[2:]-2*v[1:-1]+v[:-2]
s=np.sqrt(np.mean(dd**2)/6.0)
sig.append(s)
sig=np.array(sig)
print(f'Per-condition noise sigma (2nd-diff): median={np.median(sig):.3f} mean={sig.mean():.3f} p90={np.percentile(sig,90):.3f} max={sig.max():.3f}')
print(f'Implied best-case per-cond RMSE ~ sigma. median noise->RMSE~{np.median(sig):.2f}')
# correlate noise with angle
info=[]
for c,d in alld.groupby('cond'):
if len(d)<5: continue
dd=d.sort_values('lf')[TARGET].values; dd=dd[2:]-2*dd[1:-1]+dd[:-2]
info.append((d['attack_angle'].mean(), d['chord_length'].mean(), np.sqrt(np.mean(dd**2)/6.0)))
info=pd.DataFrame(info,columns=['ang','chord','sig'])
print(info.sort_values('sig',ascending=False).head(8).round(3).to_string(index=False))
"Per-condition noise sigma (2nd-diff): median=0.536 mean=0.690 p90=1.295 max=1.697 Implied best-case per-cond RMSE ~ sigma. median noise->RMSE~0.54 ang chord sig 17.399 0.025 1.697 12.301 0.102 1.623 22.204 0.025 1.620 17.402 0.025 1.593 0.017 0.025 1.521 12.301 0.102 1.399 15.603 0.102 1.395 7.200 0.152 1.320
cd /app && cat > /tmp/hybrid2.py << 'EOF'
import numpy as np, pandas as pd
from sklearn.base import BaseEstimator, RegressorMixin, clone
class HybridBag(BaseEstimator,RegressorMixin):
'''stage1 smooth; stage2 = average of several ET with different seeds.'''
def __init__(self, stage1, stage2_factory, n_seed=5):
self.stage1=stage1; self.stage2_factory=stage2_factory; self.n_seed=n_seed
def fit(self,X,y):
self.s1=clone(self.stage1).fit(X,y); r=y-self.s1.predict(X)
self.s2=[self.stage2_factory(s).fit(X,r) for s in range(self.n_seed)]
return self
def predict(self,X):
return self.s1.predict(X)+np.mean([m.predict(X) for m in self.s2],axis=0)
EOF
timeout 295 python3 -c "
import sys; sys.path.insert(0,'/tmp')
from harness import *
from feats import phys
from feats2 import phys2
from hybrid2 import HybridBag
import numpy as np, pandas as pd
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import FunctionTransformer, SplineTransformer
from sklearn.ensemble import ExtraTreesRegressor
from sklearn.linear_model import Ridge
tr,va=load(); alld=pd.concat([tr,va],ignore_index=True)
cond=recover_conditions(alld); X=alld[FEATURES]; y=alld[TARGET]
condv=recover_conditions(va); Xtr,ytr=tr[FEATURES],tr[TARGET]; Xva,yva=va[FEATURES],va[TARGET]
def sm(feat,al): return make_pipeline(FunctionTransformer(feat),SplineTransformer(n_knots=6,degree=3),Ridge(alpha=al))
def etf(feat): return lambda s: make_pipeline(FunctionTransformer(feat),ExtraTreesRegressor(n_estimators=300,random_state=s,n_jobs=-1))
for feat,fn,al in [(phys2,'phys2',0.3),(phys,'phys',0.3)]:
mk=lambda feat=feat,al=al: HybridBag(sm(feat,al), etf(feat), n_seed=6)
cv_eval(mk,X,y,cond,label=f'CV HybBag {fn}')
m=mk(); m.fit(Xtr,ytr); summarize(yva.values,m.predict(Xva),condv,label=f'VAL HybBag {fn}'); print()
" 2>&1 | grep -v WarningCV HybBag phys2 RMSE=2.251 MAE=1.666 | condRMSE p90=3.224 p95=4.209 max=4.740 (nconds=82) VAL HybBag phys2 RMSE=2.156 MAE=1.668 | condRMSE p90=2.945 p95=3.245 max=3.879 (nconds=15) CV HybBag phys RMSE=2.215 MAE=1.639 | condRMSE p90=3.314 p95=3.933 max=5.302 (nconds=82) VAL HybBag phys RMSE=2.195 MAE=1.681 | condRMSE p90=3.144 p95=3.649 max=4.580 (nconds=15)
cd /app && timeout 120 python3 -c "
import numpy as np, pandas as pd, sys; sys.path.insert(0,'/tmp')
from harness import per_condition_rmse, load, recover_conditions, TARGET
d=np.load('/tmp/preds.npz',allow_pickle=True)
keys=['A','B','C','D','E','F']; y=d['y']; cond=d['cond']
oof=np.vstack([d[f'oof_{k}'] for k in keys])
w=np.array([0,0.4,0.2,0,0.2,0.2]); po=w@oof
res=y-po
df=pd.DataFrame({'res':res,'c':cond})
g=df.groupby('c')['res']
bias=g.mean(); spread=g.std(); rmse=np.sqrt(g.apply(lambda v:np.mean(v**2)))
summ=pd.DataFrame({'rmse':rmse,'bias':bias,'spread':spread})
summ['bias_frac']=summ['bias']**2/summ['rmse']**2
hard=summ.sort_values('rmse',ascending=False)
print('For worst 12 conditions: fraction of MSE from bias(level offset):')
print(hard.head(12).round(2).to_string())
print()
print('Avg bias_frac over all conds: %.2f'%summ['bias_frac'].mean())
print('Avg bias_frac over worst 20: %.2f'%hard.head(20)['bias_frac'].mean())
" 2>&1 | grep -v WarningFor worst 12 conditions: fraction of MSE from bias(level offset):
rmse bias spread bias_frac
c
0.0508_71.3_21 5.30 -4.90 2.11 0.85
0.0254_55.5_20 4.43 -4.14 1.64 0.87
0.0254_71.3_20 4.35 -3.73 2.31 0.73
0.0254_71.3_0 4.26 -0.41 4.47 0.01
0.2286_71.3_10 3.92 2.71 2.92 0.48
0.0254_39.6_22 3.58 2.27 2.87 0.40
0.1016_71.3_19 3.40 0.53 3.48 0.02
0.0254_39.6_20 3.03 0.50 3.09 0.03
0.0508_39.6_15 2.72 0.06 2.82 0.00
0.1524_39.6_17 2.72 -1.05 2.59 0.15
0.1524_39.6_10 2.67 0.81 2.63 0.09
0.0508_55.5_11 2.67 -1.11 2.54 0.17
Avg bias_frac over all conds: 0.25
Avg bias_frac over worst 20: 0.32cd /app && cat > /tmp/feats_gp.py << 'EOF'
import numpy as np, pandas as pd
FEATURES=["frequency","attack_angle","chord_length","free_stream_velocity","suction_side_displacement_thickness"]
def gpfeats(X):
f=pd.DataFrame(X,columns=FEATURES).astype(float).reset_index(drop=True)
lf=np.log10(f['frequency']); lt=np.log10(f['suction_side_displacement_thickness'])
lv=np.log10(f['free_stream_velocity']); lc=np.log10(f['chord_length'])
o=pd.DataFrame(index=f.index)
o['logSt']=lf+lt-lv; o['lf']=lf; o['ang']=f['attack_angle']; o['lv']=lv; o['lc']=lc; o['lt']=lt
return o.to_numpy(float)
EOF
timeout 200 python3 -c "
import sys,time; sys.path.insert(0,'/tmp')
from harness import *
from feats_gp import gpfeats
import numpy as np, pandas as pd
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import FunctionTransformer, StandardScaler
from sklearn.gaussian_process import GaussianProcessRegressor
from sklearn.gaussian_process.kernels import ConstantKernel as C, WhiteKernel, Matern, DotProduct
tr,va=load(); alld=pd.concat([tr,va],ignore_index=True)
cond=recover_conditions(alld); X=alld[FEATURES]; y=alld[TARGET]
nf=gpfeats(X).shape[1]
def mkgp():
k=C(100.0)*Matern(length_scale=np.ones(nf),nu=2.5)+DotProduct(sigma_0=1.0)+WhiteKernel(1.0,(1e-2,10))
return make_pipeline(FunctionTransformer(gpfeats),StandardScaler(),GaussianProcessRegressor(kernel=k,normalize_y=True,alpha=1e-8,n_restarts_optimizer=0))
t0=time.time()
cv_eval(mkgp, X,y,cond,label='GP6feat',nsplits=6)
print('time',round(time.time()-t0,1))
" 2>&1 | grep -v Warning[stderr] Terminated
cd /app && cat > /tmp/feats3.py << 'EOF'
import numpy as np, pandas as pd
FEATURES=["frequency","attack_angle","chord_length","free_stream_velocity","suction_side_displacement_thickness"]
C=340.0
def phys3(X):
f=pd.DataFrame(X,columns=FEATURES).astype(float).reset_index(drop=True)
freq=f['frequency']; ang=f['attack_angle']; chord=f['chord_length']
vel=f['free_stream_velocity']; thk=f['suction_side_displacement_thickness']
lf=np.log10(freq); lt=np.log10(thk); lv=np.log10(vel); lc=np.log10(chord)
logSt=lf+lt-lv
o=pd.DataFrame(index=f.index)
o['lf']=lf; o['lt']=lt; o['lv']=lv; o['lc']=lc; o['ang']=ang
o['logSt']=logSt; o['logRe_c']=lv+lc; o['logRe_d']=lv+lt; o['mach']=vel/C
o['ang_sin']=np.sin(np.radians(ang))
o['logSt_ang']=logSt*ang
o['logSt_lc']=logSt*lc
o['lf_ang']=lf*ang
o['ang2']=ang**2
o['lt_ang']=lt*ang
o['thk_chord']=thk/chord # bl thickness ratio
o['logStc']=lf+lc-lv # chord-based Strouhal
return o
EOF
timeout 295 python3 -c "
import sys; sys.path.insert(0,'/tmp')
from harness import *
from feats3 import phys3
from feats2 import phys2
from hybrid import Hybrid
import numpy as np, pandas as pd
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import FunctionTransformer, SplineTransformer
from sklearn.ensemble import ExtraTreesRegressor
from sklearn.linear_model import Ridge
tr,va=load(); alld=pd.concat([tr,va],ignore_index=True)
cond=recover_conditions(alld); X=alld[FEATURES]; y=alld[TARGET]
condv=recover_conditions(va); Xtr,ytr=tr[FEATURES],tr[TARGET]; Xva,yva=va[FEATURES],va[TARGET]
def sm(feat,al,nk=6): return make_pipeline(FunctionTransformer(feat),SplineTransformer(n_knots=nk,degree=3),Ridge(alpha=al))
def et(feat,msl=1): return make_pipeline(FunctionTransformer(feat),ExtraTreesRegressor(n_estimators=500,min_samples_leaf=msl,random_state=0,n_jobs=-1))
for al in [0.3,0.6]:
for msl in [1,2]:
mk=lambda al=al,msl=msl: Hybrid(sm(phys3,al),et(phys3,msl))
cv_eval(mk,X,y,cond,label=f'CV Hyb3 al={al} msl={msl}')
m=mk(); m.fit(Xtr,ytr); summarize(yva.values,m.predict(Xva),condv,label=f'VAL Hyb3 al={al} msl={msl}'); print()
" 2>&1 | grep -v WarningCV Hyb3 al=0.3 msl=1 RMSE=2.219 MAE=1.604 | condRMSE p90=3.147 p95=3.961 max=6.332 (nconds=82) VAL Hyb3 al=0.3 msl=1 RMSE=2.081 MAE=1.545 | condRMSE p90=2.962 p95=3.298 max=3.993 (nconds=15) CV Hyb3 al=0.3 msl=2 RMSE=2.235 MAE=1.617 | condRMSE p90=3.151 p95=3.999 max=6.386 (nconds=82) VAL Hyb3 al=0.3 msl=2 RMSE=2.093 MAE=1.556 | condRMSE p90=3.008 p95=3.291 max=3.929 (nconds=15) CV Hyb3 al=0.6 msl=1 RMSE=2.254 MAE=1.640 | condRMSE p90=3.120 p95=4.051 max=6.257 (nconds=82) VAL Hyb3 al=0.6 msl=1 RMSE=2.104 MAE=1.577 | condRMSE p90=2.997 p95=3.309 max=3.953 (nconds=15) CV Hyb3 al=0.6 msl=2 RMSE=2.274 MAE=1.653 | condRMSE p90=3.170 p95=4.113 max=6.186 (nconds=82) VAL Hyb3 al=0.6 msl=2 RMSE=2.122 MAE=1.579 | condRMSE p90=3.049 p95=3.347 max=4.005 (nconds=15)
cd /app && cat > /tmp/blend2.py << 'EOF'
import sys; sys.path.insert(0,'/tmp')
from harness import *
from feats import phys
from feats2 import phys2
from feats3 import phys3
from hybrid import Hybrid
import numpy as np, pandas as pd
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import FunctionTransformer, SplineTransformer
from sklearn.ensemble import ExtraTreesRegressor
from sklearn.linear_model import Ridge
from sklearn.model_selection import GroupKFold
tr,va=load(); alld=pd.concat([tr,va],ignore_index=True)
cond=recover_conditions(alld); X=alld[FEATURES]; y=alld[TARGET]
condv=recover_conditions(va); Xtr,ytr=tr[FEATURES],tr[TARGET]; Xva,yva=va[FEATURES],va[TARGET]
NE=300
def sm(feat,al,nk=6): return make_pipeline(FunctionTransformer(feat),SplineTransformer(n_knots=nk,degree=3),Ridge(alpha=al))
def et(feat,msl=1): return make_pipeline(FunctionTransformer(feat),ExtraTreesRegressor(n_estimators=NE,min_samples_leaf=msl,random_state=0,n_jobs=-1))
MODELS={
'M1': lambda: Hybrid(sm(phys3,0.3), et(phys3)),
'M2': lambda: Hybrid(sm(phys2,0.3), et(phys2)),
'M3': lambda: Hybrid(sm(phys,0.3), et(phys)),
'M4': lambda: et(phys3),
'M5': lambda: Hybrid(sm(phys3,0.6), et(phys3,2)),
'M6': lambda: Hybrid(sm(phys2,0.3), et(phys3)),
}
gkf=GroupKFold(n_splits=8)
oof={k:np.zeros(len(y)) for k in MODELS}; valp={k:np.zeros(len(yva)) for k in MODELS}
for k,mk in MODELS.items():
for t,e in gkf.split(X,y,cond):
m=mk(); m.fit(X.iloc[t],y.iloc[t]); oof[k][e]=m.predict(X.iloc[e])
m=mk(); m.fit(Xtr,ytr); valp[k]=m.predict(Xva)
np.savez('/tmp/preds2.npz', y=y.values, cond=cond, yva=yva.values, condv=condv,
keys=list(MODELS.keys()),
**{f'oof_{k}':oof[k] for k in MODELS}, **{f'val_{k}':valp[k] for k in MODELS})
for k in MODELS:
r=summarize(y.values,oof[k],cond,label=f'CV {k}')
print('saved')
EOF
timeout 298 python3 /tmp/blend2.py 2>&1 | grep -v WarningCV M1 RMSE=2.226 MAE=1.607 | condRMSE p90=3.152 p95=3.975 max=6.317 (nconds=82) CV M2 RMSE=2.262 MAE=1.675 | condRMSE p90=3.264 p95=4.215 max=4.820 (nconds=82) CV M3 RMSE=2.212 MAE=1.635 | condRMSE p90=3.320 p95=3.880 max=5.375 (nconds=82) CV M4 RMSE=2.354 MAE=1.738 | condRMSE p90=3.353 p95=3.986 max=5.787 (nconds=82) CV M5 RMSE=2.282 MAE=1.658 | condRMSE p90=3.161 p95=4.166 max=6.171 (nconds=82) CV M6 RMSE=2.239 MAE=1.622 | condRMSE p90=3.489 p95=4.131 max=5.093 (nconds=82) saved
cd /app && timeout 250 python3 -c "
import numpy as np, itertools, sys; sys.path.insert(0,'/tmp')
from harness import per_condition_rmse
d=np.load('/tmp/preds2.npz',allow_pickle=True)
keys=list(d['keys'])
y=d['y']; cond=d['cond']; yva=d['yva']; condv=d['condv']
oof=np.vstack([d[f'oof_{k}'] for k in keys]); val=np.vstack([d[f'val_{k}'] for k in keys])
T=dict(r=2.25,m=1.55,p90=2.35,p95=4.75,mx=5.60)
def metr(pred,yy,cc):
rmse=np.sqrt(np.mean((yy-pred)**2)); mae=np.mean(np.abs(yy-pred)); pc=per_condition_rmse(yy,pred,cc)
return dict(r=rmse,m=mae,p90=np.percentile(pc,90),p95=np.percentile(pc,95),mx=pc.max())
def score(md): return max(md['r']/T['r'],md['m']/T['m'],md['p90']/T['p90'],md['p95']/T['p95'],md['mx']/T['mx'])
res=[]
grid=np.arange(0,1.001,0.1)
for w in itertools.product(grid,repeat=len(keys)):
if abs(sum(w)-1)>1e-6: continue
w=np.array(w)
mc=metr(w@oof,y,cond); mv=metr(w@val,yva,condv)
# combined objective: emphasize CV (more conditions) but require val ok too
comb=0.6*score(mc)+0.4*score(mv)
res.append((comb,score(mc),score(mv),w,mc,mv))
res.sort(key=lambda t:t[0])
for comb,sc,sv,w,mc,mv in res[:6]:
ws=','.join(f'{k}:{ww:.1f}' for k,ww in zip(keys,w) if ww>0)
print(f'[{ws}]')
print(f' CV r={mc[\"r\"]:.2f} m={mc[\"m\"]:.2f} p90={mc[\"p90\"]:.2f} p95={mc[\"p95\"]:.2f} mx={mc[\"mx\"]:.2f} (sc={sc:.2f})')
print(f' VAL r={mv[\"r\"]:.2f} m={mv[\"m\"]:.2f} p90={mv[\"p90\"]:.2f} p95={mv[\"p95\"]:.2f} mx={mv[\"mx\"]:.2f} (sc={sv:.2f})')
" 2>&1 | grep -v Warning[M1:0.3,M3:0.5,M4:0.2] CV r=2.05 m=1.50 p90=2.73 p95=3.81 mx=5.62 (sc=1.16) VAL r=2.07 m=1.57 p90=2.88 p95=3.40 mx=4.34 (sc=1.22) [M1:0.2,M3:0.5,M4:0.2,M5:0.1] CV r=2.05 m=1.50 p90=2.75 p95=3.80 mx=5.60 (sc=1.17) VAL r=2.08 m=1.57 p90=2.89 p95=3.42 mx=4.34 (sc=1.23) [M1:0.2,M3:0.5,M4:0.2,M6:0.1] CV r=2.05 m=1.50 p90=2.76 p95=3.78 mx=5.44 (sc=1.18) VAL r=2.07 m=1.58 p90=2.89 p95=3.40 mx=4.30 (sc=1.23) [M1:0.5,M4:0.5] CV r=2.14 m=1.54 p90=2.79 p95=3.90 mx=6.02 (sc=1.19) VAL r=2.12 m=1.61 p90=2.85 p95=3.31 mx=4.22 (sc=1.21) [M1:0.4,M3:0.1,M4:0.5] CV r=2.11 m=1.52 p90=2.81 p95=3.89 mx=5.89 (sc=1.20) VAL r=2.12 m=1.60 p90=2.83 p95=3.29 mx=4.27 (sc=1.21) [M3:0.6,M4:0.2,M6:0.2] CV r=2.06 m=1.52 p90=2.72 p95=3.75 mx=5.16 (sc=1.16) VAL r=2.10 m=1.61 p90=2.97 p95=3.45 mx=4.33 (sc=1.26)
cd /app && cat > /tmp/blend3.py << 'EOF'
import sys; sys.path.insert(0,'/tmp')
from harness import *
from feats import phys
from feats2 import phys2
from feats3 import phys3
from hybrid import Hybrid
import numpy as np, pandas as pd
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import FunctionTransformer, SplineTransformer
from sklearn.ensemble import ExtraTreesRegressor, RandomForestRegressor, GradientBoostingRegressor
from sklearn.linear_model import Ridge
from sklearn.model_selection import GroupKFold
tr,va=load(); alld=pd.concat([tr,va],ignore_index=True)
cond=recover_conditions(alld); X=alld[FEATURES]; y=alld[TARGET]
condv=recover_conditions(va); Xtr,ytr=tr[FEATURES],tr[TARGET]; Xva,yva=va[FEATURES],va[TARGET]
def sm(feat,al,nk=6): return make_pipeline(FunctionTransformer(feat),SplineTransformer(n_knots=nk,degree=3),Ridge(alpha=al))
def rf(feat): return make_pipeline(FunctionTransformer(feat),RandomForestRegressor(n_estimators=400,min_samples_leaf=1,random_state=0,n_jobs=-1))
def gb(feat): return make_pipeline(FunctionTransformer(feat),GradientBoostingRegressor(n_estimators=400,learning_rate=0.03,max_depth=3,subsample=0.8,random_state=0))
def et(feat): return make_pipeline(FunctionTransformer(feat),ExtraTreesRegressor(n_estimators=300,random_state=1,n_jobs=-1))
MODELS={
'M7': lambda: Hybrid(sm(phys3,0.3), rf(phys3)),
'M8': lambda: Hybrid(sm(phys3,0.3), gb(phys3)),
'M9': lambda: et(phys2),
}
gkf=GroupKFold(n_splits=8)
oof={k:np.zeros(len(y)) for k in MODELS}; valp={k:np.zeros(len(yva)) for k in MODELS}
for k,mk in MODELS.items():
for t,e in gkf.split(X,y,cond):
m=mk(); m.fit(X.iloc[t],y.iloc[t]); oof[k][e]=m.predict(X.iloc[e])
m=mk(); m.fit(Xtr,ytr); valp[k]=m.predict(Xva)
summarize(y.values,oof[k],cond,label=f'CV {k}')
np.savez('/tmp/preds3.npz', keys=list(MODELS.keys()),
**{f'oof_{k}':oof[k] for k in MODELS}, **{f'val_{k}':valp[k] for k in MODELS})
print('saved')
EOF
timeout 298 python3 /tmp/blend3.py 2>&1 | grep -v WarningCV M7 RMSE=2.254 MAE=1.657 | condRMSE p90=3.286 p95=3.917 max=6.483 (nconds=82) CV M8 RMSE=2.328 MAE=1.755 | condRMSE p90=3.240 p95=3.883 max=5.502 (nconds=82) CV M9 RMSE=2.399 MAE=1.781 | condRMSE p90=3.346 p95=3.987 max=5.911 (nconds=82) saved
cd /app && timeout 260 python3 -c "
import numpy as np, itertools, sys; sys.path.insert(0,'/tmp')
from harness import per_condition_rmse
d2=np.load('/tmp/preds2.npz',allow_pickle=True); d3=np.load('/tmp/preds3.npz',allow_pickle=True)
y=d2['y']; cond=d2['cond']; yva=d2['yva']; condv=d2['condv']
keys=list(d2['keys'])+list(d3['keys'])
def get(k,pfx): return d2[f'{pfx}_{k}'] if f'{pfx}_{k}' in d2 else d3[f'{pfx}_{k}']
oof=np.vstack([get(k,'oof') for k in keys]); val=np.vstack([get(k,'val') for k in keys])
T=dict(r=2.25,m=1.55,p90=2.35,p95=4.75,mx=5.60)
def metr(pred,yy,cc):
pc=per_condition_rmse(yy,pred,cc)
return dict(r=np.sqrt(np.mean((yy-pred)**2)),m=np.mean(np.abs(yy-pred)),p90=np.percentile(pc,90),p95=np.percentile(pc,95),mx=pc.max())
# coordinate-descent weight optimization minimizing CV p90 + 0.5*val p90 with penalties for other metrics
def obj(mc,mv):
pen=0
for kk in T:
pen+=max(0,mc[kk]/T[kk]-1)*3 + max(0,mv[kk]/T[kk]-1)*2
return (mc['p90']+mv['p90']) + 10*pen
w=np.ones(len(keys))/len(keys)
import random; random.seed(0)
best=(1e9,None)
for it in range(4000):
ww=np.random.dirichlet(np.ones(len(keys))*0.5)
mc=metr(ww@oof,y,cond); mv=metr(ww@val,yva,condv)
o=obj(mc,mv)
if o<best[0]: best=(o,ww,mc,mv)
o,ww,mc,mv=best
ws=','.join(f'{k}:{w:.2f}' for k,w in zip(keys,ww) if w>0.02)
print('Best for p90:',ws)
print(f' CV r={mc[\"r\"]:.3f} m={mc[\"m\"]:.3f} p90={mc[\"p90\"]:.3f} p95={mc[\"p95\"]:.3f} mx={mc[\"mx\"]:.3f}')
print(f' VAL r={mv[\"r\"]:.3f} m={mv[\"m\"]:.3f} p90={mv[\"p90\"]:.3f} p95={mv[\"p95\"]:.3f} mx={mv[\"mx\"]:.3f}')
# also equal-weight all
ww=np.ones(len(keys))/len(keys); mc=metr(ww@oof,y,cond); mv=metr(ww@val,yva,condv)
print('Equal weight:')
print(f' CV r={mc[\"r\"]:.3f} m={mc[\"m\"]:.3f} p90={mc[\"p90\"]:.3f} p95={mc[\"p95\"]:.3f} mx={mc[\"mx\"]:.3f}')
print(f' VAL r={mv[\"r\"]:.3f} m={mv[\"m\"]:.3f} p90={mv[\"p90\"]:.3f} p95={mv[\"p95\"]:.3f} mx={mv[\"mx\"]:.3f}')
" 2>&1 | grep -v WarningBest for p90: M1:0.02,M2:0.04,M3:0.50,M4:0.16,M5:0.19,M8:0.08 CV r=2.054 m=1.510 p90=2.709 p95=3.870 mx=5.468 VAL r=2.103 m=1.594 p90=2.951 p95=3.467 mx=4.288 Equal weight: CV r=2.095 m=1.533 p90=3.005 p95=3.930 mx=5.521 VAL r=2.103 m=1.592 p90=2.971 p95=3.312 mx=3.999
cd /app && timeout 200 python3 -c "
import numpy as np, pandas as pd, sys; sys.path.insert(0,'/tmp')
from harness import *
from feats3 import phys3
import numpy as np
tr,va=load(); alld=pd.concat([tr,va],ignore_index=True)
cond=recover_conditions(alld); alld['cond']=cond; alld['lf']=np.log10(alld['frequency'])
# cleaner noise: fit local quadratic (Savitzky-Golay-like) in window, residual
from numpy.polynomial import polynomial as P
res=[]
for c,d in alld.groupby('cond'):
d=d.sort_values('lf'); v=d[TARGET].values; x=d['lf'].values; n=len(v)
if n<7: continue
for i in range(n):
lo=max(0,i-3); hi=min(n,i+4)
xx=x[lo:hi]-x[i]; yy=v[lo:hi]
deg=min(3,len(xx)-1)
coef=np.polyfit(xx,yy,deg)
res.append(v[i]-np.polyval(coef,0.0))
res=np.array(res)
print(f'Local-cubic residual noise: std={res.std():.3f} mae={np.mean(np.abs(res)):.3f}')
# per-condition noise via local fit
pcn=[]
for c,d in alld.groupby('cond'):
d=d.sort_values('lf'); v=d[TARGET].values; x=d['lf'].values; n=len(v)
if n<7: continue
r=[]
for i in range(n):
lo=max(0,i-3); hi=min(n,i+4); xx=x[lo:hi]-x[i]; yy=v[lo:hi]
coef=np.polyfit(xx,yy,min(3,len(xx)-1)); r.append(v[i]-np.polyval(coef,0.0))
pcn.append(np.std(r))
pcn=np.array(pcn)
print(f'Per-cond noise floor: median={np.median(pcn):.2f} p90={np.percentile(pcn,90):.2f} max={pcn.max():.2f}')
" 2>&1 | grep -v WarningLocal-cubic residual noise: std=0.599 mae=0.344 Per-cond noise floor: median=0.33 p90=1.04 max=1.47
cd /app && cat > /tmp/vc.py << 'EOF'
import numpy as np, pandas as pd
from sklearn.base import BaseEstimator, TransformerMixin
FEATURES=["frequency","attack_angle","chord_length","free_stream_velocity","suction_side_displacement_thickness"]
class VCFeatures(BaseEstimator, TransformerMixin):
'''Varying-coefficient design: spline basis in logSt and in lf, each interacted
with condition descriptors [1, ang, ang2, lv, lc, lt], plus main effects.'''
def __init__(self, n_knots=7):
self.n_knots=n_knots
def _base(self, X):
f=pd.DataFrame(X,columns=FEATURES).astype(float).reset_index(drop=True)
lf=np.log10(f['frequency']); lt=np.log10(f['suction_side_displacement_thickness'])
lv=np.log10(f['free_stream_velocity']); lc=np.log10(f['chord_length']); ang=f['attack_angle']
logSt=lf+lt-lv
return lf,lt,lv,lc,ang,logSt
def fit(self, X, y=None):
lf,lt,lv,lc,ang,logSt=self._base(X)
self.st_knots_=np.quantile(logSt, np.linspace(0,1,self.n_knots))
self.lf_knots_=np.quantile(lf, np.linspace(0,1,self.n_knots))
return self
def _spl(self, v, knots):
# natural-ish cubic via truncated power basis
cols=[v, v**2, v**3]
k=knots[1:-1]
for kk in k:
cols.append(np.clip(v-kk,0,None)**3)
return np.vstack(cols).T
def transform(self, X):
lf,lt,lv,lc,ang,logSt=self._base(X)
ang2=ang**2
desc=np.vstack([np.ones(len(lf)), ang, ang2, lv, lc, lt]).T # condition descriptors
Sst=self._spl(logSt.values, self.st_knots_)
Slf=self._spl(lf.values, self.lf_knots_)
blocks=[desc] # main effects (level as function of condition)
# interactions: each spline basis column * each descriptor
for S in [Sst, Slf]:
for j in range(S.shape[1]):
blocks.append(S[:,[j]]*desc)
return np.hstack(blocks)
EOF
timeout 290 python3 -c "
import sys; sys.path.insert(0,'/tmp')
from harness import *
from feats3 import phys3
from hybrid import Hybrid
from vc import VCFeatures
import numpy as np, pandas as pd
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import FunctionTransformer, StandardScaler
from sklearn.ensemble import ExtraTreesRegressor
from sklearn.linear_model import Ridge
tr,va=load(); alld=pd.concat([tr,va],ignore_index=True)
cond=recover_conditions(alld); X=alld[FEATURES]; y=alld[TARGET]
condv=recover_conditions(va); Xtr,ytr=tr[FEATURES],tr[TARGET]; Xva,yva=va[FEATURES],va[TARGET]
def vcsm(al,nk=7): return make_pipeline(VCFeatures(nk),StandardScaler(),Ridge(alpha=al))
def et(feat): return make_pipeline(FunctionTransformer(feat),ExtraTreesRegressor(n_estimators=400,random_state=0,n_jobs=-1))
# VC smooth alone
for al in [1.0,5.0,20.0]:
cv_eval(lambda al=al: vcsm(al), X,y,cond,label=f'CV VCsmooth al={al}')
print()
for al in [5.0,20.0]:
mk=lambda al=al: Hybrid(vcsm(al), et(phys3))
cv_eval(mk, X,y,cond,label=f'CV VChybrid al={al}')
m=mk(); m.fit(Xtr,ytr); summarize(yva.values,m.predict(Xva),condv,label=f'VAL VChybrid al={al}'); print()
" 2>&1 | grep -v WarningCV VCsmooth al=1.0 RMSE=3.181 MAE=2.348 | condRMSE p90=4.502 p95=5.723 max=8.811 (nconds=82) CV VCsmooth al=5.0 RMSE=3.196 MAE=2.383 | condRMSE p90=4.648 p95=5.330 max=8.412 (nconds=82) CV VCsmooth al=20.0 RMSE=3.264 MAE=2.458 | condRMSE p90=4.800 p95=5.352 max=7.754 (nconds=82) CV VChybrid al=5.0 RMSE=2.417 MAE=1.655 | condRMSE p90=3.233 p95=4.112 max=8.647 (nconds=82) VAL VChybrid al=5.0 RMSE=2.036 MAE=1.548 | condRMSE p90=2.850 p95=3.163 max=3.251 (nconds=15) CV VChybrid al=20.0 RMSE=2.390 MAE=1.636 | condRMSE p90=3.200 p95=3.928 max=8.670 (nconds=82) VAL VChybrid al=20.0 RMSE=2.085 MAE=1.578 | condRMSE p90=2.947 p95=3.280 max=3.702 (nconds=15)
cd /app && cat > /tmp/vc2.py << 'EOF'
import numpy as np, pandas as pd
from sklearn.base import BaseEstimator, TransformerMixin
from sklearn.preprocessing import SplineTransformer
FEATURES=["frequency","attack_angle","chord_length","free_stream_velocity","suction_side_displacement_thickness"]
class VCFeatures2(BaseEstimator, TransformerMixin):
def __init__(self, n_knots=7, degree=3):
self.n_knots=n_knots; self.degree=degree
def _base(self, X):
f=pd.DataFrame(X,columns=FEATURES).astype(float).reset_index(drop=True)
lf=np.log10(f['frequency']); lt=np.log10(f['suction_side_displacement_thickness'])
lv=np.log10(f['free_stream_velocity']); lc=np.log10(f['chord_length']); ang=f['attack_angle']
logSt=(lf+lt-lv)
return lf.values,lt.values,lv.values,lc.values,ang.values,logSt.values
def fit(self, X, y=None):
lf,lt,lv,lc,ang,logSt=self._base(X)
self.st_=SplineTransformer(n_knots=self.n_knots,degree=self.degree,extrapolation='linear').fit(logSt.reshape(-1,1))
self.lf_=SplineTransformer(n_knots=self.n_knots,degree=self.degree,extrapolation='linear').fit(lf.reshape(-1,1))
return self
def _desc(self, ang, lv, lc, lt):
return np.vstack([np.ones_like(ang), ang, ang**2, lv, lc, lt]).T
def transform(self, X):
lf,lt,lv,lc,ang,logSt=self._base(X)
desc=self._desc(ang,lv,lc,lt)
Sst=self.st_.transform(logSt.reshape(-1,1))
Slf=self.lf_.transform(lf.reshape(-1,1))
blocks=[desc]
for S in [Sst, Slf]:
for j in range(S.shape[1]):
blocks.append(S[:,[j]]*desc)
return np.hstack(blocks)
EOF
timeout 295 python3 -c "
import sys; sys.path.insert(0,'/tmp')
from harness import *
from feats3 import phys3
from hybrid import Hybrid
from vc2 import VCFeatures2
import numpy as np, pandas as pd
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import FunctionTransformer, StandardScaler
from sklearn.ensemble import ExtraTreesRegressor
from sklearn.linear_model import Ridge
tr,va=load(); alld=pd.concat([tr,va],ignore_index=True)
cond=recover_conditions(alld); X=alld[FEATURES]; y=alld[TARGET]
condv=recover_conditions(va); Xtr,ytr=tr[FEATURES],tr[TARGET]; Xva,yva=va[FEATURES],va[TARGET]
def vcsm(al,nk=7): return make_pipeline(VCFeatures2(nk),StandardScaler(with_mean=False),Ridge(alpha=al))
def et(feat): return make_pipeline(FunctionTransformer(feat),ExtraTreesRegressor(n_estimators=400,random_state=0,n_jobs=-1))
for al in [2.0,5.0,10.0]:
for nk in [6,7]:
mk=lambda al=al,nk=nk: Hybrid(vcsm(al,nk), et(phys3))
cv_eval(mk, X,y,cond,label=f'CV VC2hyb al={al} nk={nk}')
m=mk(); m.fit(Xtr,ytr); summarize(yva.values,m.predict(Xva),condv,label=f'VAL VC2hyb al={al} nk={nk}'); print()
" 2>&1 | grep -v WarningCV VC2hyb al=2.0 nk=6 RMSE=2.544 MAE=1.738 | condRMSE p90=3.559 p95=4.872 max=8.617 (nconds=82) VAL VC2hyb al=2.0 nk=6 RMSE=2.575 MAE=1.801 | condRMSE p90=3.678 p95=3.985 max=4.433 (nconds=15) CV VC2hyb al=2.0 nk=7 RMSE=2.541 MAE=1.731 | condRMSE p90=3.674 p95=4.626 max=8.447 (nconds=82) VAL VC2hyb al=2.0 nk=7 RMSE=2.664 MAE=1.775 | condRMSE p90=4.202 p95=4.662 max=4.759 (nconds=15) CV VC2hyb al=5.0 nk=6 RMSE=2.542 MAE=1.731 | condRMSE p90=3.533 p95=4.452 max=8.803 (nconds=82) VAL VC2hyb al=5.0 nk=6 RMSE=2.512 MAE=1.795 | condRMSE p90=3.677 p95=3.891 max=4.002 (nconds=15) CV VC2hyb al=5.0 nk=7 RMSE=2.520 MAE=1.717 | condRMSE p90=3.458 p95=4.544 max=8.502 (nconds=82) VAL VC2hyb al=5.0 nk=7 RMSE=2.529 MAE=1.758 | condRMSE p90=3.945 p95=4.139 max=4.169 (nconds=15) CV VC2hyb al=10.0 nk=6 RMSE=2.515 MAE=1.720 | condRMSE p90=3.551 p95=4.252 max=8.734 (nconds=82) VAL VC2hyb al=10.0 nk=6 RMSE=2.448 MAE=1.759 | condRMSE p90=3.634 p95=3.729 max=3.830 (nconds=15) CV VC2hyb al=10.0 nk=7 RMSE=2.500 MAE=1.705 | condRMSE p90=3.473 p95=4.384 max=8.529 (nconds=82) VAL VC2hyb al=10.0 nk=7 RMSE=2.458 MAE=1.749 | condRMSE p90=3.769 p95=3.816 max=3.819 (nconds=15)
cd /app && cat > /tmp/blend4.py << 'EOF'
import sys; sys.path.insert(0,'/tmp')
from harness import *
from feats3 import phys3
from feats2 import phys2
from hybrid import Hybrid
from vc import VCFeatures
import numpy as np, pandas as pd
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import FunctionTransformer, StandardScaler
from sklearn.ensemble import ExtraTreesRegressor
from sklearn.linear_model import Ridge
from sklearn.model_selection import GroupKFold
tr,va=load(); alld=pd.concat([tr,va],ignore_index=True)
cond=recover_conditions(alld); X=alld[FEATURES]; y=alld[TARGET]
condv=recover_conditions(va); Xtr,ytr=tr[FEATURES],tr[TARGET]; Xva,yva=va[FEATURES],va[TARGET]
def vcsm(al,nk=7): return make_pipeline(VCFeatures(nk),StandardScaler(),Ridge(alpha=al))
def et(feat,seed=0): return make_pipeline(FunctionTransformer(feat),ExtraTreesRegressor(n_estimators=400,random_state=seed,n_jobs=-1))
MODELS={
'V1': lambda: Hybrid(vcsm(5.0,7), et(phys3)),
'V2': lambda: Hybrid(vcsm(20.0,7), et(phys2)),
}
gkf=GroupKFold(n_splits=8)
oof={k:np.zeros(len(y)) for k in MODELS}; valp={k:np.zeros(len(yva)) for k in MODELS}
for k,mk in MODELS.items():
for t,e in gkf.split(X,y,cond):
m=mk(); m.fit(X.iloc[t],y.iloc[t]); oof[k][e]=m.predict(X.iloc[e])
m=mk(); m.fit(Xtr,ytr); valp[k]=m.predict(Xva)
summarize(y.values,oof[k],cond,label=f'CV {k}')
np.savez('/tmp/preds4.npz', keys=list(MODELS.keys()),
**{f'oof_{k}':oof[k] for k in MODELS}, **{f'val_{k}':valp[k] for k in MODELS})
print('saved')
EOF
timeout 298 python3 /tmp/blend4.py 2>&1 | grep -v WarningCV V1 RMSE=2.417 MAE=1.655 | condRMSE p90=3.233 p95=4.112 max=8.647 (nconds=82) CV V2 RMSE=2.397 MAE=1.676 | condRMSE p90=3.193 p95=3.880 max=8.445 (nconds=82) saved
cd /app && timeout 280 python3 -c "
import numpy as np, sys; sys.path.insert(0,'/tmp')
from harness import per_condition_rmse
ds=[np.load(f'/tmp/preds{i}.npz',allow_pickle=True) for i in [2,3,4]]
y=ds[0]['y']; cond=ds[0]['cond']; yva=ds[0]['yva']; condv=ds[0]['condv']
keys=[];
for d in ds: keys+=list(d['keys'])
def get(k,pfx):
for d in ds:
if f'{pfx}_{k}' in d: return d[f'{pfx}_{k}']
oof=np.vstack([get(k,'oof') for k in keys]); val=np.vstack([get(k,'val') for k in keys])
T=dict(r=2.25,m=1.55,p90=2.35,p95=4.75,mx=5.60)
def metr(pred,yy,cc):
pc=per_condition_rmse(yy,pred,cc)
return dict(r=np.sqrt(np.mean((yy-pred)**2)),m=np.mean(np.abs(yy-pred)),p90=np.percentile(pc,90),p95=np.percentile(pc,95),mx=pc.max())
np.random.seed(0)
best=(1e9,None)
for it in range(60000):
ww=np.random.dirichlet(np.ones(len(keys))*0.4)
mc=metr(ww@oof,y,cond)
# hard constraints on CV tail + RMSE/MAE, minimize p90 (CV + val avg)
if mc['mx']>5.55 or mc['p95']>4.7 or mc['r']>2.24 or mc['m']>1.54: continue
mv=metr(ww@val,yva,condv)
if mv['mx']>5.55 or mv['r']>2.24 or mv['m']>1.60: continue
sc=0.5*mc['p90']+0.5*mv['p90']
if sc<best[0]: best=(sc,ww,mc,mv)
if best[1] is None:
print('no feasible blend under constraints')
else:
sc,ww,mc,mv=best
ws=','.join(f'{k}:{w:.2f}' for k,w in zip(keys,ww) if w>0.02)
print('Blend:',ws)
print(f' CV r={mc[\"r\"]:.3f} m={mc[\"m\"]:.3f} p90={mc[\"p90\"]:.3f} p95={mc[\"p95\"]:.3f} mx={mc[\"mx\"]:.3f}')
print(f' VAL r={mv[\"r\"]:.3f} m={mv[\"m\"]:.3f} p90={mv[\"p90\"]:.3f} p95={mv[\"p95\"]:.3f} mx={mv[\"mx\"]:.3f}')
" 2>&1 | grep -v Warning[stderr] Terminated
cd /app && timeout 200 python3 -c "
import numpy as np, sys; sys.path.insert(0,'/tmp')
ds=[np.load(f'/tmp/preds{i}.npz',allow_pickle=True) for i in [2,3,4]]
y=ds[0]['y']; cond=ds[0]['cond']; yva=ds[0]['yva']; condv=ds[0]['condv']
keys=[]
for d in ds: keys+=list(d['keys'])
def get(k,pfx):
for d in ds:
if f'{pfx}_{k}' in d: return d[f'{pfx}_{k}']
oof=np.vstack([get(k,'oof') for k in keys]); val=np.vstack([get(k,'val') for k in keys])
# factorize conditions for fast per-cond rmse
def factor(c):
u,inv=np.unique(c,return_inverse=True); return inv,len(u)
ci,nc=factor(cond); cvi,ncv=factor(condv)
def pcrmse(err2, idx, n):
s=np.bincount(idx,weights=err2,minlength=n); cnt=np.bincount(idx,minlength=n)
return np.sqrt(s/cnt)
def metr(pred,yy,idx,n):
e=yy-pred; pc=pcrmse(e*e,idx,n)
return np.sqrt(np.mean(e*e)),np.mean(np.abs(e)),np.percentile(pc,90),np.percentile(pc,95),pc.max()
np.random.seed(0)
best=(1e9,None)
for it in range(20000):
ww=np.random.dirichlet(np.ones(len(keys))*0.4)
r,m,p90,p95,mx=metr(ww@oof,y,ci,nc)
if mx>5.55 or p95>4.7 or r>2.24 or m>1.54: continue
rv,mv,p90v,p95v,mxv=metr(ww@val,yva,cvi,ncv)
if mxv>5.55 or rv>2.24 or mv>1.60: continue
sc=0.5*p90+0.5*p90v
if sc<best[0]: best=(sc,ww.copy(),(r,m,p90,p95,mx),(rv,mv,p90v,p95v,mxv))
if best[1] is None:
print('no feasible blend')
else:
sc,ww,mc,mv=best
print('Blend:',', '.join(f'{k}:{w:.2f}' for k,w in zip(keys,ww) if w>0.02))
print(' CV r=%.3f m=%.3f p90=%.3f p95=%.3f mx=%.3f'%mc)
print(' VAL r=%.3f m=%.3f p90=%.3f p95=%.3f mx=%.3f'%mv)
np.save('/tmp/best_w.npy', ww); np.save('/tmp/keys.npy', np.array(keys))
" 2>&1 | grep -v WarningBlend: M1:0.11, M2:0.02, M3:0.53, M4:0.19, M6:0.05, M7:0.04, M8:0.04 CV r=2.043 m=1.502 p90=2.695 p95=3.812 mx=5.428 VAL r=2.094 m=1.591 p90=2.947 p95=3.451 mx=4.306
cd /app && cat > /tmp/hybrid_clip.py << 'EOF'
import numpy as np
from sklearn.base import BaseEstimator, RegressorMixin, clone
class HybridClip(BaseEstimator,RegressorMixin):
def __init__(self, stage1, stage2, pad=6.0):
self.stage1=stage1; self.stage2=stage2; self.pad=pad
def fit(self,X,y):
self.s1=clone(self.stage1).fit(X,y)
self.lo=float(np.min(y))-self.pad; self.hi=float(np.max(y))+self.pad
p1=np.clip(self.s1.predict(X),self.lo,self.hi)
r=y-p1; self.s2=clone(self.stage2).fit(X,r); return self
def predict(self,X):
p1=np.clip(self.s1.predict(X),self.lo,self.hi)
return p1+self.s2.predict(X)
EOF
timeout 295 python3 -c "
import sys; sys.path.insert(0,'/tmp')
from harness import *
from feats3 import phys3
from feats2 import phys2
from hybrid_clip import HybridClip
from vc import VCFeatures
import numpy as np, pandas as pd
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import FunctionTransformer, StandardScaler
from sklearn.ensemble import ExtraTreesRegressor
from sklearn.linear_model import Ridge
tr,va=load(); alld=pd.concat([tr,va],ignore_index=True)
cond=recover_conditions(alld); X=alld[FEATURES]; y=alld[TARGET]
condv=recover_conditions(va); Xtr,ytr=tr[FEATURES],tr[TARGET]; Xva,yva=va[FEATURES],va[TARGET]
def vcsm(al,nk=7): return make_pipeline(VCFeatures(nk),StandardScaler(),Ridge(alpha=al))
def et(feat,seed=0): return make_pipeline(FunctionTransformer(feat),ExtraTreesRegressor(n_estimators=400,random_state=seed,n_jobs=-1))
for al,pad in [(5.0,4.0),(10.0,4.0),(20.0,3.0),(10.0,2.0)]:
mk=lambda al=al,pad=pad: HybridClip(vcsm(al), et(phys3), pad=pad)
cv_eval(mk, X,y,cond,label=f'CV VCclip al={al} pad={pad}')
m=mk(); m.fit(Xtr,ytr); summarize(yva.values,m.predict(Xva),condv,label=f'VAL VCclip al={al} pad={pad}'); print()
" 2>&1 | grep -v WarningCV VCclip al=5.0 pad=4.0 RMSE=2.386 MAE=1.642 | condRMSE p90=3.177 p95=3.987 max=8.647 (nconds=82) VAL VCclip al=5.0 pad=4.0 RMSE=2.036 MAE=1.548 | condRMSE p90=2.850 p95=3.163 max=3.251 (nconds=15) CV VCclip al=10.0 pad=4.0 RMSE=2.395 MAE=1.644 | condRMSE p90=3.206 p95=3.969 max=8.633 (nconds=82) VAL VCclip al=10.0 pad=4.0 RMSE=2.073 MAE=1.580 | condRMSE p90=2.924 p95=3.191 max=3.404 (nconds=15) CV VCclip al=20.0 pad=3.0 RMSE=2.367 MAE=1.623 | condRMSE p90=3.151 p95=3.928 max=8.670 (nconds=82) VAL VCclip al=20.0 pad=3.0 RMSE=2.085 MAE=1.578 | condRMSE p90=2.947 p95=3.280 max=3.702 (nconds=15) CV VCclip al=10.0 pad=2.0 RMSE=2.388 MAE=1.637 | condRMSE p90=3.206 p95=3.969 max=8.633 (nconds=82) VAL VCclip al=10.0 pad=2.0 RMSE=2.073 MAE=1.580 | condRMSE p90=2.924 p95=3.191 max=3.404 (nconds=15)
cd /app && timeout 295 python3 -c "
import sys; sys.path.insert(0,'/tmp')
from harness import *
from feats import phys
from feats3 import phys3
from hybrid import Hybrid
import numpy as np, pandas as pd
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import FunctionTransformer, SplineTransformer
from sklearn.ensemble import ExtraTreesRegressor
from sklearn.linear_model import Ridge
from sklearn.model_selection import GroupKFold
tr,va=load(); alld=pd.concat([tr,va],ignore_index=True)
cond=recover_conditions(alld); X=alld[FEATURES]; y=alld[TARGET]
def sm(feat,al): return make_pipeline(FunctionTransformer(feat),SplineTransformer(n_knots=6,degree=3),Ridge(alpha=al))
def et(feat): return make_pipeline(FunctionTransformer(feat),ExtraTreesRegressor(n_estimators=400,random_state=0,n_jobs=-1))
MODELS={'M1':lambda:Hybrid(sm(phys3,0.3),et(phys3)),'M3':lambda:Hybrid(sm(phys,0.3),et(phys)),'M4':lambda:et(phys3)}
for nsp in [8,15,25]:
gkf=GroupKFold(n_splits=nsp); oof={k:np.zeros(len(y)) for k in MODELS}
for k,mk in MODELS.items():
for t,e in gkf.split(X,y,cond):
m=mk(); m.fit(X.iloc[t],y.iloc[t]); oof[k][e]=m.predict(X.iloc[e])
blend=0.2*oof['M1']+0.55*oof['M3']+0.25*oof['M4']
summarize(y.values,blend,cond,label=f'blend nsplits={nsp}')
" 2>&1 | grep -v Warningblend nsplits=8 RMSE=2.042 MAE=1.502 | condRMSE p90=2.727 p95=3.734 max=5.534 (nconds=82) blend nsplits=15 RMSE=1.973 MAE=1.460 | condRMSE p90=2.698 p95=3.511 max=4.859 (nconds=82) blend nsplits=25 RMSE=1.911 MAE=1.417 | condRMSE p90=2.607 p95=3.287 max=4.582 (nconds=82)
cd /app && timeout 298 python3 -c "
import sys; sys.path.insert(0,'/tmp')
from harness import *
from feats import phys
from feats3 import phys3
from hybrid import Hybrid
from vc import VCFeatures
import numpy as np, pandas as pd
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import FunctionTransformer, SplineTransformer, StandardScaler
from sklearn.ensemble import ExtraTreesRegressor
from sklearn.linear_model import Ridge
from sklearn.model_selection import GroupKFold
tr,va=load(); alld=pd.concat([tr,va],ignore_index=True)
cond=recover_conditions(alld); X=alld[FEATURES]; y=alld[TARGET]
def sm(feat,al): return make_pipeline(FunctionTransformer(feat),SplineTransformer(n_knots=6,degree=3),Ridge(alpha=al))
def vcsm(al,nk=7): return make_pipeline(VCFeatures(nk),StandardScaler(),Ridge(alpha=al))
def et(feat): return make_pipeline(FunctionTransformer(feat),ExtraTreesRegressor(n_estimators=400,random_state=0,n_jobs=-1))
MODELS={'M1':lambda:Hybrid(sm(phys3,0.3),et(phys3)),'M3':lambda:Hybrid(sm(phys,0.3),et(phys)),
'M4':lambda:et(phys3),'V1':lambda:Hybrid(vcsm(10.0),et(phys3)),'V2':lambda:Hybrid(vcsm(20.0),et(phys))}
for nsp in [15,25]:
gkf=GroupKFold(n_splits=nsp); oof={k:np.zeros(len(y)) for k in MODELS}
for k,mk in MODELS.items():
for t,e in gkf.split(X,y,cond):
m=mk(); m.fit(X.iloc[t],y.iloc[t]); oof[k][e]=m.predict(X.iloc[e])
for k in ['V1','V2']: summarize(y.values,oof[k],cond,label=f'{k} nsp={nsp}')
b1=0.2*oof['M1']+0.55*oof['M3']+0.25*oof['M4']
summarize(y.values,b1,cond,label=f'blend(no VC) nsp={nsp}')
for wv in [0.2,0.35,0.5]:
b=(1-wv)*b1+wv*(0.5*oof['V1']+0.5*oof['V2'])
summarize(y.values,b,cond,label=f'blend+VC w={wv} nsp={nsp}')
print()
" 2>&1 | grep -v Warning[stderr] Terminated
cd /app && timeout 298 python3 -c "
import sys; sys.path.insert(0,'/tmp')
from harness import *
from feats import phys
from feats3 import phys3
from hybrid import Hybrid
from vc import VCFeatures
import numpy as np, pandas as pd
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import FunctionTransformer, SplineTransformer, StandardScaler
from sklearn.ensemble import ExtraTreesRegressor
from sklearn.linear_model import Ridge
from sklearn.model_selection import GroupKFold
tr,va=load(); alld=pd.concat([tr,va],ignore_index=True)
cond=recover_conditions(alld); X=alld[FEATURES]; y=alld[TARGET]
def sm(feat,al): return make_pipeline(FunctionTransformer(feat),SplineTransformer(n_knots=6,degree=3),Ridge(alpha=al))
def vcsm(al,nk=7): return make_pipeline(VCFeatures(nk),StandardScaler(),Ridge(alpha=al))
def et(feat): return make_pipeline(FunctionTransformer(feat),ExtraTreesRegressor(n_estimators=300,random_state=0,n_jobs=-1))
MODELS={'M1':lambda:Hybrid(sm(phys3,0.3),et(phys3)),'M3':lambda:Hybrid(sm(phys,0.3),et(phys)),
'M4':lambda:et(phys3),'V1':lambda:Hybrid(vcsm(10.0),et(phys3)),'V2':lambda:Hybrid(vcsm(20.0),et(phys))}
gkf=GroupKFold(n_splits=15); oof={k:np.zeros(len(y)) for k in MODELS}
for k,mk in MODELS.items():
for t,e in gkf.split(X,y,cond):
m=mk(); m.fit(X.iloc[t],y.iloc[t]); oof[k][e]=m.predict(X.iloc[e])
np.savez('/tmp/oof15.npz',y=y.values,cond=cond,**{k:oof[k] for k in MODELS})
for k in ['V1','V2']: summarize(y.values,oof[k],cond,label=f'{k}')
b1=0.2*oof['M1']+0.55*oof['M3']+0.25*oof['M4']
summarize(y.values,b1,cond,label='blend(no VC)')
for wv in [0.25,0.4,0.55]:
b=(1-wv)*b1+wv*(0.5*oof['V1']+0.5*oof['V2'])
summarize(y.values,b,cond,label=f'blend+VC w={wv}')
" 2>&1 | grep -v WarningV1 RMSE=2.227 MAE=1.567 | condRMSE p90=3.081 p95=3.503 max=8.526 (nconds=82) V2 RMSE=2.262 MAE=1.623 | condRMSE p90=3.170 p95=3.709 max=7.573 (nconds=82) blend(no VC) RMSE=1.977 MAE=1.463 | condRMSE p90=2.702 p95=3.516 max=4.867 (nconds=82) blend+VC w=0.25 RMSE=1.970 MAE=1.448 | condRMSE p90=2.639 p95=3.694 max=4.462 (nconds=82) blend+VC w=0.4 RMSE=1.990 MAE=1.453 | condRMSE p90=2.622 p95=3.690 max=4.937 (nconds=82) blend+VC w=0.55 RMSE=2.026 MAE=1.470 | condRMSE p90=2.667 p95=3.665 max=5.635 (nconds=82)
command: create
path: /app/train_and_predict.py
file_text: #!/usr/bin/env python3
"""Airfoil self-noise surrogate.
Trains on the labeled public data (train.csv + validation.csv) and writes
predictions for the grader-staged evaluation features in test_features.csv.
Modeling strategy
-----------------
The hidden split holds out whole aerodynamic condition groups
``(attack_angle, chord_length, free_stream_velocity)``, so the surrogate must
generalize to unseen physical regimes rather than interpolate nearby rows.
Two ideas drive the design:
1. Physics-informed features. Airfoil self-noise collapses onto a master
spectrum when expressed through the boundary-layer Strouhal number
``St = f * delta* / U`` together with Reynolds / Mach style amplitude terms.
Building features in log space around these quantities lets the model
extrapolate the spectral *shape* across conditions.
2. Semi-parametric hybrids. Each base model is a smooth stage-1 regressor
(spline / varying-coefficient ridge) that captures the global, smoothly
varying physics and therefore extrapolates gracefully to unseen condition
groups, followed by an ExtraTrees stage-2 fit on the residual that cleans up
local structure. Several such hybrids (plus a plain ExtraTrees) are blended.
The blend averages away the occasional catastrophic per-condition miss that
any single smooth model can make on an extreme held-out regime, which keeps
the worst / tail per-condition errors bounded.
The whole workflow is deterministic and self-contained (no internet, no
external data, and the raw source labels are never used).
"""
from pathlib import Path
import numpy as np
import pandas as pd
from sklearn.base import BaseEstimator, RegressorMixin, TransformerMixin, clone
from sklearn.ensemble import ExtraTreesRegressor
from sklearn.linear_model import Ridge
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import FunctionTransformer, SplineTransformer, 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",
]
SPEED_OF_SOUND = 340.0
N_TREES = 500
RANDOM_STATE = 0
# --------------------------------------------------------------------------- #
# Physics-informed feature builders
# --------------------------------------------------------------------------- #
def _raw(X):
f = pd.DataFrame(X, columns=FEATURES).astype(float).reset_index(drop=True)
freq = f["frequency"].clip(lower=1e-6)
ang = f["attack_angle"]
chord = f["chord_length"].clip(lower=1e-9)
vel = f["free_stream_velocity"].clip(lower=1e-6)
thk = f["suction_side_displacement_thickness"].clip(lower=1e-12)
lf = np.log10(freq)
lt = np.log10(thk)
lv = np.log10(vel)
lc = np.log10(chord)
return freq, ang, chord, vel, thk, lf, lt, lv, lc
def phys(X):
"""Compact physics feature set."""
freq, ang, chord, vel, thk, lf, lt, lv, lc = _raw(X)
logSt = lf + lt - lv
o = pd.DataFrame(index=range(len(lf)))
o["lf"] = lf
o["lt"] = lt
o["lv"] = lv
o["lc"] = lc
o["ang"] = ang
o["logSt"] = logSt
o["logRe_c"] = lv + lc
o["logRe_d"] = lv + lt
o["mach"] = vel / SPEED_OF_SOUND
o["ang_sin"] = np.sin(np.radians(ang))
o["logSt2"] = logSt ** 2
o["lf_ang"] = lf * ang
o["lf_vel"] = lf * vel
return o.to_numpy(float)
def phys2(X):
"""Physics features with extra Strouhal / angle interactions."""
freq, ang, chord, vel, thk, lf, lt, lv, lc = _raw(X)
logSt = lf + lt - lv
o = pd.DataFrame(index=range(len(lf)))
o["lf"] = lf
o["lt"] = lt
o["lv"] = lv
o["lc"] = lc
o["ang"] = ang
o["logSt"] = logSt
o["logRe_c"] = lv + lc
o["logRe_d"] = lv + lt
o["mach"] = vel / SPEED_OF_SOUND
o["ang_sin"] = np.si
…[truncated]Existing file was successfully overwritten at /app/train_and_predict.py
cd /app && rm -f output/predictions.csv && time python3 train_and_predict.py 2>&1 | grep -viE "warn" | tail -20 && echo "=== OUTPUT ===" && head -5 output/predictions.csv && echo "..." && wc -l output/predictions.csv
=== OUTPUT === sample_id,predicted_scaled_sound_pressure_level af_val_0000,115.83643257299998 af_val_0001,124.21334761100022 af_val_0002,133.62115659700015 af_val_0003,123.53453448200024 ... 41 output/predictions.csv [stderr] real 0m13.417s user 0m18.860s sys 0m0.600s
cd /app && timeout 200 python3 -c "
import sys; sys.path.insert(0,'/app'); sys.path.insert(0,'/tmp')
import numpy as np, pandas as pd
import train_and_predict as T
from harness import recover_conditions, summarize
tr=pd.read_csv('/app/data/train.csv'); va=pd.read_csv('/app/data/validation.csv')
Xtr,ytr=tr[T.FEATURES],tr[T.TARGET].astype(float)
Xva,yva=va[T.FEATURES],va[T.TARGET].astype(float)
condv=recover_conditions(va)
tot=0.0; pred=np.zeros(len(Xva))
for name,fac,w in T.build_models():
m=fac(); m.fit(Xtr,ytr); p=m.predict(Xva); pred+=w*p; tot+=w
summarize(yva.values,p,condv,label=name)
pred/=tot
print('='*60)
summarize(yva.values,pred,condv,label='FINAL BLEND (train->val)')
" 2>&1 | grep -v Warninghyb_phys3 RMSE=2.081 MAE=1.545 | condRMSE p90=2.962 p95=3.298 max=3.993 (nconds=15) hyb_phys RMSE=2.309 MAE=1.765 | condRMSE p90=3.175 p95=3.711 max=4.852 (nconds=15) et_phys3 RMSE=2.337 MAE=1.825 | condRMSE p90=3.008 p95=3.477 max=4.435 (nconds=15) hyb_phys2 RMSE=2.075 MAE=1.601 | condRMSE p90=2.847 p95=3.155 max=3.799 (nconds=15) vc_phys3 RMSE=2.075 MAE=1.575 | condRMSE p90=2.970 p95=3.230 max=3.397 (nconds=15) vc_phys RMSE=2.165 MAE=1.635 | condRMSE p90=3.221 p95=3.552 max=3.567 (nconds=15) ============================================================ FINAL BLEND (train->val) RMSE=2.080 MAE=1.575 | condRMSE p90=2.898 p95=3.345 max=4.129 (nconds=15)
cd /app && cat > /tmp/finalcv.py << 'EOF'
import sys; sys.path.insert(0,'/app'); sys.path.insert(0,'/tmp')
import numpy as np, pandas as pd
import train_and_predict as T
from harness import recover_conditions
from sklearn.model_selection import GroupKFold
tr=pd.read_csv('/app/data/train.csv'); va=pd.read_csv('/app/data/validation.csv')
alld=pd.concat([tr,va],ignore_index=True)
cond=recover_conditions(alld); X=alld[T.FEATURES]; y=alld[T.TARGET].astype(float)
condv=recover_conditions(va); Xtr,ytr=tr[T.FEATURES],tr[T.TARGET].astype(float)
Xva,yva=va[T.FEATURES],va[T.TARGET].astype(float)
names=[n for n,_,_ in T.build_models()]; facs={n:f for n,f,_ in T.build_models()}
gkf=GroupKFold(n_splits=8)
oof={n:np.zeros(len(y)) for n in names}; valp={n:np.zeros(len(yva)) for n in names}
for n in names:
for t,e in gkf.split(X,y,cond):
m=facs[n](); m.fit(X.iloc[t],y.iloc[t]); oof[n][e]=m.predict(X.iloc[e])
m=facs[n](); m.fit(Xtr,ytr); valp[n]=m.predict(Xva)
np.savez('/tmp/final_oof.npz', y=y.values, cond=cond, yva=yva.values, condv=condv,
names=names, **{f'o_{n}':oof[n] for n in names}, **{f'v_{n}':valp[n] for n in names})
print('done', names)
EOF
timeout 298 python3 /tmp/finalcv.py 2>&1 | grep -v Warningdone ['hyb_phys3', 'hyb_phys', 'et_phys3', 'hyb_phys2', 'vc_phys3', 'vc_phys']
cd /app && timeout 200 python3 -c "
import numpy as np
d=np.load('/tmp/final_oof.npz',allow_pickle=True)
names=list(d['names']); y=d['y']; cond=d['cond']; yva=d['yva']; condv=d['condv']
O=np.vstack([d[f'o_{n}'] for n in names]); V=np.vstack([d[f'v_{n}'] for n in names])
def fac(c): u,inv=np.unique(c,return_inverse=True); return inv,len(u)
ci,nc=fac(cond); cvi,ncv=fac(condv)
def pc(e2,idx,n):
s=np.bincount(idx,weights=e2,minlength=n); c=np.bincount(idx,minlength=n); return np.sqrt(s/c)
def metr(pred,yy,idx,n):
e=yy-pred; p=pc(e*e,idx,n)
return dict(r=np.sqrt(np.mean(e*e)),m=np.mean(np.abs(e)),p90=np.percentile(p,90),p95=np.percentile(p,95),mx=p.max())
T=dict(r=2.25,m=1.55,p90=2.35,p95=4.75,mx=5.60)
def score(md): return max(v/T[k] for k,v in md.items())
np.random.seed(0); best=(1e9,None)
for _ in range(40000):
w=np.random.dirichlet(np.ones(len(names))*0.5)
mc=metr(w@O,y,ci,nc)
if mc['mx']>5.55 or mc['p95']>4.7: continue
mv=metr(w@V,yva,cvi,ncv)
if mv['mx']>5.55: continue
# robust objective: emphasize p90 & mae on both, with CV weighted more
obj=0.55*(0.5*mc['p90']/T['p90']+0.5*mc['m']/T['m'])+0.45*(0.5*mv['p90']/T['p90']+0.5*mv['m']/T['m'])
if obj<best[0]: best=(obj,w.copy(),mc,mv)
obj,w,mc,mv=best
print('weights:',{n:round(float(x),3) for n,x in zip(names,w)})
print('CV :',{k:round(v,3) for k,v in mc.items()})
print('VAL:',{k:round(v,3) for k,v in mv.items()})
np.save('/tmp/fw.npy',w)
" 2>&1 | grep -v Warningweights: {'hyb_phys3': 0.248, 'hyb_phys': 0.141, 'et_phys3': 0.223, 'hyb_phys2': 0.116, 'vc_phys3': 0.27, 'vc_phys': 0.0}
CV : {'r': 2.098, 'm': 1.494, 'p90': 2.95, 'p95': 4.002, 'mx': 5.518}
VAL: {'r': 2.043, 'm': 1.533, 'p90': 2.796, 'p95': 3.222, 'mx': 3.905}cd /app && timeout 200 python3 -c "
import numpy as np
d=np.load('/tmp/final_oof.npz',allow_pickle=True)
names=list(d['names']); y=d['y']; cond=d['cond']; yva=d['yva']; condv=d['condv']
O=np.vstack([d[f'o_{n}'] for n in names]); V=np.vstack([d[f'v_{n}'] for n in names])
def fac(c): u,inv=np.unique(c,return_inverse=True); return inv,len(u)
ci,nc=fac(cond); cvi,ncv=fac(condv)
def pc(e2,idx,n): s=np.bincount(idx,weights=e2,minlength=n); c=np.bincount(idx,minlength=n); return np.sqrt(s/c)
def metr(pred,yy,idx,n):
e=yy-pred; p=pc(e*e,idx,n)
return dict(r=np.sqrt(np.mean(e*e)),m=np.mean(np.abs(e)),p90=np.percentile(p,90),p95=np.percentile(p,95),mx=p.max())
np.random.seed(1); best=(1e9,None)
for _ in range(80000):
w=np.random.dirichlet(np.ones(len(names))*0.5)
mc=metr(w@O,y,ci,nc)
if mc['m']>1.53 or mc['mx']>5.55 or mc['p95']>4.6 or mc['r']>2.24: continue
mv=metr(w@V,yva,cvi,ncv)
if mv['m']>1.58 or mv['mx']>5.0 or mv['r']>2.2: continue
obj=0.6*mc['p90']+0.4*mv['p90']
if obj<best[0]: best=(obj,w.copy(),mc,mv)
obj,w,mc,mv=best
print('weights:',{n:round(float(x),3) for n,x in zip(names,w)})
print('CV :',{k:round(v,3) for k,v in mc.items()})
print('VAL:',{k:round(v,3) for k,v in mv.items()})
np.save('/tmp/fw2.npy',w)
" 2>&1 | grep -v Warningweights: {'hyb_phys3': 0.171, 'hyb_phys': 0.08, 'et_phys3': 0.401, 'hyb_phys2': 0.151, 'vc_phys3': 0.192, 'vc_phys': 0.005}
CV : {'r': 2.119, 'm': 1.521, 'p90': 2.881, 'p95': 3.995, 'mx': 5.532}
VAL: {'r': 2.081, 'm': 1.575, 'p90': 2.787, 'p95': 3.197, 'mx': 3.984}cd /app && timeout 298 python3 -c "
import sys; sys.path.insert(0,'/app'); sys.path.insert(0,'/tmp')
import numpy as np, pandas as pd
import train_and_predict as T
from harness import recover_conditions
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import FunctionTransformer
from sklearn.ensemble import RandomForestRegressor, GradientBoostingRegressor
from sklearn.model_selection import GroupKFold
tr=pd.read_csv('/app/data/train.csv'); va=pd.read_csv('/app/data/validation.csv')
alld=pd.concat([tr,va],ignore_index=True); cond=recover_conditions(alld)
X=alld[T.FEATURES]; y=alld[T.TARGET].astype(float); condv=recover_conditions(va)
Xtr,ytr=tr[T.FEATURES],tr[T.TARGET].astype(float); Xva,yva=va[T.FEATURES],va[T.TARGET].astype(float)
def rf(): return make_pipeline(FunctionTransformer(T.phys3),RandomForestRegressor(n_estimators=400,random_state=0,n_jobs=-1))
def gb(): return make_pipeline(FunctionTransformer(T.phys3),GradientBoostingRegressor(n_estimators=400,learning_rate=0.03,max_depth=3,subsample=0.8,random_state=0))
M={'rf_h':lambda:T.Hybrid(T._spline_ridge(T.phys3,0.3),rf()),'gb_h':lambda:T.Hybrid(T._spline_ridge(T.phys3,0.3),gb())}
gkf=GroupKFold(n_splits=8); oof={k:np.zeros(len(y)) for k in M}; vp={k:np.zeros(len(yva)) for k in M}
for k,mk in M.items():
for t,e in gkf.split(X,y,cond):
m=mk(); m.fit(X.iloc[t],y.iloc[t]); oof[k][e]=m.predict(X.iloc[e])
m=mk(); m.fit(Xtr,ytr); vp[k]=m.predict(Xva)
np.savez('/tmp/extra_oof.npz',**{f'o_{k}':oof[k] for k in M},**{f'v_{k}':vp[k] for k in M},names=list(M.keys()))
print('done')
" 2>&1 | grep -v Warningdone
cd /app && timeout 220 python3 -c "
import numpy as np
d=np.load('/tmp/final_oof.npz',allow_pickle=True); e=np.load('/tmp/extra_oof.npz',allow_pickle=True)
names=list(d['names'])+list(e['names'])
y=d['y']; cond=d['cond']; yva=d['yva']; condv=d['condv']
def oget(n): return d[f'o_{n}'] if f'o_{n}' in d else e[f'o_{n}']
def vget(n): return d[f'v_{n}'] if f'v_{n}' in d else e[f'v_{n}']
O=np.vstack([oget(n) for n in names]); V=np.vstack([vget(n) for n in names])
def fac(c): u,inv=np.unique(c,return_inverse=True); return inv,len(u)
ci,nc=fac(cond); cvi,ncv=fac(condv)
def pc(e2,idx,n): s=np.bincount(idx,weights=e2,minlength=n); c=np.bincount(idx,minlength=n); return np.sqrt(s/c)
def metr(pred,yy,idx,n):
er=yy-pred; p=pc(er*er,idx,n)
return dict(r=np.sqrt(np.mean(er*er)),m=np.mean(np.abs(er)),p90=np.percentile(p,90),p95=np.percentile(p,95),mx=p.max())
np.random.seed(3); best=(1e9,None)
for _ in range(120000):
w=np.random.dirichlet(np.ones(len(names))*0.45)
mc=metr(w@O,y,ci,nc)
if mc['m']>1.52 or mc['mx']>5.5 or mc['p95']>4.5 or mc['r']>2.2: continue
mv=metr(w@V,yva,cvi,ncv)
if mv['m']>1.57 or mv['mx']>5.0 or mv['r']>2.15: continue
obj=0.6*mc['p90']+0.4*mv['p90']
if obj<best[0]: best=(obj,w.copy(),mc,mv)
obj,w,mc,mv=best
print('names:',names)
print('weights:',[round(float(x),3) for x in w])
print('CV :',{k:round(v,3) for k,v in mc.items()})
print('VAL:',{k:round(v,3) for k,v in mv.items()})
np.save('/tmp/fw3.npy',w);
import json; open('/tmp/fw3_names.json','w').write(json.dumps(names))
" 2>&1 | grep -v Warningnames: ['hyb_phys3', 'hyb_phys', 'et_phys3', 'hyb_phys2', 'vc_phys3', 'vc_phys', 'rf_h', 'gb_h']
weights: [0.435, 0.305, 0.135, 0.063, 0.048, 0.009, 0.001, 0.003]
CV : {'r': 2.074, 'm': 1.513, 'p90': 2.891, 'p95': 4.009, 'mx': 5.466}
VAL: {'r': 2.077, 'm': 1.565, 'p90': 2.847, 'p95': 3.326, 'mx': 4.18}cd /app && timeout 120 python3 -c "
import numpy as np
d=np.load('/tmp/final_oof.npz',allow_pickle=True)
names=list(d['names']); y=d['y']; cond=d['cond']; yva=d['yva']; condv=d['condv']
W={'hyb_phys3':0.25,'hyb_phys':0.14,'et_phys3':0.22,'hyb_phys2':0.12,'vc_phys3':0.27,'vc_phys':0.0}
def blend(pfx,n):
s=sum(W[nm]*d[f'{pfx}_{nm}'] for nm in names); return s/sum(W.values())
def fac(c): u,inv=np.unique(c,return_inverse=True); return inv,len(u)
def show(pred,yy,c,lab):
idx,n=fac(c); e=yy-pred
s=np.bincount(idx,weights=e*e,minlength=n); cnt=np.bincount(idx,minlength=n); p=np.sqrt(s/cnt)
print(f'{lab}: RMSE={np.sqrt(np.mean(e*e)):.3f} MAE={np.mean(np.abs(e)):.3f} p90={np.percentile(p,90):.3f} p95={np.percentile(p,95):.3f} max={p.max():.3f}')
show(blend('o',names),y,cond,'CV (8-fold, train~72 conds)')
show(blend('v',names),yva,condv,'VAL (train 67 conds)')
print('Targets: RMSE<=2.25 MAE<=1.55 p90<=2.35 p95<=4.75 max<=5.60')
" 2>&1 | grep -v WarningCV (8-fold, train~72 conds): RMSE=2.098 MAE=1.494 p90=2.952 p95=4.006 max=5.517 VAL (train 67 conds): RMSE=2.043 MAE=1.532 p90=2.795 p95=3.221 max=3.902 Targets: RMSE<=2.25 MAE<=1.55 p90<=2.35 p95<=4.75 max<=5.60
command: str_replace
path: /app/train_and_predict.py
old_str: return [
("hyb_phys3", lambda: Hybrid(_spline_ridge(phys3, 0.3), _et(phys3)), 0.20),
("hyb_phys", lambda: Hybrid(_spline_ridge(phys, 0.3), _et(phys)), 0.34),
("et_phys3", lambda: _et(phys3), 0.18),
("hyb_phys2", lambda: Hybrid(_spline_ridge(phys2, 0.3), _et(phys3)), 0.12),
("vc_phys3", lambda: Hybrid(_vc_ridge(10.0), _et(phys3)), 0.08),
("vc_phys", lambda: Hybrid(_vc_ridge(20.0), _et(phys)), 0.08),
]
new_str: return [
("hyb_phys3", lambda: Hybrid(_spline_ridge(phys3, 0.3), _et(phys3)), 0.25),
("hyb_phys", lambda: Hybrid(_spline_ridge(phys, 0.3), _et(phys)), 0.14),
("et_phys3", lambda: _et(phys3), 0.22),
("hyb_phys2", lambda: Hybrid(_spline_ridge(phys2, 0.3), _et(phys3)), 0.12),
("vc_phys3", lambda: Hybrid(_vc_ridge(10.0), _et(phys3)), 0.27),
]The file /app/train_and_predict.py has been edited successfully.
cd /app && rm -f output/predictions.csv && time python3 train_and_predict.py 2>&1 | grep -viE "warn" | tail -5
echo "=== VALIDATION ==="
python3 -c "
import pandas as pd, numpy as np
feat=pd.read_csv('/app/data/test_features.csv')
pred=pd.read_csv('/app/output/predictions.csv')
print('columns:', list(pred.columns))
assert list(pred.columns)==['sample_id','predicted_scaled_sound_pressure_level'], 'bad columns'
print('n_pred:', len(pred), 'n_feat:', len(feat))
assert len(pred)==len(feat), 'row count mismatch'
assert list(pred['sample_id'].astype(str))==list(feat['sample_id'].astype(str)), 'sample_id misalignment'
assert pred['predicted_scaled_sound_pressure_level'].notna().all(), 'NaNs'
assert np.isfinite(pred['predicted_scaled_sound_pressure_level']).all(), 'non-finite'
assert pred['predicted_scaled_sound_pressure_level'].nunique()>1, 'constant'
print('pred range: [%.2f, %.2f]'%(pred['predicted_scaled_sound_pressure_level'].min(),pred['predicted_scaled_sound_pressure_level'].max()))
print('ALL FORMAT CHECKS PASSED')
"=== VALIDATION === columns: ['sample_id', 'predicted_scaled_sound_pressure_level'] n_pred: 40 n_feat: 40 pred range: [114.74, 138.23] ALL FORMAT CHECKS PASSED [stderr] real 0m11.743s user 0m16.050s sys 0m0.870s
cd /app && cp data/test_features.csv /tmp/tf_backup.csv && md5sum data/test_features.csv
echo "--- create synthetic hidden test (7 novel rows, new ids, shuffled cols order) ---"
python3 -c "
import pandas as pd
tr=pd.read_csv('/app/data/train.csv')
# take some rows, perturb slightly, assign NEW sample_ids, drop target
s=tr.sample(7,random_state=42).reset_index(drop=True)
s['sample_id']=['hidden_%04d'%i for i in range(7)]
cols=['sample_id','frequency','attack_angle','chord_length','free_stream_velocity','suction_side_displacement_thickness']
s[cols].to_csv('/app/data/test_features.csv',index=False)
print('wrote synthetic test_features with', len(s), 'rows')
"
rm -f output/predictions.csv && python3 train_and_predict.py 2>&1 | grep -viE "warn" | tail -3
python3 -c "
import pandas as pd, numpy as np
f=pd.read_csv('/app/data/test_features.csv'); p=pd.read_csv('/app/output/predictions.csv')
assert list(p.columns)==['sample_id','predicted_scaled_sound_pressure_level']
assert list(p.sample_id.astype(str))==list(f.sample_id.astype(str)), 'misaligned'
assert len(p)==7 and np.isfinite(p.iloc[:,1]).all()
print('Synthetic hidden run OK. ids:', list(p.sample_id))
print(p.to_string(index=False))
"
echo "--- restore original ---"
cp /tmp/tf_backup.csv data/test_features.csv && md5sum data/test_features.csvfc4ec5b2349476fa1e598e1846bb2711 data/test_features.csv --- create synthetic hidden test (7 novel rows, new ids, shuffled cols order) --- wrote synthetic test_features with 7 rows Synthetic hidden run OK. ids: ['hidden_0000', 'hidden_0001', 'hidden_0002', 'hidden_0003', 'hidden_0004', 'hidden_0005', 'hidden_0006'] sample_id predicted_scaled_sound_pressure_level hidden_0000 121.404212 hidden_0001 128.155194 hidden_0002 129.345030 hidden_0003 121.848760 hidden_0004 124.701409 hidden_0005 109.021402 hidden_0006 111.932857 --- restore original --- fc4ec5b2349476fa1e598e1846bb2711 data/test_features.csv
cd /app && rm -f output/predictions.csv && python3 train_and_predict.py 2>/dev/null && cp output/predictions.csv /tmp/run1.csv && python3 train_and_predict.py 2>/dev/null && cp output/predictions.csv /tmp/run2.csv if diff -q /tmp/run1.csv /tmp/run2.csv >/dev/null; then echo "DETERMINISTIC: two runs identical"; else echo "NON-DETERMINISTIC"; diff /tmp/run1.csv /tmp/run2.csv | head; fi echo "--- confirm no /tmp imports in script ---" grep -nE "sys.path|/tmp|import harness|import feats|import vc|import hybrid" train_and_predict.py || echo "clean: no external/dev imports" echo "--- final head of predictions ---"; head -3 output/predictions.csv
DETERMINISTIC: two runs identical --- confirm no /tmp imports in script --- clean: no external/dev imports --- final head of predictions --- sample_id,predicted_scaled_sound_pressure_level af_val_0000,115.83643257299997 af_val_0001,124.21334761100024
cd /app && python3 -c "import ast; ast.parse(open('train_and_predict.py').read()); print('syntax OK')" && sed -n '250,320p' train_and_predict.pysyntax OK
parts.append(pd.read_csv(validation_path))
data = pd.concat(parts, ignore_index=True)
return data.dropna(subset=FEATURES + [TARGET]).reset_index(drop=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()
X = labeled[FEATURES]
y = labeled[TARGET].astype(float)
X_eval = evaluation[FEATURES]
total_w = 0.0
blended = np.zeros(len(X_eval), dtype=float)
for name, factory, weight in build_models():
model = factory()
model.fit(X, y)
blended += weight * model.predict(X_eval)
total_w += weight
blended /= total_w
# Guard against any non-finite value so the output is always valid.
if not np.all(np.isfinite(blended)):
fallback = float(np.median(y))
blended = np.where(np.isfinite(blended), blended, fallback)
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
pd.DataFrame(
{
"sample_id": evaluation["sample_id"].astype(str),
PREDICTION: blended.astype(float),
}
).to_csv(OUTPUT_DIR / "predictions.csv", index=False)
if __name__ == "__main__":
main()cd /app && ls -la output/predictions.csv && echo && timeout 120 python3 -c "
import numpy as np
d=np.load('/tmp/final_oof.npz',allow_pickle=True)
names=['hyb_phys3','hyb_phys','et_phys3','hyb_phys2','vc_phys3']
W={'hyb_phys3':0.25,'hyb_phys':0.14,'et_phys3':0.22,'hyb_phys2':0.12,'vc_phys3':0.27}
tw=sum(W.values())
def fac(c): u,inv=np.unique(c,return_inverse=True); return inv,len(u)
def m(pred,yy,c):
idx,n=fac(c); e=yy-pred
s=np.bincount(idx,weights=e*e,minlength=n); cnt=np.bincount(idx,minlength=n); p=np.sqrt(s/cnt)
return np.sqrt(np.mean(e*e)),np.mean(np.abs(e)),np.percentile(p,90),np.percentile(p,95),p.max()
ocv=sum(W[n]*d[f'o_{n}'] for n in names)/tw
oval=sum(W[n]*d[f'v_{n}'] for n in names)/tw
T=[2.25,1.55,2.35,4.75,5.60]
labels=['RMSE','MAE','p90_cond','p95_cond','max_cond']
print('%-26s %8s %8s %8s %8s %8s'%('Scenario','RMSE','MAE','p90','p95','max'))
print('%-26s %8.3f %8.3f %8.3f %8.3f %8.3f'%(('CV 8-fold (train~72 conds)',)+m(ocv,d['y'],d['cond'])))
print('%-26s %8.3f %8.3f %8.3f %8.3f %8.3f'%(('TRAIN->VAL (67 conds)',)+m(oval,d['yva'],d['condv'])))
print('%-26s %8.2f %8.2f %8.2f %8.2f %8.2f'%(('TARGET (<=)',)+tuple(T)))
print()
print('Final model trains on all 82 conds -> expect further improvement')
print('(fold-count study: 72->79 conds moved p90 2.73->2.61, MAE 1.50->1.42)')
"-rw-r--r-- 1 h2tagent h2tagent 1276 Jun 21 06:39 output/predictions.csv Scenario RMSE MAE p90 p95 max CV 8-fold (train~72 conds) 2.098 1.494 2.952 4.006 5.517 TRAIN->VAL (67 conds) 2.043 1.532 2.795 3.221 3.902 TARGET (<=) 2.25 1.55 2.35 4.75 5.60 Final model trains on all 82 conds -> expect further improvement (fold-count study: 72->79 conds moved p90 2.73->2.61, MAE 1.50->1.42)
No file changes, the agent left the workspace untouched.
# SyncVals normalized verifier summary # task: airfoil-self-noise # attempt: 1 status: completed raw_score: 0.5 # reward rule: continuous: score 0.5 >= 0.5 # => reward = 1.0 (PASS) # # 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). ---------------------------------------------------------------------- -rw-r--r-- 1 h2tagent h2tagent 1276 Jun 21 06:39 output/predictions.csv Scenario RMSE MAE p90 p95 max CV 8-fold (train~72 conds) 2.098 1.494 2.952 4.006 5.517 TRAIN->VAL (67 conds) 2.043 1.532 2.795 3.221 3.902 TARGET (<=) 2.25 1.55 2.35 4.75 5.60 Final model trains on all 82 conds -> expect further improvement (fold-count study: 72->79 conds moved p90 2.73->2.61, MAE 1.50->1.42)
Reproduce this trial: git checkout 2f94510 && PYTHONPATH=src python3 scripts/build_site.py , then open trial/trial_a71ba0d2552a40b8. Re-running the agent live requires EVAL_PLATFORM_ENABLE_OAUTH_SMOKE=1 and is non-deterministic.
Trial trial_a71ba0d2552a40b8 · verifier authoritative; classifier explanatory.