tests/test.sh exit code (0 → resolved); the classification below is post-hoc and cannot change it.cat /app/train_and_predict.py
#!/usr/bin/env python3
"""Starter baseline for the Airfoil Self-Noise surrogate task.
This deliberately modest Ridge model is useful as a working end-to-end
baseline, but it is not strong enough to clear the hidden grouped split.
"""
from pathlib import Path
import numpy as np
import pandas as pd
from sklearn.base import BaseEstimator, TransformerMixin
from sklearn.linear_model import Ridge
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
DATA_DIR = Path("/app/data")
OUTPUT_DIR = Path("/app/output")
TARGET = "scaled_sound_pressure_level"
PREDICTION = "predicted_scaled_sound_pressure_level"
FEATURES = [
"frequency",
"attack_angle",
"chord_length",
"free_stream_velocity",
"suction_side_displacement_thickness",
]
class AirfoilFeatures(BaseEstimator, TransformerMixin):
def fit(self, X, y=None):
return self
def transform(self, X):
frame = pd.DataFrame(X, columns=FEATURES if not isinstance(X, pd.DataFrame) else X.columns)
frequency = frame["frequency"].astype(float)
angle = frame["attack_angle"].astype(float)
chord = frame["chord_length"].astype(float)
velocity = frame["free_stream_velocity"].astype(float)
thickness = frame["suction_side_displacement_thickness"].astype(float)
log_frequency = np.log10(frequency)
log_thickness = np.log10(thickness)
out = pd.DataFrame(index=frame.index)
out["frequency"] = frequency
out["attack_angle"] = angle
out["chord_length"] = chord
out["free_stream_velocity"] = velocity
out["suction_side_displacement_thickness"] = thickness
out["log_frequency"] = log_frequency
out["log_thickness"] = log_thickness
out["freq_velocity"] = log_frequency * velocity
out["freq_angle"] = log_frequency * angle
out["angle_velocity"] = angle * velocity
out["thickness_angle"] = log_thickness * angle
out["chord_velocity"] = chord * velocity
return out.to_numpy(dtype=float)
def load_labeled_data() -> pd.DataFrame:
parts = [pd.read_csv(DATA_DIR / "train.csv")]
validation_path = DATA_DIR / "validation.csv"
if validation_path.exists():
parts.append(pd.read_csv(validation_path))
return pd.concat(parts, ignore_index=True)
def load_evaluation_features() -> pd.DataFrame:
test_path = DATA_DIR / "test_features.csv"
if test_path.exists():
return pd.read_csv(test_path)
validation = pd.read_csv(DATA_DIR / "validation.csv")
return validation.drop(columns=[TARGET])
def main() -> None:
labeled = load_labeled_data()
evaluation = load_evaluation_features()
model = make_pipeline(
AirfoilFeatures(),
StandardScaler(),
Ridge(alpha=10.0),
)
model.fit(labeled[FEATURES], labeled[TARGET])
predictions = model.predict(evaluation[FEATURES])
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
pd.DataFrame(
{
"sample_id": evaluation["sample_id"].astype(str),
PREDICTION: predictions,
}
).to_csv(OUTPUT_DIR / "predictions.csv", index=False)
if __name__ == "__main__":
main()cd /app && echo "=== Directory structure ===" && ls -la && echo "" && echo "=== Data dir ===" && ls -la data/ && echo "" && echo "=== Output dir ===" && ls -la output/ 2>/dev/null || echo "no output dir"
=== Directory structure === total 20 drwxrws--- 4 h2tagent h2tagent 4096 Jun 21 05:20 . drwxr-xr-x 26 root root 4096 Jun 21 05:25 .. drwxrws--- 2 h2tagent h2tagent 4096 Jun 21 05:20 data drwxrws--- 2 h2tagent h2tagent 4096 Jun 21 05:20 output -rwxrwx--- 1 h2tagent h2tagent 3211 Jun 21 05:18 train_and_predict.py === Data dir === total 128 drwxrws--- 2 h2tagent h2tagent 4096 Jun 21 05:20 . drwxrws--- 4 h2tagent h2tagent 4096 Jun 21 05:20 .. -rw-rw---- 1 h2tagent h2tagent 3595 Jun 21 05:18 test_features.csv -rw-rw---- 1 h2tagent h2tagent 97417 Jun 21 05:18 train.csv -rw-rw---- 1 h2tagent h2tagent 20351 Jun 21 05:18 validation.csv === Output dir === total 8 drwxrws--- 2 h2tagent h2tagent 4096 Jun 21 05:20 . drwxrws--- 4 h2tagent h2tagent 4096 Jun 21 05:20 ..
cd /app && echo "=== train.csv head ===" && head -5 data/train.csv && echo "" && echo "=== validation.csv head ===" && head -5 data/validation.csv && echo "" && echo "=== test_features.csv head ===" && head -5 data/test_features.csv && echo "" && echo "=== line counts ===" && wc -l data/*.csv
=== train.csv head ===
sample_id,frequency,attack_angle,chord_length,free_stream_velocity,suction_side_displacement_thickness,scaled_sound_pressure_level
af_train_0000,5003.72368377,0.034997843085,0.101695197262,71.2730786718,0.00120791730525,131.375168508
af_train_0001,1997.22544774,15.6337827466,0.101571096822,71.3314610261,0.0436885053477,119.435701427
af_train_0002,1248.10938712,9.47742633424,0.0253695328899,31.6576914656,0.00461706652486,131.137445156
af_train_0003,200.228488983,15.5929878809,0.101720824729,39.5544682279,0.0527780201324,124.741187675
=== validation.csv head ===
sample_id,frequency,attack_angle,chord_length,free_stream_velocity,suction_side_displacement_thickness,scaled_sound_pressure_level
af_val_0000,1998.53240303,12.3252028109,0.101492415739,39.5853154533,0.040726689388,115.836432573
af_val_0001,629.718430551,0.0186262062548,0.152498614376,39.5692331074,0.00193529208188,124.213347611
af_val_0002,12482.9663422,0.0105284256024,0.0253720007765,71.3111171362,0.000400617998197,133.621156597
af_val_0003,1600.32755515,0.0338230474173,0.0253792825467,39.6448710656,0.000429527467403,123.534534482
=== test_features.csv head ===
sample_id,frequency,attack_angle,chord_length,free_stream_velocity,suction_side_displacement_thickness
af_val_0000,1998.53240303,12.3252028109,0.101492415739,39.5853154533,0.040726689388
af_val_0001,629.718430551,0.0186262062548,0.152498614376,39.5692331074,0.00193529208188
af_val_0002,12482.9663422,0.0105284256024,0.0253720007765,71.3111171362,0.000400617998197
af_val_0003,1600.32755515,0.0338230474173,0.0253792825467,39.6448710656,0.000429527467403
=== line counts ===
41 data/test_features.csv
957 data/train.csv
202 data/validation.csv
1200 totalcd /app && python3 -c "
import pandas as pd
import numpy as np
train = pd.read_csv('data/train.csv')
val = pd.read_csv('data/validation.csv')
test = pd.read_csv('data/test_features.csv')
print('train shape', train.shape)
print('val shape', val.shape)
print('test shape', test.shape)
print()
print('=== train describe ===')
print(train.describe())
print()
print('=== test_features sample_ids ===')
print(test['sample_id'].tolist()[:10])
print('test ids are subset of val ids:', set(test['sample_id']).issubset(set(val['sample_id'])))
"train shape (956, 7)
val shape (201, 7)
test shape (40, 6)
=== train describe ===
frequency ... scaled_sound_pressure_level
count 956.000000 ... 956.000000
mean 2688.724820 ... 125.402453
std 2909.340380 ... 6.704146
min 199.660258 ... 104.935002
25% 630.914658 ... 121.083472
50% 1600.651809 ... 126.104163
75% 3994.319231 ... 130.491660
max 20025.582040 ... 140.289040
[8 rows x 6 columns]
=== test_features sample_ids ===
['af_val_0000', 'af_val_0001', 'af_val_0002', 'af_val_0003', 'af_val_0004', 'af_val_0005', 'af_val_0006', 'af_val_0007', 'af_val_0008', 'af_val_0009']
test ids are subset of val ids: Truecd /app && python3 -c "
import pandas as pd
import numpy as np
pd.set_option('display.width', 200)
pd.set_option('display.max_columns', 20)
train = pd.read_csv('data/train.csv')
val = pd.read_csv('data/validation.csv')
test = pd.read_csv('data/test_features.csv')
feats = ['frequency','attack_angle','chord_length','free_stream_velocity','suction_side_displacement_thickness']
print('=== Unique values per feature (train) ===')
for f in feats:
u = train[f].unique()
print(f, 'n_unique=', len(u))
print()
print('=== chord_length unique (train) ===', np.sort(train['chord_length'].unique()))
print('=== velocity unique (train) ===', np.sort(train['free_stream_velocity'].unique()))
print('=== attack_angle unique count ===', train['attack_angle'].nunique())
print(np.sort(train['attack_angle'].unique())[:20])
"=== Unique values per feature (train) === frequency n_unique= 956 attack_angle n_unique= 956 chord_length n_unique= 956 free_stream_velocity n_unique= 956 suction_side_displacement_thickness n_unique= 956 === chord_length unique (train) === [0.02536952 0.02536953 0.02536953 0.02536953 0.02536953 0.02536953 0.02536954 0.02536954 0.02536954 0.02536954 0.02536955 0.02537162 0.02537166 0.0253717 0.02537172 0.02537175 0.02537177 0.02537179 0.02537181 0.02537185 0.02537187 0.02537189 0.02537196 0.02537236 0.02537243 0.02537246 0.02537248 0.02537253 0.02537255 0.02537258 0.0253726 0.02537263 0.02537267 0.02537272 0.0253786 0.02537868 0.02537876 0.0253788 0.02537884 0.02537888 0.02537892 0.02537896 0.02537904 0.02537908 0.02537924 0.0253799 0.02537998 0.02538011 0.02538015 0.02538019 0.02538028 0.02538032 0.02538036 0.0253804 0.02538045 0.02538053 0.02538062 0.02538929 0.02538939 0.02538949 0.02538954 0.0253896 0.02538965 0.0253897 0.02538975 0.02538986 0.02538991 0.02539012 0.02539017 0.02539096 0.02539107 0.02539123 0.02539128 0.02539133 0.02539144 0.02539149 0.02539154 0.0253916 0.02539165 0.02539176 0.02539186 0.02540182 0.02540193 0.02540204 0.0254021 0.02540215 0.02540221 0.02540226 0.02540232 0.02540243 0.02540248 0.0254027 0.02540276 0.02540359 0.0254037 0.02540386 0.02540392 0.02540397 0.02540408 0.02540414 0.02540419 0.02540425 0.0254043 0.02540441 0.02540452 0.02541404 0.02541414 0.02541424 0.02541429 0.02541434 0.02541438 0.02541443 0.02541448 0.02541458 0.02541463 0.02541482 0.02541487 0.02541559 0.02541569 0.02541583 0.02541588 0.02541592 0.02541602 0.02541606 0.02541611 0.02541616 0.0254162 0.0254163 0.02541639 0.02542383 0.0254239 0.02542397 0.02542401 0.02542404 0.02542407 0.02542411 0.02542414 0.02542421 0.02542424 0.02542438 0.02542441 0.0254249 0.02542496 0.02542506 0.02542509 0.02542518 0.02542521 0.02542524 0.02542527 0.02542531 0.02542537 0.02542543 0.0254295 0.02542953 0.02542956 0.02542957 0.02542958 0.0254296 0.02542961 0.02542962 0.02542965 0.02542966 0.02542972 0.0254299 0.02542995 0.02542996 0.02542998 0.02542999 0.02543 0.02543001 0.02543002 0.02543003 0.02543005 0.02543007 0.05073904 0.05073904 0.05073904 0.05073904 0.05073904 0.05073904 0.05073904 0.05073904 0.05073904 0.05073904 0.05073904 0.050744 0.05074405 0.05074409 0.05074409 0.05074418 0.05074418 0.05074422 0.05074422 0.05074427 0.05074427 0.05074431 0.05074431 0.05074436 0.05074436 0.0507444 0.0507444 0.05074445 0.05074445 0.05074454 0.05074454 0.05074459 0.05074463 0.05075865 0.05075873 0.05075881 0.05075881 0.05075897 0.05075898 0.05075905 0.05075906 0.05075914 0.05075914 0.05075922 0.05075922 0.0507593 0.05075931 0.05075938 0.05075939 0.05075947 0.05075963 0.05075964 0.05075971 0.0507598 0.05078045 0.05078056 0.05078066 0.05078066 0.05078087 0.05078087 0.05078097 0.05078098 0.05078108 0.05078108 0.05078118 0.05078119 0.05078129 0.05078129 0.05078139 0.0507814 0.0507815 0.05078171 0.05078171 0.05078181 0.05078192 0.05080563 0.05080574 0.05080585 0.05080585 0.05080607 0.05080607 0.05080618 0.05080618 0.05080629 0.05080629 0.0508064 0.0508064 0.05080651 0.05080651 0.05080662 0.05080662 0.05080673 0.05080695 0.05080695 0.05080706 0.05080717 0.05082984 0.05082994 0.05083003 0.05083003 0.05083022 0.05083023 0.05083032 0.05083032 0.05083041 0.05083042 0.05083051 0.05083051 0.05083061 0.05083061 0.0508307 0.05083071 0.0508308 0.05083099 0.05083099 0.05083108 0.05083118 0.05084882 0.05084889 0.05084895 0.05084902 0.05084902 0.05084915 0.05084915 0.05084921 0.05084922 0.05084928 0.05084928 0.05084934 0.05084935 0.05084941 0.05084941 0.05084947 0.05084947 0.05084954 0.05084966 0.05084967 0.05084973 0.05084979 0.05085945 0.05085947 0.0508595 0.05085952 0.05085957 0.05085957 0.05085959 0.05085959 0.05085961 0.05085962 0.05085964 0.05085964 0.05085966 0.05085966 0.05085968 0.05085968 0.05085971 0.05085975 0.05085975 0.05085977 0.05085979 0.10147821 0.10147822 0.10147823 …[truncated]
cd /app && python3 -c "
import pandas as pd, numpy as np
train = pd.read_csv('data/train.csv')
val = pd.read_csv('data/validation.csv')
# Nominal chord and velocity from UCI
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 nearest(x, arr):
return arr[np.argmin(np.abs(arr[None,:]-np.array(x)[:,None]),axis=1)]
for name,df in [('train',train),('val',val)]:
c = nearest(df['chord_length'].values, chords)
v = nearest(df['free_stream_velocity'].values, vels)
print(name, 'chord counts:', {round(k,4):int((c==k).sum()) for k in chords})
print(name, 'vel counts:', {round(k,1):int((v==k).sum()) for k in vels})
print()
# attack angle distribution
print('train angle min/max', train['attack_angle'].min(), train['attack_angle'].max())
print('val angle min/max', val['attack_angle'].min(), val['attack_angle'].max())
"train chord counts: {0.0254: 174, 0.0508: 160, 0.1016: 198, 0.1524: 139, 0.2286: 178, 0.3048: 107}
train vel counts: {31.7: 175, 39.6: 301, 55.5: 173, 71.3: 307}
val chord counts: {0.0254: 61, 0.0508: 25, 0.1016: 52, 0.1524: 30, 0.2286: 15, 0.3048: 18}
val vel counts: {31.7: 49, 39.6: 70, 55.5: 49, 71.3: 33}
train angle min/max -0.0349999301153 22.2347699697
val angle min/max -0.034726269645 17.4347681873cd /app && python3 -c "
import pandas as pd, numpy as np
train = pd.read_csv('data/train.csv'); val = pd.read_csv('data/validation.csv')
alldf = pd.concat([train,val])
ang = np.sort(alldf['attack_angle'].values)
# find clusters: gaps > 0.3
clusters=[]; cur=[ang[0]]
for a in ang[1:]:
if a-cur[-1] > 0.3:
clusters.append(cur); cur=[a]
else: cur.append(a)
clusters.append(cur)
print('num angle clusters', len(clusters))
for c in clusters:
print(f' center={np.mean(c):.3f} min={min(c):.3f} max={max(c):.3f} n={len(c)}')
"num angle clusters 19 center=-0.002 min=-0.035 max=0.035 n=228 center=1.498 min=1.465 max=1.535 n=31 center=2.001 min=1.965 max=2.035 n=56 center=3.188 min=2.965 max=3.335 n=80 center=4.043 min=3.965 max=4.235 n=99 center=4.804 min=4.765 max=4.835 n=38 center=5.352 min=5.268 max=5.435 n=28 center=6.700 min=6.665 max=6.735 n=33 center=7.260 min=7.165 max=7.335 n=81 center=8.399 min=8.365 max=8.435 n=34 center=8.901 min=8.865 max=8.935 n=32 center=9.498 min=9.465 max=9.535 n=40 center=9.901 min=9.865 max=9.935 n=48 center=11.203 min=11.165 max=11.235 n=28 center=12.480 min=12.265 max=12.735 n=130 center=15.476 min=15.365 max=15.635 n=79 center=17.400 min=17.365 max=17.435 n=63 center=19.694 min=19.665 max=19.728 n=14 center=22.204 min=22.168 max=22.235 n=15
cd /app && python3 -c "
import pandas as pd, numpy as np
train = pd.read_csv('data/train.csv'); val = pd.read_csv('data/validation.csv')
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])
# nominal angles from UCI
angs = np.array([0,1.5,2.0,3.0,3.3,4.0,4.8,5.3,6.7,7.3,8.4,8.9,9.5,9.9,11.2,12.3,12.6,15.4,15.6,17.4,19.7,22.2])
def nn(x, arr): return arr[np.argmin(np.abs(arr[None,:]-np.array(x)[:,None]),axis=1)]
def cond(df):
c = nn(df['chord_length'].values, chords)
v = nn(df['free_stream_velocity'].values, vels)
a = nn(df['attack_angle'].values, angs)
return set(zip(np.round(c,4),np.round(v,1),np.round(a,2)))
tc, vc = cond(train), cond(val)
print('train conditions:', len(tc))
print('val conditions:', len(vc))
print('overlap:', len(tc & vc))
print('val-only:', len(vc - tc))
"train conditions: 67 val conditions: 15 overlap: 0 val-only: 15
cd /app && cat > /tmp/evalharness.py << 'EOF'
import pandas as pd, numpy as np
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])
angs = np.array([0,1.5,2.0,3.0,3.3,4.0,4.8,5.3,6.7,7.3,8.4,8.9,9.5,9.9,11.2,12.3,12.6,15.4,15.6,17.4,19.7,22.2])
def nn(x, arr): return arr[np.argmin(np.abs(arr[None,:]-np.array(x)[:,None]),axis=1)]
def cond_ids(df):
c = nn(df['chord_length'].values, chords)
v = nn(df['free_stream_velocity'].values, vels)
a = nn(df['attack_angle'].values, angs)
return list(zip(np.round(c,4),np.round(v,1),np.round(a,2)))
def metrics(y_true, y_pred, cond):
y_true=np.asarray(y_true,float); y_pred=np.asarray(y_pred,float)
err = y_pred-y_true
rmse = np.sqrt(np.mean(err**2))
mae = np.mean(np.abs(err))
df = pd.DataFrame({'c':cond,'e':err})
crmse = df.groupby('c')['e'].apply(lambda s: np.sqrt(np.mean(s**2)))
p90 = np.percentile(crmse, 90); p95 = np.percentile(crmse,95); mx = crmse.max()
return dict(rmse=rmse, mae=mae, p90=p90, p95=p95, mx=mx, ncond=len(crmse))
def report(name, y_true, y_pred, cond):
m = metrics(y_true,y_pred,cond)
tgt = dict(rmse=2.25, mae=1.55, p90=2.35, p95=4.75, mx=5.60)
flags = {k:('OK' if m[k]<=tgt[k] else 'FAIL') for k in tgt}
print(f'{name}: rmse={m[\"rmse\"]:.3f}[{flags[\"rmse\"]}] mae={m[\"mae\"]:.3f}[{flags[\"mae\"]}] '
f'p90={m[\"p90\"]:.3f}[{flags[\"p90\"]}] p95={m[\"p95\"]:.3f}[{flags[\"p95\"]}] '
f'max={m[\"mx\"]:.3f}[{flags[\"mx\"]}] ncond={m[\"ncond\"]}')
return m
EOF
echo "harness written"harness written
cd /app && python3 -c "
import sys; sys.path.insert(0,'/tmp')
import pandas as pd, numpy as np
from evalharness import cond_ids, report
import train_and_predict as tp
train = pd.read_csv('data/train.csv'); val = pd.read_csv('data/validation.csv')
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import Ridge
model = make_pipeline(tp.AirfoilFeatures(), StandardScaler(), Ridge(alpha=10.0))
model.fit(train[tp.FEATURES], train[tp.TARGET])
pred = model.predict(val[tp.FEATURES])
cond = cond_ids(val)
report('Baseline Ridge (train->val)', val[tp.TARGET], pred, cond)
"[stderr]
Traceback (most recent call last):
File "<string>", line 4, in <module>
File "/tmp/evalharness.py", line 29
print(f'{name}: rmse={m[\"rmse\"]:.3f}[{flags[\"rmse\"]}] mae={m[\"mae\"]:.3f}[{flags[\"mae\"]}] '
^
SyntaxError: unexpected character after line continuation charactercd /app && cat > /tmp/evalharness.py << 'EOF'
import pandas as pd, numpy as np
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])
angs = np.array([0,1.5,2.0,3.0,3.3,4.0,4.8,5.3,6.7,7.3,8.4,8.9,9.5,9.9,11.2,12.3,12.6,15.4,15.6,17.4,19.7,22.2])
def nn(x, arr): return arr[np.argmin(np.abs(arr[None,:]-np.array(x)[:,None]),axis=1)]
def cond_ids(df):
c = nn(df['chord_length'].values, chords)
v = nn(df['free_stream_velocity'].values, vels)
a = nn(df['attack_angle'].values, angs)
return list(zip(np.round(c,4),np.round(v,1),np.round(a,2)))
def metrics(y_true, y_pred, cond):
y_true=np.asarray(y_true,float); y_pred=np.asarray(y_pred,float)
err = y_pred-y_true
rmse = float(np.sqrt(np.mean(err**2)))
mae = float(np.mean(np.abs(err)))
df = pd.DataFrame({'c':cond,'e':err})
crmse = df.groupby('c')['e'].apply(lambda s: np.sqrt(np.mean(s**2)))
return dict(rmse=rmse, mae=mae, p90=float(np.percentile(crmse,90)),
p95=float(np.percentile(crmse,95)), mx=float(crmse.max()), ncond=len(crmse))
def report(name, y_true, y_pred, cond):
m = metrics(y_true,y_pred,cond)
tgt = dict(rmse=2.25, mae=1.55, p90=2.35, p95=4.75, mx=5.60)
def fl(k): return 'OK' if m[k]<=tgt[k] else 'FAIL'
print('{0}: rmse={1:.3f}[{2}] mae={3:.3f}[{4}] p90={5:.3f}[{6}] p95={7:.3f}[{8}] max={9:.3f}[{10}] ncond={11}'.format(
name, m['rmse'],fl('rmse'), m['mae'],fl('mae'), m['p90'],fl('p90'),
m['p95'],fl('p95'), m['mx'],fl('mx'), m['ncond']))
return m
EOF
python3 -c "
import sys; sys.path.insert(0,'/tmp'); sys.path.insert(0,'/app')
import pandas as pd, numpy as np
from evalharness import cond_ids, report
import train_and_predict as tp
train = pd.read_csv('/app/data/train.csv'); val = pd.read_csv('/app/data/validation.csv')
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import Ridge
model = make_pipeline(tp.AirfoilFeatures(), StandardScaler(), Ridge(alpha=10.0))
model.fit(train[tp.FEATURES], train[tp.TARGET])
pred = model.predict(val[tp.FEATURES])
report('Baseline Ridge (train->val)', val[tp.TARGET], pred, cond_ids(val))
"Baseline Ridge (train->val): rmse=5.840[FAIL] mae=4.697[FAIL] p90=7.948[FAIL] p95=8.562[FAIL] max=9.477[FAIL] ncond=15
cd /app && python3 -c "
import sklearn; print('sklearn', sklearn.__version__)
import numpy; print('numpy', numpy.__version__)
import scipy; print('scipy', scipy.__version__)
import pandas; print('pandas', pandas.__version__)
for m in ['xgboost','lightgbm','catboost','torch','tensorflow']:
try:
mod=__import__(m); print(m, getattr(mod,'__version__','?'))
except Exception as e:
print(m, 'NOT available')
"sklearn 1.4.2 numpy 1.26.4 scipy 1.17.1 pandas 2.2.2 xgboost NOT available lightgbm NOT available catboost NOT available torch NOT available tensorflow NOT available
cd /app && python3 -c "
import sys; sys.path.insert(0,'/tmp'); sys.path.insert(0,'/app')
import pandas as pd, numpy as np
from evalharness import cond_ids, report
import warnings; warnings.filterwarnings('ignore')
train = pd.read_csv('/app/data/train.csv'); val = pd.read_csv('/app/data/validation.csv')
FEAT=['frequency','attack_angle','chord_length','free_stream_velocity','suction_side_displacement_thickness']
TGT='scaled_sound_pressure_level'
def feateng(df):
f=df['frequency'].astype(float); a=df['attack_angle'].astype(float)
c=df['chord_length'].astype(float); v=df['free_stream_velocity'].astype(float)
t=df['suction_side_displacement_thickness'].astype(float)
X=pd.DataFrame(index=df.index)
X['logf']=np.log10(f); X['a']=a; X['logc']=np.log10(c); X['v']=v; X['logt']=np.log10(t)
X['strouhal']=np.log10(f*t/v); X['logfc']=np.log10(f*c)
return X
Xtr, Xval = feateng(train), feateng(val)
ytr, yval = train[TGT].values, val[TGT].values
cond = cond_ids(val)
from sklearn.ensemble import RandomForestRegressor, ExtraTreesRegressor, GradientBoostingRegressor, HistGradientBoostingRegressor
from sklearn.neural_network import MLPRegressor
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
models = {
'RF': RandomForestRegressor(n_estimators=400, n_jobs=-1, random_state=0),
'ET': ExtraTreesRegressor(n_estimators=400, n_jobs=-1, random_state=0),
'GBR': GradientBoostingRegressor(n_estimators=400, max_depth=3, learning_rate=0.05, random_state=0),
'HGB': HistGradientBoostingRegressor(max_iter=600, learning_rate=0.05, random_state=0),
}
for name,m in models.items():
m.fit(Xtr, ytr); p=m.predict(Xval); report(name, yval, p, cond)
# MLP
mlp = make_pipeline(StandardScaler(), MLPRegressor(hidden_layer_sizes=(128,128,64), max_iter=2000, random_state=0, early_stopping=True))
mlp.fit(Xtr,ytr); report('MLP', yval, mlp.predict(Xval), cond)
"RF: rmse=2.694[FAIL] mae=2.110[FAIL] p90=3.805[FAIL] p95=4.034[OK] max=4.308[OK] ncond=15 ET: rmse=2.158[OK] mae=1.649[FAIL] p90=3.041[FAIL] p95=3.418[OK] max=3.839[OK] ncond=15 GBR: rmse=2.606[FAIL] mae=2.006[FAIL] p90=3.852[FAIL] p95=4.147[OK] max=4.257[OK] ncond=15 HGB: rmse=2.575[FAIL] mae=1.894[FAIL] p90=3.681[FAIL] p95=4.278[OK] max=5.127[OK] ncond=15 MLP: rmse=2.979[FAIL] mae=2.116[FAIL] p90=4.146[FAIL] p95=4.881[FAIL] max=6.587[FAIL] ncond=15
cd /app && python3 -c "
import pandas as pd, numpy as np
train = pd.read_csv('/app/data/train.csv')
# Look at one condition's frequency sweep
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])
sub = train[(np.abs(train.chord_length-0.3048)<0.01)&(np.abs(train.free_stream_velocity-71.3)<1)&(np.abs(train.attack_angle)<0.3)]
sub=sub.sort_values('frequency')
print('Condition chord=0.3048 v=71.3 angle=0, n=',len(sub))
print(sub[['frequency','suction_side_displacement_thickness','scaled_sound_pressure_level']].to_string(index=False))
"Condition chord=0.3048 v=71.3 angle=0, n= 14 frequency suction_side_displacement_thickness scaled_sound_pressure_level 800.907956 0.002667 126.491888 1001.689949 0.002669 125.606080 1251.726767 0.002670 126.548943 1600.587193 0.002669 128.480796 1998.330096 0.002666 128.674150 2495.974373 0.002662 127.120413 3145.077386 0.002659 127.065030 3997.133267 0.002657 125.192026 5002.477914 0.002657 123.620691 6309.164063 0.002659 121.979015 8013.362856 0.002662 119.622313 10010.325725 0.002666 117.815577 12498.339476 0.002669 114.584747 15980.313985 0.002670 110.974459
cd /app && python3 -c "
import sys; sys.path.insert(0,'/tmp'); sys.path.insert(0,'/app')
import pandas as pd, numpy as np
from evalharness import cond_ids, report
import warnings; warnings.filterwarnings('ignore')
train = pd.read_csv('/app/data/train.csv'); val = pd.read_csv('/app/data/validation.csv')
TGT='scaled_sound_pressure_level'
def feateng(df):
f=df['frequency'].astype(float); a=df['attack_angle'].astype(float)
c=df['chord_length'].astype(float); v=df['free_stream_velocity'].astype(float)
t=df['suction_side_displacement_thickness'].astype(float)
X=pd.DataFrame(index=df.index)
X['logf']=np.log10(f); X['a']=a; X['logc']=np.log10(c); X['v']=v; X['logt']=np.log10(t)
X['strouhal']=np.log10(f*t/v); X['logfc']=np.log10(f*c); X['logft']=np.log10(f*t)
X['mach']=v/340.0; X['are']=a*np.log10(v*c)
return X
Xtr, Xval = feateng(train), feateng(val)
ytr, yval = train[TGT].values, val[TGT].values
cond = cond_ids(val)
from sklearn.svm import SVR
from sklearn.kernel_ridge import KernelRidge
from sklearn.gaussian_process import GaussianProcessRegressor
from sklearn.gaussian_process.kernels import RBF, WhiteKernel, ConstantKernel, Matern
from sklearn.neighbors import KNeighborsRegressor
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
for C in [10,30,100]:
for g in ['scale',0.1,0.3]:
m=make_pipeline(StandardScaler(), SVR(C=C, gamma=g, epsilon=0.05))
m.fit(Xtr,ytr); report('SVR C=%s g=%s'%(C,g), yval, m.predict(Xval), cond)
m=make_pipeline(StandardScaler(), KernelRidge(kernel='rbf', alpha=0.01, gamma=0.1))
m.fit(Xtr,ytr); report('KRR', yval, m.predict(Xval), cond)
m=make_pipeline(StandardScaler(), KNeighborsRegressor(n_neighbors=7, weights='distance'))
m.fit(Xtr,ytr); report('KNN', yval, m.predict(Xval), cond)
"SVR C=10 g=scale: rmse=2.676[FAIL] mae=1.943[FAIL] p90=4.361[FAIL] p95=4.714[OK] max=4.768[OK] ncond=15 SVR C=10 g=0.1: rmse=2.676[FAIL] mae=1.943[FAIL] p90=4.361[FAIL] p95=4.714[OK] max=4.768[OK] ncond=15 SVR C=10 g=0.3: rmse=3.057[FAIL] mae=2.263[FAIL] p90=4.727[FAIL] p95=5.714[FAIL] max=7.010[FAIL] ncond=15 SVR C=30 g=scale: rmse=2.731[FAIL] mae=1.961[FAIL] p90=4.284[FAIL] p95=4.745[OK] max=5.444[OK] ncond=15 SVR C=30 g=0.1: rmse=2.731[FAIL] mae=1.961[FAIL] p90=4.284[FAIL] p95=4.745[OK] max=5.444[OK] ncond=15 SVR C=30 g=0.3: rmse=3.134[FAIL] mae=2.344[FAIL] p90=4.783[FAIL] p95=5.895[FAIL] max=7.238[FAIL] ncond=15 SVR C=100 g=scale: rmse=3.099[FAIL] mae=2.174[FAIL] p90=5.075[FAIL] p95=6.113[FAIL] max=7.240[FAIL] ncond=15 SVR C=100 g=0.1: rmse=3.099[FAIL] mae=2.174[FAIL] p90=5.075[FAIL] p95=6.113[FAIL] max=7.240[FAIL] ncond=15 SVR C=100 g=0.3: rmse=3.303[FAIL] mae=2.418[FAIL] p90=5.260[FAIL] p95=6.367[FAIL] max=7.566[FAIL] ncond=15 KRR: rmse=6.042[FAIL] mae=3.585[FAIL] p90=10.375[FAIL] p95=15.458[FAIL] max=19.778[FAIL] ncond=15 KNN: rmse=3.078[FAIL] mae=2.330[FAIL] p90=4.544[FAIL] p95=4.627[OK] max=4.759[OK] ncond=15
cd /app && python3 -c "
import pandas as pd, numpy as np
train = pd.read_csv('/app/data/train.csv')
sub = train[(np.abs(train.chord_length-0.3048)<0.01)&(np.abs(train.free_stream_velocity-71.3)<1)&(np.abs(train.attack_angle)<0.3)].sort_values('frequency')
print(sub[['attack_angle','chord_length','free_stream_velocity']].to_string())
print()
print('unique chord in this group:', sub.chord_length.nunique())
print('unique angle:', sub.attack_angle.nunique(), 'unique vel:', sub.free_stream_velocity.nunique())
"attack_angle chord_length free_stream_velocity 760 0.032542 0.304949 71.402392 565 0.025513 0.305072 71.389106 431 0.014901 0.305148 71.368226 709 0.002196 0.305164 71.341530 740 -0.010817 0.305117 71.311294 65 -0.022311 0.305015 71.280095 410 -0.030672 0.304876 71.250593 529 -0.034724 0.304724 71.225303 404 -0.033900 0.304585 71.206379 99 -0.028314 0.304483 71.195435 679 -0.018751 0.304436 71.193404 96 -0.006555 0.304452 71.200459 425 0.006562 0.304529 71.215998 490 0.018758 0.304652 71.238698 unique chord in this group: 14 unique angle: 14 unique vel: 14
cd /app && python3 -c "
import sys; sys.path.insert(0,'/tmp'); sys.path.insert(0,'/app')
import pandas as pd, numpy as np
from evalharness import cond_ids
import warnings; warnings.filterwarnings('ignore')
train = pd.read_csv('/app/data/train.csv'); val = pd.read_csv('/app/data/validation.csv')
TGT='scaled_sound_pressure_level'
def feateng(df):
f=df['frequency'].astype(float); a=df['attack_angle'].astype(float)
c=df['chord_length'].astype(float); v=df['free_stream_velocity'].astype(float)
t=df['suction_side_displacement_thickness'].astype(float)
X=pd.DataFrame(index=df.index)
X['logf']=np.log10(f); X['a']=a; X['logc']=np.log10(c); X['v']=v; X['logt']=np.log10(t)
X['strouhal']=np.log10(f*t/v); X['logfc']=np.log10(f*c)
return X
Xtr,Xval=feateng(train),feateng(val)
ytr,yval=train[TGT].values,val[TGT].values
cond=cond_ids(val)
from sklearn.ensemble import ExtraTreesRegressor
m=ExtraTreesRegressor(n_estimators=600,n_jobs=-1,random_state=0)
m.fit(Xtr,ytr); p=m.predict(Xval)
df=pd.DataFrame({'c':cond,'e':p-yval,'y':yval})
g=df.groupby('c').agg(rmse=('e',lambda s:np.sqrt(np.mean(s**2))), bias=('e','mean'), n=('e','size'), ymean=('y','mean'))
print(g.sort_values('rmse',ascending=False).to_string())
"rmse bias n ymean c (0.0254, 71.3, 0.0) 3.824442 -0.028933 10 132.879889 (0.0254, 39.6, 17.4) 3.200302 0.023598 15 123.744412 (0.1016, 55.5, 12.3) 2.812008 -2.194908 16 125.177910 (0.1016, 39.6, 12.3) 2.621539 -2.365317 16 121.085856 (0.1016, 55.5, 6.7) 2.377539 -1.134131 8 133.384306 (0.0508, 55.5, 8.4) 2.165724 0.419131 12 128.445698 (0.0254, 71.3, 4.8) 2.066155 -0.030550 11 130.019743 (0.2286, 31.7, 4.0) 1.948202 -0.813805 15 125.341869 (0.3048, 31.7, 0.0) 1.940895 1.485691 18 121.421665 (0.1016, 71.3, 3.3) 1.465301 0.305242 12 128.260271 (0.0254, 39.6, 0.0) 1.388290 0.406471 11 132.420735 (0.0254, 39.6, 4.8) 1.343732 0.624983 14 127.157219 (0.0508, 55.5, 0.0) 1.254669 -0.119444 13 130.834232 (0.1524, 31.7, 9.9) 1.037413 -0.498181 16 121.852227 (0.1524, 39.6, 0.0) 1.022816 0.134518 14 126.222150
cd /app && cat > /tmp/gcv.py << 'EOF'
import sys; sys.path.insert(0,'/tmp'); sys.path.insert(0,'/app')
import pandas as pd, numpy as np
from evalharness import cond_ids, metrics
import warnings; warnings.filterwarnings('ignore')
from sklearn.model_selection import GroupKFold
train = pd.read_csv('/app/data/train.csv'); val = pd.read_csv('/app/data/validation.csv')
ALL = pd.concat([train,val], ignore_index=True)
TGT='scaled_sound_pressure_level'
COND = cond_ids(ALL)
# integer group ids
uniq = {c:i for i,c in enumerate(sorted(set(COND)))}
GROUPS = np.array([uniq[c] for c in COND])
print('total conditions:', len(uniq), 'rows:', len(ALL))
def grouped_eval(make_model, feateng, n_splits=10, seed=0):
y = ALL[TGT].values
oof = np.full(len(ALL), np.nan)
gkf = GroupKFold(n_splits=n_splits)
for tr,te in gkf.split(ALL, y, GROUPS):
Xtr = feateng(ALL.iloc[tr]); Xte = feateng(ALL.iloc[te])
m = make_model(); m.fit(Xtr, y[tr]); oof[te] = m.predict(Xte)
m = metrics(y, oof, COND)
return m, oof
def pr(name, m):
tgt = dict(rmse=2.25, mae=1.55, p90=2.35, p95=4.75, mx=5.60)
def fl(k): return 'OK' if m[k]<=tgt[k] else 'XX'
print('{0:28s} rmse={1:.3f}[{2}] mae={3:.3f}[{4}] p90={5:.3f}[{6}] p95={7:.3f}[{8}] max={9:.3f}[{10}] nc={11}'.format(
name, m['rmse'],fl('rmse'), m['mae'],fl('mae'), m['p90'],fl('p90'), m['p95'],fl('p95'), m['mx'],fl('mx'), m['ncond']))
EOF
echo "gcv module written"gcv module written
cd /app && python3 -c "
import sys; sys.path.insert(0,'/tmp')
from gcv import grouped_eval, pr
import numpy as np, pandas as pd
def feateng(df):
f=df['frequency'].astype(float); a=df['attack_angle'].astype(float)
c=df['chord_length'].astype(float); v=df['free_stream_velocity'].astype(float)
t=df['suction_side_displacement_thickness'].astype(float)
X=pd.DataFrame(index=df.index)
X['logf']=np.log10(f); X['a']=a; X['logc']=np.log10(c); X['v']=v; X['logt']=np.log10(t)
X['strouhal']=np.log10(f*t/v); X['logfc']=np.log10(f*c)
return X
from sklearn.ensemble import ExtraTreesRegressor, RandomForestRegressor, HistGradientBoostingRegressor
pr('ET-600', grouped_eval(lambda:ExtraTreesRegressor(n_estimators=600,n_jobs=-1,random_state=0), feateng)[0])
pr('RF-600', grouped_eval(lambda:RandomForestRegressor(n_estimators=600,n_jobs=-1,random_state=0), feateng)[0])
pr('HGB', grouped_eval(lambda:HistGradientBoostingRegressor(max_iter=600,learning_rate=0.05,random_state=0), feateng)[0])
"total conditions: 82 rows: 1157 ET-600 rmse=2.122[OK] mae=1.588[XX] p90=2.913[XX] p95=3.701[OK] max=4.738[OK] nc=82 RF-600 rmse=2.425[XX] mae=1.807[XX] p90=3.430[XX] p95=4.200[OK] max=5.363[OK] nc=82 HGB rmse=2.343[XX] mae=1.743[XX] p90=3.506[XX] p95=4.056[OK] max=5.838[XX] nc=82
cd /app && python3 -c "
import sys; sys.path.insert(0,'/tmp')
from gcv import grouped_eval, pr
import numpy as np, pandas as pd
from sklearn.ensemble import ExtraTreesRegressor
def fe_rich(df):
f=df['frequency'].astype(float); a=df['attack_angle'].astype(float)
c=df['chord_length'].astype(float); v=df['free_stream_velocity'].astype(float)
t=df['suction_side_displacement_thickness'].astype(float)
X=pd.DataFrame(index=df.index)
X['logf']=np.log10(f); X['a']=a; X['logc']=np.log10(c); X['v']=v; X['logt']=np.log10(t)
X['St']=np.log10(f*t/v); X['logfc']=np.log10(f*c); X['logft']=np.log10(f*t)
X['Re_c']=np.log10(v*c); X['Re_t']=np.log10(v*t); X['mach']=v/340.0
X['a_logc']=a*np.log10(c); X['a_v']=a*v; X['St_a']=np.log10(f*t/v)*a
X['tc']=t/c; X['logtc']=np.log10(t/c)
return X
for msl in [1,2,3]:
for mf in [0.6,0.8,1.0]:
m,_=grouped_eval(lambda:ExtraTreesRegressor(n_estimators=500,n_jobs=-1,random_state=0,
min_samples_leaf=msl,max_features=mf), fe_rich)
pr('ET msl=%d mf=%.1f'%(msl,mf), m)
"total conditions: 82 rows: 1157 ET msl=1 mf=0.6 rmse=2.219[OK] mae=1.651[XX] p90=3.227[XX] p95=3.632[OK] max=5.585[OK] nc=82 ET msl=1 mf=0.8 rmse=2.199[OK] mae=1.637[XX] p90=3.105[XX] p95=3.698[OK] max=5.506[OK] nc=82 ET msl=1 mf=1.0 rmse=2.176[OK] mae=1.615[XX] p90=3.034[XX] p95=3.729[OK] max=5.512[OK] nc=82 ET msl=2 mf=0.6 rmse=2.249[OK] mae=1.685[XX] p90=3.247[XX] p95=3.689[OK] max=5.620[XX] nc=82 ET msl=2 mf=0.8 rmse=2.224[OK] mae=1.656[XX] p90=3.146[XX] p95=3.727[OK] max=5.535[OK] nc=82 ET msl=2 mf=1.0 rmse=2.198[OK] mae=1.639[XX] p90=3.038[XX] p95=3.724[OK] max=5.466[OK] nc=82 ET msl=3 mf=0.6 rmse=2.320[XX] mae=1.742[XX] p90=3.293[XX] p95=3.777[OK] max=5.594[OK] nc=82 ET msl=3 mf=0.8 rmse=2.283[XX] mae=1.712[XX] p90=3.225[XX] p95=3.713[OK] max=5.468[OK] nc=82 ET msl=3 mf=1.0 rmse=2.254[XX] mae=1.685[XX] p90=3.166[XX] p95=3.766[OK] max=5.462[OK] nc=82
cd /app && python3 -c "
import sys; sys.path.insert(0,'/tmp')
from gcv import grouped_eval, pr
import numpy as np, pandas as pd
from sklearn.ensemble import ExtraTreesRegressor
def mk(cols):
def fe(df):
f=df['frequency'].astype(float); a=df['attack_angle'].astype(float)
c=df['chord_length'].astype(float); v=df['free_stream_velocity'].astype(float)
t=df['suction_side_displacement_thickness'].astype(float)
d=dict(logf=np.log10(f), a=a, logc=np.log10(c), v=v, logt=np.log10(t),
St=np.log10(f*t/v), logfc=np.log10(f*c), logft=np.log10(f*t),
Re_c=np.log10(v*c), mach=v/340.0, f=f, t=t, c=c)
return pd.DataFrame({k:d[k] for k in cols}, index=df.index)
return fe
base=['logf','a','logc','v','logt','St','logfc']
mk_model=lambda:ExtraTreesRegressor(n_estimators=500,n_jobs=-1,random_state=0)
pr('base7', grouped_eval(mk_model, mk(base))[0])
pr('base-St', grouped_eval(mk_model, mk([x for x in base if x!='St']))[0])
pr('base-logfc', grouped_eval(mk_model, mk([x for x in base if x!='logfc']))[0])
pr('base-logc', grouped_eval(mk_model, mk([x for x in base if x!='logc']))[0])
pr('base+logft', grouped_eval(mk_model, mk(base+['logft']))[0])
pr('base+Re_c', grouped_eval(mk_model, mk(base+['Re_c']))[0])
pr('core5', grouped_eval(mk_model, mk(['logf','a','logc','v','logt']))[0])
pr('core5+St', grouped_eval(mk_model, mk(['logf','a','logc','v','logt','St']))[0])
"total conditions: 82 rows: 1157 base7 rmse=2.121[OK] mae=1.588[XX] p90=2.926[XX] p95=3.700[OK] max=4.736[OK] nc=82 base-St rmse=2.382[XX] mae=1.787[XX] p90=3.572[XX] p95=4.190[OK] max=5.364[OK] nc=82 base-logfc rmse=2.214[OK] mae=1.662[XX] p90=3.118[XX] p95=3.613[OK] max=5.053[OK] nc=82 base-logc rmse=2.230[OK] mae=1.666[XX] p90=3.464[XX] p95=3.793[OK] max=4.669[OK] nc=82 base+logft rmse=2.110[OK] mae=1.589[XX] p90=2.911[XX] p95=3.549[OK] max=4.962[OK] nc=82 base+Re_c rmse=2.160[OK] mae=1.611[XX] p90=3.052[XX] p95=3.710[OK] max=5.173[OK] nc=82 core5 rmse=2.617[XX] mae=1.991[XX] p90=3.936[XX] p95=4.296[OK] max=5.615[XX] nc=82 core5+St rmse=2.214[OK] mae=1.662[XX] p90=3.118[XX] p95=3.613[OK] max=5.053[OK] nc=82
cd /app && python3 -c "
import sys; sys.path.insert(0,'/tmp')
from gcv import grouped_eval, pr
import numpy as np, pandas as pd
from sklearn.neural_network import MLPRegressor
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
def fe(df):
f=df['frequency'].astype(float); a=df['attack_angle'].astype(float)
c=df['chord_length'].astype(float); v=df['free_stream_velocity'].astype(float)
t=df['suction_side_displacement_thickness'].astype(float)
return pd.DataFrame(dict(logf=np.log10(f), a=a, logc=np.log10(c), v=v, logt=np.log10(t),
St=np.log10(f*t/v), logfc=np.log10(f*c), logft=np.log10(f*t)), index=df.index)
def mk(arch, alpha):
return make_pipeline(StandardScaler(),
MLPRegressor(hidden_layer_sizes=arch, alpha=alpha, max_iter=3000,
random_state=0, activation='tanh'))
for arch in [(64,64),(128,64),(100,100,50)]:
for alpha in [1e-3,1e-2]:
pr('MLP %s a=%.e'%(str(arch),alpha), grouped_eval(lambda arch=arch,alpha=alpha:mk(arch,alpha), fe)[0])
"[stderr] total conditions: 82 rows: 1157 MLP (64, 64) a=1e-03 rmse=2.822[XX] mae=2.022[XX] p90=3.621[XX] p95=4.219[OK] max=10.884[XX] nc=82 MLP (64, 64) a=1e-02 rmse=2.848[XX] mae=2.019[XX] p90=3.809[XX] p95=4.160[OK] max=11.034[XX] nc=82 MLP (128, 64) a=1e-03 rmse=4.948[XX] mae=3.427[XX] p90=8.408[XX] p95=9.273[XX] max=15.192[XX] nc=82 MLP (128, 64) a=1e-02 rmse=4.822[XX] mae=3.321[XX] p90=8.722[XX] p95=9.736[XX] max=13.432[XX] nc=82 MLP (100, 100, 50) a=1e-03 rmse=6.739[XX] mae=5.465[XX] p90=8.413[XX] p95=9.014[XX] max=13.055[XX] nc=82Shell command timed out. Tool interaction exceeded 300 seconds.
restart: true
<system>tool has been restarted.</system>
cd /app && timeout 120 python3 -c "
import pandas as pd, numpy as np
train = pd.read_csv('/app/data/train.csv'); val=pd.read_csv('/app/data/validation.csv')
ALL=pd.concat([train,val],ignore_index=True)
# Test Strouhal collapse: group by condition, look at spectral shape
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])
angs=np.array([0,1.5,2.0,3.0,3.3,4.0,4.8,5.3,6.7,7.3,8.4,8.9,9.5,9.9,11.2,12.3,12.6,15.4,15.6,17.4,19.7,22.2])
def nn(x,arr): return arr[np.argmin(np.abs(arr[None,:]-np.array(x)[:,None]),axis=1)]
f=ALL.frequency.values; t=ALL.suction_side_displacement_thickness.values; v=ALL.free_stream_velocity.values
St=f*t/v
ALL['St']=St; ALL['logSt']=np.log10(St)
print('log10(St) range:', np.round(np.log10(St).min(),2), np.round(np.log10(St).max(),2))
# For each condition, find peak SPL and peak frequency
c=nn(ALL.chord_length.values,chords); vv=nn(v,vels); a=nn(ALL.attack_angle.values,angs)
ALL['cid']=list(zip(np.round(c,4),np.round(vv,1),np.round(a,2)))
# correlation of SPL with logSt overall
print('corr SPL vs logSt:', np.corrcoef(ALL.logSt, ALL.scaled_sound_pressure_level)[0,1])
" log10(St) range: -1.97 0.97 corr SPL vs logSt: -0.579236477883863
cd /app && timeout 280 python3 -c "
import sys; sys.path.insert(0,'/tmp')
from gcv import grouped_eval, pr
import numpy as np, pandas as pd
from sklearn.ensemble import ExtraTreesRegressor
def mk(cols):
def fe(df):
f=df['frequency'].astype(float); a=df['attack_angle'].astype(float)
c=df['chord_length'].astype(float); v=df['free_stream_velocity'].astype(float)
t=df['suction_side_displacement_thickness'].astype(float)
d=dict(logf=np.log10(f), a=a, logc=np.log10(c), v=v, logt=np.log10(t),
St=np.log10(f*t/v), logfc=np.log10(f*c), logft=np.log10(f*t),
a2=a*a, asep=np.maximum(0,a-8.0), a_logt=a*np.log10(t), St2=np.log10(f*t/v)**2)
return pd.DataFrame({k:d[k] for k in cols}, index=df.index)
return fe
base=['logf','a','logc','v','logt','St','logfc','logft']
M=lambda:ExtraTreesRegressor(n_estimators=400,n_jobs=-1,random_state=0)
pr('base8', grouped_eval(M, mk(base))[0])
pr('base8+a2', grouped_eval(M, mk(base+['a2']))[0])
pr('base8+asep', grouped_eval(M, mk(base+['asep']))[0])
pr('base8+St2', grouped_eval(M, mk(base+['St2']))[0])
pr('base8+a_logt', grouped_eval(M, mk(base+['a_logt']))[0])
pr('base8+all', grouped_eval(M, mk(base+['a2','asep','a_logt','St2']))[0])
"total conditions: 82 rows: 1157 base8 rmse=2.108[OK] mae=1.587[XX] p90=2.944[XX] p95=3.506[OK] max=4.945[OK] nc=82 base8+a2 rmse=2.100[OK] mae=1.575[XX] p90=2.918[XX] p95=3.583[OK] max=5.042[OK] nc=82 base8+asep rmse=2.096[OK] mae=1.577[XX] p90=2.977[XX] p95=3.371[OK] max=4.972[OK] nc=82 base8+St2 rmse=2.117[OK] mae=1.586[XX] p90=2.897[XX] p95=3.734[OK] max=4.965[OK] nc=82 base8+a_logt rmse=2.100[OK] mae=1.578[XX] p90=2.862[XX] p95=3.567[OK] max=5.080[OK] nc=82 base8+all rmse=2.106[OK] mae=1.577[XX] p90=2.753[XX] p95=3.596[OK] max=5.065[OK] nc=82
cd /app && timeout 280 python3 -c "
import sys; sys.path.insert(0,'/tmp')
from gcv import grouped_eval, pr
import numpy as np, pandas as pd
from sklearn.gaussian_process import GaussianProcessRegressor
from sklearn.gaussian_process.kernels import Matern, RBF, WhiteKernel, ConstantKernel as C
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
def fe(df):
f=df['frequency'].astype(float); a=df['attack_angle'].astype(float)
c=df['chord_length'].astype(float); v=df['free_stream_velocity'].astype(float)
t=df['suction_side_displacement_thickness'].astype(float)
return pd.DataFrame(dict(logf=np.log10(f), a=a, logc=np.log10(c), v=v, logt=np.log10(t),
St=np.log10(f*t/v), logfc=np.log10(f*c)), index=df.index)
def mkgp(ls, nu, noise):
k = C(50.0)*Matern(length_scale=ls, nu=nu) + WhiteKernel(noise)
return make_pipeline(StandardScaler(),
GaussianProcessRegressor(kernel=k, optimizer=None, normalize_y=True, alpha=1e-6))
for ls in [1.0,2.0,3.0]:
for nu in [1.5,2.5]:
pr('GP ls=%.1f nu=%.1f'%(ls,nu), grouped_eval(lambda ls=ls,nu=nu:mkgp(ls,nu,1.0), fe, n_splits=5)[0])
"total conditions: 82 rows: 1157 GP ls=1.0 nu=1.5 rmse=2.607[XX] mae=1.929[XX] p90=3.688[XX] p95=4.812[XX] max=5.853[XX] nc=82 GP ls=1.0 nu=2.5 rmse=2.616[XX] mae=1.937[XX] p90=3.739[XX] p95=4.720[OK] max=5.846[XX] nc=82 GP ls=2.0 nu=1.5 rmse=2.438[XX] mae=1.809[XX] p90=3.472[XX] p95=3.892[OK] max=5.852[XX] nc=82 GP ls=2.0 nu=2.5 rmse=2.453[XX] mae=1.822[XX] p90=3.370[XX] p95=4.093[OK] max=6.187[XX] nc=82 GP ls=3.0 nu=1.5 rmse=2.423[XX] mae=1.785[XX] p90=3.357[XX] p95=3.930[OK] max=6.759[XX] nc=82 GP ls=3.0 nu=2.5 rmse=2.430[XX] mae=1.780[XX] p90=3.363[XX] p95=3.575[OK] max=7.313[XX] nc=82
cd /app && timeout 290 python3 -c "
import sys; sys.path.insert(0,'/tmp')
from gcv import grouped_eval, pr
from evalharness import metrics
import numpy as np, pandas as pd
from sklearn.ensemble import ExtraTreesRegressor, RandomForestRegressor, HistGradientBoostingRegressor, GradientBoostingRegressor
import warnings; warnings.filterwarnings('ignore')
train=pd.read_csv('/app/data/train.csv'); val=pd.read_csv('/app/data/validation.csv')
ALL=pd.concat([train,val],ignore_index=True)
from evalharness import cond_ids
COND=cond_ids(ALL); y=ALL['scaled_sound_pressure_level'].values
def fe(df):
f=df['frequency'].astype(float); a=df['attack_angle'].astype(float)
c=df['chord_length'].astype(float); v=df['free_stream_velocity'].astype(float)
t=df['suction_side_displacement_thickness'].astype(float)
return pd.DataFrame(dict(logf=np.log10(f), a=a, logc=np.log10(c), v=v, logt=np.log10(t),
St=np.log10(f*t/v), logfc=np.log10(f*c), logft=np.log10(f*t),
a2=a*a, asep=np.maximum(0,a-8.0), a_logt=a*np.log10(t), St2=np.log10(f*t/v)**2), index=df.index)
models={
'ET': lambda:ExtraTreesRegressor(n_estimators=600,n_jobs=-1,random_state=0),
'ET2': lambda:ExtraTreesRegressor(n_estimators=600,n_jobs=-1,random_state=7,max_features=0.7),
'HGB': lambda:HistGradientBoostingRegressor(max_iter=700,learning_rate=0.04,max_leaf_nodes=31,random_state=0,l2_regularization=1.0),
'GBR': lambda:GradientBoostingRegressor(n_estimators=500,max_depth=3,learning_rate=0.04,subsample=0.8,random_state=0),
}
oofs={}
for name,mk in models.items():
m,oof=grouped_eval(mk, fe, n_splits=10); oofs[name]=oof; pr(name, m)
# blends
print('--- blends ---')
pr('ET+HGB', metrics(y,(oofs['ET']+oofs['HGB'])/2, COND))
pr('ET+GBR', metrics(y,(oofs['ET']+oofs['GBR'])/2, COND))
pr('ET+ET2+HGB+GBR', metrics(y,(oofs['ET']+oofs['ET2']+oofs['HGB']+oofs['GBR'])/4, COND))
pr('ET2+HGB', metrics(y,(oofs['ET2']+oofs['HGB'])/2, COND))
pr('0.5ET+0.25HGB+0.25GBR', metrics(y,0.5*oofs['ET']+0.25*oofs['HGB']+0.25*oofs['GBR'], COND))
np.save('/tmp/oof_ET.npy',oofs['ET']); np.save('/tmp/oof_HGB.npy',oofs['HGB']); np.save('/tmp/oof_GBR.npy',oofs['GBR']); np.save('/tmp/y.npy',y)
"total conditions: 82 rows: 1157 ET rmse=2.112[OK] mae=1.580[XX] p90=2.773[XX] p95=3.670[OK] max=5.083[OK] nc=82 ET2 rmse=2.109[OK] mae=1.583[XX] p90=2.802[XX] p95=3.698[OK] max=5.074[OK] nc=82 HGB rmse=2.376[XX] mae=1.776[XX] p90=3.495[XX] p95=3.840[OK] max=5.576[OK] nc=82 GBR rmse=2.480[XX] mae=1.832[XX] p90=3.529[XX] p95=4.469[OK] max=6.944[XX] nc=82 --- blends --- ET+HGB rmse=2.130[OK] mae=1.595[XX] p90=2.995[XX] p95=3.784[OK] max=4.642[OK] nc=82 ET+GBR rmse=2.162[OK] mae=1.612[XX] p90=2.974[XX] p95=3.991[OK] max=5.346[OK] nc=82 ET+ET2+HGB+GBR rmse=2.125[OK] mae=1.590[XX] p90=2.959[XX] p95=3.852[OK] max=4.935[OK] nc=82 ET2+HGB rmse=2.129[OK] mae=1.597[XX] p90=3.045[XX] p95=3.801[OK] max=4.638[OK] nc=82 0.5ET+0.25HGB+0.25GBR rmse=2.126[OK] mae=1.589[XX] p90=2.940[XX] p95=3.869[OK] max=4.903[OK] nc=82
cd /app && timeout 200 python3 -c "
import sys; sys.path.insert(0,'/tmp')
from gcv import grouped_eval
import numpy as np, pandas as pd
from sklearn.ensemble import ExtraTreesRegressor
train=pd.read_csv('/app/data/train.csv'); val=pd.read_csv('/app/data/validation.csv')
ALL=pd.concat([train,val],ignore_index=True)
def fe(df):
f=df['frequency'].astype(float); a=df['attack_angle'].astype(float)
c=df['chord_length'].astype(float); v=df['free_stream_velocity'].astype(float)
t=df['suction_side_displacement_thickness'].astype(float)
return pd.DataFrame(dict(logf=np.log10(f), a=a, logc=np.log10(c), v=v, logt=np.log10(t),
St=np.log10(f*t/v), logfc=np.log10(f*c), logft=np.log10(f*t),
a2=a*a, asep=np.maximum(0,a-8.0), a_logt=a*np.log10(t), St2=np.log10(f*t/v)**2), index=df.index)
m,oof=grouped_eval(lambda:ExtraTreesRegressor(n_estimators=500,n_jobs=-1,random_state=0), fe, n_splits=10)
err=oof-ALL['scaled_sound_pressure_level'].values
ALL['err']=err; ALL['abserr']=np.abs(err)
ALL['logf']=np.log10(ALL.frequency)
print('=== mean |err| by freq decile ===')
ALL['fb']=pd.qcut(ALL.logf,6)
print(ALL.groupby('fb',observed=True)['err'].agg(['mean','std','count']).round(3).to_string())
print('=== mean err by angle bin ===')
ALL['ab']=pd.cut(ALL.attack_angle,[-1,2,5,8,11,14,23])
print(ALL.groupby('ab',observed=True)['err'].agg(['mean','std','count']).round(3).to_string())
print('=== by chord ===')
ALL['cb']=pd.cut(ALL.chord_length,[0,0.04,0.07,0.13,0.19,0.26,0.4])
print(ALL.groupby('cb',observed=True)['err'].agg(['mean','std','count']).round(3).to_string())
"total conditions: 82 rows: 1157
=== mean |err| by freq decile ===
mean std count
fb
(2.299, 2.699] 0.104 3.021 193
(2.699, 2.999] -0.149 2.124 193
(2.999, 3.205] 0.026 1.830 193
(3.205, 3.498] 0.027 1.589 192
(3.498, 3.699] -0.185 1.867 193
(3.699, 4.302] 0.190 1.913 193
=== mean err by angle bin ===
mean std count
ab
(-1, 2] 0.036 1.646 286
(2, 5] -0.104 1.427 246
(5, 8] 0.002 2.261 142
(8, 11] -0.002 2.149 154
(11, 14] -0.097 2.772 158
(14, 23] 0.194 2.679 171
=== by chord ===
mean std count
cb
(0.0, 0.04] -0.301 2.153 235
(0.04, 0.07] 0.414 2.478 185
(0.07, 0.13] -0.469 2.082 250
(0.13, 0.19] 0.758 2.162 169
(0.19, 0.26] -0.228 1.753 193
(0.26, 0.4] 0.239 1.407 125cd /app && timeout 200 python3 -c "
import sys; sys.path.insert(0,'/tmp')
from gcv import grouped_eval
from evalharness import cond_ids
import numpy as np, pandas as pd
from sklearn.ensemble import ExtraTreesRegressor
train=pd.read_csv('/app/data/train.csv'); val=pd.read_csv('/app/data/validation.csv')
ALL=pd.concat([train,val],ignore_index=True); COND=cond_ids(ALL)
y=ALL['scaled_sound_pressure_level'].values
def fe(df):
f=df['frequency'].astype(float); a=df['attack_angle'].astype(float)
c=df['chord_length'].astype(float); v=df['free_stream_velocity'].astype(float)
t=df['suction_side_displacement_thickness'].astype(float)
return pd.DataFrame(dict(logf=np.log10(f), a=a, logc=np.log10(c), v=v, logt=np.log10(t),
St=np.log10(f*t/v), logfc=np.log10(f*c), logft=np.log10(f*t),
a2=a*a, asep=np.maximum(0,a-8.0), a_logt=a*np.log10(t), St2=np.log10(f*t/v)**2), index=df.index)
_,oof=grouped_eval(lambda:ExtraTreesRegressor(n_estimators=800,n_jobs=-1,random_state=0), fe, n_splits=10)
df=pd.DataFrame({'c':COND,'e':oof-y})
cr=df.groupby('c')['e'].apply(lambda s:np.sqrt(np.mean(s**2))).sort_values(ascending=False)
print('worst 12 conditions:'); print(cr.head(12).to_string())
print()
print('n conditions > 2.35:', (cr>2.35).sum(), 'of', len(cr))
print('p90=%.3f p95=%.3f'%(np.percentile(cr,90),np.percentile(cr,95)))
"total conditions: 82 rows: 1157 worst 12 conditions: c (0.1524, 39.6, 12.6) 5.091927 (0.0254, 71.3, 0.0) 3.981369 (0.2286, 71.3, 7.3) 3.895024 (0.0508, 71.3, 19.7) 3.837997 (0.0508, 31.7, 8.4) 3.651712 (0.1016, 71.3, 15.6) 3.339254 (0.0254, 39.6, 22.2) 3.320620 (0.1524, 71.3, 7.3) 2.817991 (0.0254, 39.6, 17.4) 2.797618 (0.1016, 55.5, 6.7) 2.772422 (0.1016, 39.6, 8.9) 2.735121 (0.1016, 31.7, 12.3) 2.698755 n conditions > 2.35: 20 of 82 p90=2.795 p95=3.636
cd /app && timeout 250 python3 -c "
import sys; sys.path.insert(0,'/tmp')
from gcv import grouped_eval
from evalharness import cond_ids, metrics
import numpy as np, pandas as pd
from sklearn.ensemble import ExtraTreesRegressor
train=pd.read_csv('/app/data/train.csv'); val=pd.read_csv('/app/data/validation.csv')
ALL=pd.concat([train,val],ignore_index=True); COND=cond_ids(ALL)
y=ALL['scaled_sound_pressure_level'].values
logf=np.log10(ALL.frequency.values)
def fe(df):
f=df['frequency'].astype(float); a=df['attack_angle'].astype(float)
c=df['chord_length'].astype(float); v=df['free_stream_velocity'].astype(float)
t=df['suction_side_displacement_thickness'].astype(float)
return pd.DataFrame(dict(logf=np.log10(f), a=a, logc=np.log10(c), v=v, logt=np.log10(t),
St=np.log10(f*t/v), logfc=np.log10(f*c), logft=np.log10(f*t),
a2=a*a, asep=np.maximum(0,a-8.0), a_logt=a*np.log10(t), St2=np.log10(f*t/v)**2), index=df.index)
_,oof=grouped_eval(lambda:ExtraTreesRegressor(n_estimators=800,n_jobs=-1,random_state=0), fe, n_splits=10)
def pr(n,m):
print('%-22s rmse=%.3f mae=%.3f p90=%.3f p95=%.3f max=%.3f'%(n,m['rmse'],m['mae'],m['p90'],m['p95'],m['mx']))
pr('ET raw', metrics(y,oof,COND))
# smooth within condition: polynomial in logf
cid=pd.Series(COND)
for deg in [2,3,4]:
sm=oof.copy()
for c,idx in pd.Series(range(len(COND))).groupby(cid).groups.items():
idx=np.array(idx);
if len(idx)>=deg+2:
x=logf[idx]; p=np.polyfit(x,oof[idx],deg); sm[idx]=np.polyval(p,x)
pr('ET poly deg=%d'%deg, metrics(y,sm,COND))
"total conditions: 82 rows: 1157 ET raw rmse=2.107 mae=1.577 p90=2.795 p95=3.636 max=5.092 ET poly deg=2 rmse=2.424 mae=1.828 p90=3.596 p95=3.980 max=5.154 ET poly deg=3 rmse=2.267 mae=1.697 p90=3.553 p95=3.910 max=5.123 ET poly deg=4 rmse=2.212 mae=1.647 p90=3.412 p95=3.832 max=5.095
cd /app && timeout 120 python3 -c "
import numpy as np, pandas as pd
train=pd.read_csv('/app/data/train.csv'); val=pd.read_csv('/app/data/validation.csv')
ALL=pd.concat([train,val],ignore_index=True)
# Examine rows near chord 0.1524, vel 39.6, angle ~12.3-12.7
sub=ALL[(np.abs(ALL.chord_length-0.1524)<0.003)&(np.abs(ALL.free_stream_velocity-39.6)<1)&(ALL.attack_angle>11.5)&(ALL.attack_angle<13.5)].sort_values(['attack_angle','frequency'])
print('rows in chord0.1524 v39.6 angle~12:', len(sub))
print(sub[['attack_angle','frequency','suction_side_displacement_thickness','scaled_sound_pressure_level']].round(4).to_string())
"rows in chord0.1524 v39.6 angle~12: 16
attack_angle frequency suction_side_displacement_thickness scaled_sound_pressure_level
619 12.5650 6308.7375 0.0586 105.3786
22 12.5674 5008.4445 0.0585 106.4291
420 12.5718 199.7950 0.0584 116.2533
498 12.5743 4004.5115 0.0585 106.2408
889 12.5814 249.5829 0.0584 116.8960
830 12.5849 3149.9702 0.0584 108.8268
248 12.5937 314.5403 0.0585 117.4579
351 12.5976 2497.1451 0.0583 109.2480
635 12.6068 399.7981 0.0586 117.8738
541 12.6106 1996.6181 0.0583 112.2933
773 12.6189 500.3540 0.0586 116.7178
588 12.6221 1597.7986 0.0583 113.2034
412 12.6284 630.9822 0.0585 118.6923
595 12.6306 1249.5528 0.0583 114.9303
562 12.6340 801.2906 0.0584 118.9846
106 12.6347 1000.8432 0.0584 117.1859cd /app && timeout 280 python3 -c "
import sys; sys.path.insert(0,'/tmp')
from gcv import grouped_eval
from evalharness import cond_ids, metrics
import numpy as np, pandas as pd
from sklearn.preprocessing import SplineTransformer, StandardScaler
from sklearn.linear_model import Ridge
from sklearn.pipeline import make_pipeline
from sklearn.compose import ColumnTransformer
from sklearn.ensemble import ExtraTreesRegressor
train=pd.read_csv('/app/data/train.csv'); val=pd.read_csv('/app/data/validation.csv')
ALL=pd.concat([train,val],ignore_index=True); COND=cond_ids(ALL); y=ALL['scaled_sound_pressure_level'].values
def fe(df):
f=df['frequency'].astype(float); a=df['attack_angle'].astype(float)
c=df['chord_length'].astype(float); v=df['free_stream_velocity'].astype(float)
t=df['suction_side_displacement_thickness'].astype(float)
return pd.DataFrame(dict(logf=np.log10(f), a=a, logc=np.log10(c), v=v, logt=np.log10(t),
St=np.log10(f*t/v), logfc=np.log10(f*c)), index=df.index)
def pr(n,m): print('%-26s rmse=%.3f mae=%.3f p90=%.3f p95=%.3f max=%.3f'%(n,m['rmse'],m['mae'],m['p90'],m['p95'],m['mx']))
# spline model: splines on St and logf, linear on level vars + interactions
def fe_spline(df):
X=fe(df); return X
def mk_spline(nknots=6, deg=3, alpha=1.0):
spline_cols=['St','logf','a','logt']
lin_cols=['logc','v','logt','a','logfc','St']
ct=ColumnTransformer([
('sp', SplineTransformer(n_knots=nknots, degree=deg, include_bias=False), spline_cols),
('lin', StandardScaler(), lin_cols)])
return make_pipeline(ct, StandardScaler(with_mean=False), Ridge(alpha=alpha))
for nk in [6,8,10]:
for al in [0.5,2.0]:
m,_=grouped_eval(lambda nk=nk,al=al:mk_spline(nk,3,al), fe_spline, n_splits=10)
pr('spline nk=%d a=%.1f'%(nk,al), m)
# ET ref + blend
_,oofET=grouped_eval(lambda:ExtraTreesRegressor(n_estimators=600,n_jobs=-1,random_state=0), fe, n_splits=10)
_,oofSP=grouped_eval(lambda:mk_spline(8,3,1.0), fe_spline, n_splits=10)
pr('ET', metrics(y,oofET,COND))
pr('spline8', metrics(y,oofSP,COND))
for w in [0.2,0.3,0.4]:
pr('blend %.1f*SP'%w, metrics(y,(1-w)*oofET+w*oofSP,COND))
np.save('/tmp/oofET.npy',oofET); np.save('/tmp/oofSP.npy',oofSP)
"total conditions: 82 rows: 1157 spline nk=6 a=0.5 rmse=3.900 mae=2.925 p90=5.396 p95=7.615 max=8.515 spline nk=6 a=2.0 rmse=3.903 mae=2.954 p90=5.506 p95=7.447 max=8.492 spline nk=8 a=0.5 rmse=3.872 mae=2.898 p90=5.373 p95=7.274 max=8.649 spline nk=8 a=2.0 rmse=3.889 mae=2.933 p90=5.340 p95=7.271 max=8.598 spline nk=10 a=0.5 rmse=9.836 mae=3.918 p90=5.632 p95=7.879 max=81.928 spline nk=10 a=2.0 rmse=7.486 mae=3.658 p90=5.784 p95=7.816 max=57.809 ET rmse=2.122 mae=1.588 p90=2.913 p95=3.701 max=4.738 spline8 rmse=3.881 mae=2.915 p90=5.383 p95=7.269 max=8.626 blend 0.2*SP rmse=2.193 mae=1.639 p90=2.977 p95=3.788 max=4.421 blend 0.3*SP rmse=2.302 mae=1.724 p90=3.103 p95=3.817 max=4.797 blend 0.4*SP rmse=2.453 mae=1.841 p90=3.505 p95=3.741 max=5.201
cd /app && timeout 60 python3 -c "
import sys; sys.path.insert(0,'/tmp')
from evalharness import cond_ids
import numpy as np, pandas as pd
test=pd.read_csv('/app/data/test_features.csv')
print('placeholder test rows:', len(test))
c=cond_ids(test)
from collections import Counter
cc=Counter(c)
print('n conditions in placeholder:', len(cc))
for k,v in sorted(cc.items()): print(' ',k,v)
"placeholder test rows: 40 n conditions in placeholder: 15 (0.0254, 39.6, 0.0) 4 (0.0254, 39.6, 4.8) 3 (0.0254, 39.6, 17.4) 3 (0.0254, 71.3, 0.0) 1 (0.0254, 71.3, 4.8) 3 (0.0508, 55.5, 0.0) 6 (0.0508, 55.5, 8.4) 2 (0.1016, 39.6, 12.3) 3 (0.1016, 55.5, 6.7) 3 (0.1016, 55.5, 12.3) 1 (0.1016, 71.3, 3.3) 2 (0.1524, 31.7, 9.9) 1 (0.1524, 39.6, 0.0) 4 (0.2286, 31.7, 4.0) 1 (0.3048, 31.7, 0.0) 3
cd /app && timeout 290 python3 -c "
import sys; sys.path.insert(0,'/tmp')
from gcv import grouped_eval
from evalharness import cond_ids, metrics
import numpy as np, pandas as pd
from sklearn.preprocessing import StandardScaler, PolynomialFeatures
from sklearn.linear_model import Ridge
from sklearn.pipeline import make_pipeline
from sklearn.ensemble import ExtraTreesRegressor
from sklearn.base import BaseEstimator, RegressorMixin
train=pd.read_csv('/app/data/train.csv'); val=pd.read_csv('/app/data/validation.csv')
ALL=pd.concat([train,val],ignore_index=True); COND=cond_ids(ALL); y=ALL['scaled_sound_pressure_level'].values
def fe(df):
f=df['frequency'].astype(float); a=df['attack_angle'].astype(float)
c=df['chord_length'].astype(float); v=df['free_stream_velocity'].astype(float)
t=df['suction_side_displacement_thickness'].astype(float)
return pd.DataFrame(dict(logf=np.log10(f), a=a, logc=np.log10(c), v=v, logt=np.log10(t),
St=np.log10(f*t/v), logfc=np.log10(f*c), logft=np.log10(f*t),
a2=a*a, asep=np.maximum(0,a-8.0), a_logt=a*np.log10(t), St2=np.log10(f*t/v)**2), index=df.index)
def pr(n,m): print('%-24s rmse=%.3f mae=%.3f p90=%.3f p95=%.3f max=%.3f'%(n,m['rmse'],m['mae'],m['p90'],m['p95'],m['mx']))
class BasePlusET(BaseEstimator,RegressorMixin):
def __init__(self, deg=2, alpha=5.0, n_est=500, etw=1.0):
self.deg=deg; self.alpha=alpha; self.n_est=n_est; self.etw=etw
def fit(self,X,y):
self.base=make_pipeline(PolynomialFeatures(self.deg,include_bias=False),StandardScaler(),Ridge(self.alpha))
self.base.fit(X,y); r=y-self.base.predict(X)
self.et=ExtraTreesRegressor(n_estimators=self.n_est,n_jobs=-1,random_state=0); self.et.fit(X,r)
return self
def predict(self,X): return self.base.predict(X)+self.etw*self.et.predict(X)
for deg in [2,3]:
for al in [2.0,10.0]:
m,_=grouped_eval(lambda deg=deg,al=al:BasePlusET(deg,al,500), fe, n_splits=10)
pr('base(deg%d,a%.0f)+ET'%(deg,al), m)
# compare pure ET
_,oofET=grouped_eval(lambda:ExtraTreesRegressor(n_estimators=500,n_jobs=-1,random_state=0), fe, n_splits=10)
pr('pure ET', metrics(y,oofET,COND))
"total conditions: 82 rows: 1157 base(deg2,a2)+ET rmse=2.270 mae=1.561 p90=2.978 p95=3.211 max=10.695 base(deg2,a10)+ET rmse=2.292 mae=1.559 p90=3.142 p95=3.335 max=10.820 base(deg3,a2)+ET rmse=2.267 mae=1.572 p90=3.063 p95=3.515 max=9.673 base(deg3,a10)+ET rmse=2.193 mae=1.527 p90=2.910 p95=3.133 max=9.413 pure ET rmse=2.107 mae=1.576 p90=2.743 p95=3.675 max=5.066
cd /app && timeout 200 python3 -c "
import sys; sys.path.insert(0,'/tmp')
from evalharness import cond_ids
import numpy as np, pandas as pd
train=pd.read_csv('/app/data/train.csv'); val=pd.read_csv('/app/data/validation.csv')
ALL=pd.concat([train,val],ignore_index=True); COND=cond_ids(ALL)
ALL['cid']=COND; y=ALL['scaled_sound_pressure_level'].values
ALL['logSt']=np.log10(ALL.frequency*ALL.suction_side_displacement_thickness/ALL.free_stream_velocity)
# per-condition mean SPL
ALL['cmean']=ALL.groupby('cid')['scaled_sound_pressure_level'].transform('mean')
ALL['resid']=ALL['scaled_sound_pressure_level']-ALL['cmean']
# Does resid collapse vs logSt? bin and look at std
ALL['stb']=pd.cut(ALL.logSt, np.linspace(-2,1,13))
g=ALL.groupby('stb',observed=True)['resid'].agg(['mean','std','count'])
print('residual (SPL - condition_mean) vs logSt:')
print(g.round(3).to_string())
print()
print('overall resid std:', round(ALL.resid.std(),3))
# correlation of condition mean with features
cond_df=ALL.groupby('cid').agg(cmean=('scaled_sound_pressure_level','mean'),
a=('attack_angle','mean'), logc=('chord_length', lambda s:np.log10(s.mean())),
v=('free_stream_velocity','mean'), logt=('suction_side_displacement_thickness', lambda s: np.log10(s.mean())))
print('n conditions:', len(cond_df), 'cmean std:', round(cond_df.cmean.std(),3))
"residual (SPL - condition_mean) vs logSt:
mean std count
stb
(-2.0, -1.75] -4.964 4.456 20
(-1.75, -1.5] -1.519 3.842 60
(-1.5, -1.25] 1.596 4.080 124
(-1.25, -1.0] 3.416 4.163 172
(-1.0, -0.75] 2.401 4.097 179
(-0.75, -0.5] 1.178 4.712 190
(-0.5, -0.25] -0.327 6.109 149
(-0.25, 0.0] -2.836 5.638 113
(0.0, 0.25] -3.608 5.192 70
(0.25, 0.5] -7.074 3.810 50
(0.5, 0.75] -9.034 3.130 22
(0.75, 1.0] -9.297 1.683 8
overall resid std: 5.611
n conditions: 82 cmean std: 3.874cd /app && timeout 290 python3 -c "
import sys; sys.path.insert(0,'/tmp')
from gcv import grouped_eval
from evalharness import cond_ids, metrics
import numpy as np, pandas as pd
from sklearn.ensemble import ExtraTreesRegressor
from sklearn.model_selection import GroupKFold
train=pd.read_csv('/app/data/train.csv'); val=pd.read_csv('/app/data/validation.csv')
ALL=pd.concat([train,val],ignore_index=True); COND=cond_ids(ALL); y=ALL['scaled_sound_pressure_level'].values
uniq={c:i for i,c in enumerate(sorted(set(COND)))}; GROUPS=np.array([uniq[c] for c in COND])
def denoise(df):
# group by inferred condition, replace condition feats with group mean
d=df.copy(); cid=cond_ids(d)
d['__c']=cid
for col in ['attack_angle','chord_length','free_stream_velocity','suction_side_displacement_thickness']:
d[col]=d.groupby('__c')[col].transform('mean')
return d.drop(columns='__c')
def fe(df):
f=df['frequency'].astype(float); a=df['attack_angle'].astype(float)
c=df['chord_length'].astype(float); v=df['free_stream_velocity'].astype(float)
t=df['suction_side_displacement_thickness'].astype(float)
return pd.DataFrame(dict(logf=np.log10(f), a=a, logc=np.log10(c), v=v, logt=np.log10(t),
St=np.log10(f*t/v), logfc=np.log10(f*c), logft=np.log10(f*t),
a2=a*a, asep=np.maximum(0,a-8.0), a_logt=a*np.log10(t), St2=np.log10(f*t/v)**2), index=df.index)
def pr(n,m): print('%-22s rmse=%.3f mae=%.3f p90=%.3f p95=%.3f max=%.3f'%(n,m['rmse'],m['mae'],m['p90'],m['p95'],m['mx']))
# CV with denoising applied per fold (both train and test)
def cv_denoise(mk,n_splits=10):
oof=np.full(len(ALL),np.nan); gkf=GroupKFold(n_splits)
for tr,te in gkf.split(ALL,y,GROUPS):
Xtr=fe(denoise(ALL.iloc[tr])); Xte=fe(denoise(ALL.iloc[te]))
m=mk(); m.fit(Xtr,y[tr]); oof[te]=m.predict(Xte)
return oof
oofD=cv_denoise(lambda:ExtraTreesRegressor(n_estimators=800,n_jobs=-1,random_state=0))
pr('ET denoised', metrics(y,oofD,COND))
_,oofR=grouped_eval(lambda:ExtraTreesRegressor(n_estimators=800,n_jobs=-1,random_state=0), fe, n_splits=10)
pr('ET raw', metrics(y,oofR,COND))
np.save('/tmp/oofD.npy',oofD)
"total conditions: 82 rows: 1157 ET denoised rmse=2.074 mae=1.558 p90=2.730 p95=3.566 max=4.942 ET raw rmse=2.107 mae=1.577 p90=2.795 p95=3.636 max=5.092
cd /app && timeout 290 python3 -c "
import sys; sys.path.insert(0,'/tmp')
from evalharness import cond_ids, metrics
import numpy as np, pandas as pd
from sklearn.ensemble import ExtraTreesRegressor
from sklearn.model_selection import GroupKFold
train=pd.read_csv('/app/data/train.csv'); val=pd.read_csv('/app/data/validation.csv')
ALL=pd.concat([train,val],ignore_index=True); COND=cond_ids(ALL); y=ALL['scaled_sound_pressure_level'].values
uniq={c:i for i,c in enumerate(sorted(set(COND)))}; GROUPS=np.array([uniq[c] for c in COND])
NOMF=np.array([200,250,315,400,500,630,800,1000,1250,1600,2000,2500,3150,4000,5000,6300,8000,10000,12500,16000,20000.])
logNOMF=np.log10(NOMF)
def snapf(f):
lf=np.log10(f.values); idx=np.argmin(np.abs(lf[:,None]-logNOMF[None,:]),axis=1); return NOMF[idx]
def denoise(df, snap=True):
d=df.copy(); d['__c']=cond_ids(d)
for col in ['attack_angle','chord_length','free_stream_velocity','suction_side_displacement_thickness']:
d[col]=d.groupby('__c')[col].transform('mean')
if snap: d['frequency']=snapf(d['frequency'])
return d.drop(columns='__c')
def fe(df):
f=df['frequency'].astype(float); a=df['attack_angle'].astype(float)
c=df['chord_length'].astype(float); v=df['free_stream_velocity'].astype(float)
t=df['suction_side_displacement_thickness'].astype(float)
return pd.DataFrame(dict(logf=np.log10(f), a=a, logc=np.log10(c), v=v, logt=np.log10(t),
St=np.log10(f*t/v), logfc=np.log10(f*c), logft=np.log10(f*t),
a2=a*a, asep=np.maximum(0,a-8.0), a_logt=a*np.log10(t), St2=np.log10(f*t/v)**2), index=df.index)
def pr(n,m): print('%-24s rmse=%.3f mae=%.3f p90=%.3f p95=%.3f max=%.3f'%(n,m['rmse'],m['mae'],m['p90'],m['p95'],m['mx']))
def cv(mk,snap,n_splits=10):
oof=np.full(len(ALL),np.nan); gkf=GroupKFold(n_splits)
for tr,te in gkf.split(ALL,y,GROUPS):
Xtr=fe(denoise(ALL.iloc[tr],snap)); Xte=fe(denoise(ALL.iloc[te],snap))
m=mk(); m.fit(Xtr,y[tr]); oof[te]=m.predict(Xte)
return oof
pr('denoise+snapF', metrics(y,cv(lambda:ExtraTreesRegressor(n_estimators=800,n_jobs=-1,random_state=0),True),COND))
pr('denoise only', metrics(y,cv(lambda:ExtraTreesRegressor(n_estimators=800,n_jobs=-1,random_state=0),False),COND))
"denoise+snapF rmse=2.065 mae=1.547 p90=2.682 p95=3.553 max=4.963 denoise only rmse=2.074 mae=1.558 p90=2.730 p95=3.566 max=4.942
cd /app && cat > /tmp/dn.py << 'EOF'
import sys; sys.path.insert(0,'/tmp')
from evalharness import cond_ids, metrics
import numpy as np, pandas as pd
from sklearn.model_selection import GroupKFold
train=pd.read_csv('/app/data/train.csv'); val=pd.read_csv('/app/data/validation.csv')
ALL=pd.concat([train,val],ignore_index=True); COND=cond_ids(ALL); y=ALL['scaled_sound_pressure_level'].values
uniq={c:i for i,c in enumerate(sorted(set(COND)))}; GROUPS=np.array([uniq[c] for c in COND])
NOMF=np.array([200,250,315,400,500,630,800,1000,1250,1600,2000,2500,3150,4000,5000,6300,8000,10000,12500,16000,20000.])
logNOMF=np.log10(NOMF)
def snapf(f):
lf=np.log10(np.asarray(f,float)); idx=np.argmin(np.abs(lf[:,None]-logNOMF[None,:]),axis=1); return NOMF[idx]
def denoise(df, snap=True):
d=df.copy(); d['__c']=cond_ids(d)
for col in ['attack_angle','chord_length','free_stream_velocity','suction_side_displacement_thickness']:
d[col]=d.groupby('__c')[col].transform('mean')
if snap: d['frequency']=snapf(d['frequency'].values)
return d.drop(columns='__c')
def fe(df):
f=df['frequency'].astype(float); a=df['attack_angle'].astype(float)
c=df['chord_length'].astype(float); v=df['free_stream_velocity'].astype(float)
t=df['suction_side_displacement_thickness'].astype(float)
return pd.DataFrame(dict(logf=np.log10(f), a=a, logc=np.log10(c), v=v, logt=np.log10(t),
St=np.log10(f*t/v), logfc=np.log10(f*c), logft=np.log10(f*t),
a2=a*a, asep=np.maximum(0,a-8.0), a_logt=a*np.log10(t), St2=np.log10(f*t/v)**2), index=df.index)
def pr(n,m): print('%-26s rmse=%.3f mae=%.3f p90=%.3f p95=%.3f max=%.3f'%(n,m['rmse'],m['mae'],m['p90'],m['p95'],m['mx']))
def cv_oof(mk, snap=True, n_splits=10):
oof=np.full(len(ALL),np.nan); gkf=GroupKFold(n_splits)
for tr,te in gkf.split(ALL,y,GROUPS):
Xtr=fe(denoise(ALL.iloc[tr],snap)); Xte=fe(denoise(ALL.iloc[te],snap))
m=mk(); m.fit(Xtr,y[tr]); oof[te]=m.predict(Xte)
return oof
EOF
echo donedone
cd /app && timeout 290 python3 -c "
import sys; sys.path.insert(0,'/tmp')
from dn import cv_oof, pr, metrics, y, COND
import numpy as np
from sklearn.ensemble import ExtraTreesRegressor, RandomForestRegressor
oofs={}
oofs['ET0']=cv_oof(lambda:ExtraTreesRegressor(n_estimators=1000,n_jobs=-1,random_state=0))
oofs['ET1']=cv_oof(lambda:ExtraTreesRegressor(n_estimators=1000,n_jobs=-1,random_state=1,max_features=0.7))
oofs['ET2']=cv_oof(lambda:ExtraTreesRegressor(n_estimators=1000,n_jobs=-1,random_state=2,min_samples_leaf=2))
oofs['RF']=cv_oof(lambda:RandomForestRegressor(n_estimators=800,n_jobs=-1,random_state=0,max_features=0.5))
for k in oofs: pr(k, metrics(y,oofs[k],COND))
print('--- blends ---')
pr('ET0+ET1+ET2', metrics(y,(oofs['ET0']+oofs['ET1']+oofs['ET2'])/3,COND))
pr('3ET+RF', metrics(y,(oofs['ET0']+oofs['ET1']+oofs['ET2']+oofs['RF'])/4,COND))
pr('ET0+RF', metrics(y,(oofs['ET0']+oofs['RF'])/2,COND))
import pickle; pickle.dump(oofs, open('/tmp/oofs.pkl','wb'))
"ET0 rmse=2.068 mae=1.549 p90=2.678 p95=3.590 max=4.960 ET1 rmse=2.079 mae=1.554 p90=2.827 p95=3.795 max=5.006 ET2 rmse=2.121 mae=1.592 p90=2.766 p95=3.813 max=4.995 RF rmse=2.195 mae=1.638 p90=2.882 p95=4.139 max=4.966 --- blends --- ET0+ET1+ET2 rmse=2.085 mae=1.561 p90=2.756 p95=3.752 max=4.985 3ET+RF rmse=2.095 mae=1.565 p90=2.740 p95=3.849 max=4.978 ET0+RF rmse=2.107 mae=1.574 p90=2.769 p95=3.906 max=4.959
cd /app && timeout 120 python3 -c "
import sys; sys.path.insert(0,'/tmp')
from dn import cv_oof, metrics, y, COND, ALL
import numpy as np, pandas as pd, pickle
oofs=pickle.load(open('/tmp/oofs.pkl','rb')); oof=oofs['ET0']
df=pd.DataFrame({'c':COND,'e':oof-y,'ang':ALL.attack_angle.values})
g=df.groupby('c').agg(rmse=('e',lambda s:np.sqrt(np.mean(s**2))),bias=('e','mean'),n=('e','size'),
angmin=('ang','min'),angmax=('ang','max')).sort_values('rmse',ascending=False)
print(g.head(14).round(3).to_string())
print()
print('conditions>2.35:',(g.rmse>2.35).sum(),'/',len(g))
print('conditions>3.0:',(g.rmse>3.0).sum())
"rmse bias n angmin angmax c (0.1524, 39.6, 12.6) 4.960 3.655 16 12.565 12.635 (0.0508, 71.3, 19.7) 4.008 3.752 14 19.665 19.728 (0.0254, 71.3, 0.0) 3.962 0.055 10 -0.015 0.035 (0.2286, 71.3, 7.3) 3.724 -2.619 16 7.265 7.335 (0.0508, 31.7, 8.4) 3.617 3.173 11 8.366 8.435 (0.1016, 71.3, 15.6) 3.092 0.651 14 15.565 15.635 (0.0254, 39.6, 22.2) 3.063 -2.249 15 22.168 22.235 (0.1524, 71.3, 7.3) 2.760 1.188 16 7.165 7.235 (0.1016, 31.7, 12.3) 2.679 2.426 16 12.265 12.335 (0.0254, 39.6, 17.4) 2.673 -0.079 15 17.365 17.435 (0.1016, 71.3, 12.3) 2.669 -1.790 16 12.265 12.335 (0.1016, 55.5, 6.7) 2.628 -1.448 8 6.665 6.715 (0.1016, 39.6, 8.9) 2.587 -1.610 16 8.865 8.935 (0.0508, 55.5, 15.4) 2.582 -2.036 16 15.365 15.435 conditions>2.35: 20 / 82 conditions>3.0: 7
cd /app && timeout 200 python3 -c "
import sys; sys.path.insert(0,'/tmp')
from dn import ALL, COND, y, denoise, fe
import numpy as np, pandas as pd
from sklearn.model_selection import GroupKFold
from sklearn.ensemble import ExtraTreesRegressor, RandomForestRegressor, HistGradientBoostingRegressor
# Build condition-level dataset
dd=denoise(ALL, snap=True); dd['cid']=COND; dd['y']=y
clev=dd.groupby('cid').agg(a=('attack_angle','mean'),c=('chord_length','mean'),
v=('free_stream_velocity','mean'),t=('suction_side_displacement_thickness','mean'),
ymean=('y','mean')).reset_index()
clev['logc']=np.log10(clev.c); clev['logt']=np.log10(clev.t)
clev['Re']=np.log10(clev.v*clev.c); clev['a_logt']=clev.a*clev.logt
Xcols=['a','logc','v','logt','Re','a_logt']
Xc=clev[Xcols].values; yc=clev.ymean.values
gid=np.arange(len(clev))
gkf=GroupKFold(10)
for name,mk in [('ET',lambda:ExtraTreesRegressor(n_estimators=500,n_jobs=-1,random_state=0)),
('RF',lambda:RandomForestRegressor(n_estimators=500,n_jobs=-1,random_state=0)),
('HGB',lambda:HistGradientBoostingRegressor(max_iter=300,random_state=0))]:
oof=np.full(len(clev),np.nan)
for tr,te in gkf.split(Xc,yc,gid):
m=mk(); m.fit(Xc[tr],yc[tr]); oof[te]=m.predict(Xc[te])
err=oof-yc
print('%s level: RMSE=%.3f MAE=%.3f max|e|=%.3f'%(name, np.sqrt(np.mean(err**2)), np.mean(np.abs(err)), np.max(np.abs(err))))
"ET level: RMSE=1.619 MAE=1.250 max|e|=5.170 RF level: RMSE=2.058 MAE=1.546 max|e|=6.177 HGB level: RMSE=2.744 MAE=2.127 max|e|=8.457
cd /app && timeout 250 python3 -c "
import sys; sys.path.insert(0,'/tmp')
from dn import ALL, COND, y, metrics, pr, fe, GROUPS
import numpy as np, pandas as pd
from sklearn.model_selection import GroupKFold
from sklearn.ensemble import ExtraTreesRegressor
CH=np.array([0.0254,0.0508,0.1016,0.1524,0.2286,0.3048]); VE=np.array([31.7,39.6,55.5,71.3])
ANG=np.array([0,1.5,2.0,3.0,3.3,4.0,4.8,5.3,6.7,7.3,8.4,8.9,9.5,9.9,11.2,12.3,12.6,15.4,15.6,17.4,19.7,22.2])
NOMF=np.array([200,250,315,400,500,630,800,1000,1250,1600,2000,2500,3150,4000,5000,6300,8000,10000,12500,16000,20000.])
def snap(x,grid): x=np.asarray(x,float); return grid[np.argmin(np.abs(x[:,None]-grid[None,:]),axis=1)]
def denoise_grid(df):
d=df.copy()
d['chord_length']=snap(d.chord_length,CH); d['free_stream_velocity']=snap(d.free_stream_velocity,VE)
d['attack_angle']=snap(d.attack_angle,ANG); d['frequency']=snap(d.frequency,NOMF)
key=list(zip(d.chord_length,d.free_stream_velocity,d.attack_angle))
d['__k']=key; d['suction_side_displacement_thickness']=d.groupby('__k')['suction_side_displacement_thickness'].transform('mean')
return d.drop(columns='__k')
def cv(mk,dn,n=10):
oof=np.full(len(ALL),np.nan); gkf=GroupKFold(n)
for tr,te in gkf.split(ALL,y,GROUPS):
Xtr=fe(dn(ALL.iloc[tr])); Xte=fe(dn(ALL.iloc[te]))
m=mk(); m.fit(Xtr,y[tr]); oof[te]=m.predict(Xte)
return oof
pr('grid-snap denoise', metrics(y,cv(lambda:ExtraTreesRegressor(n_estimators=1000,n_jobs=-1,random_state=0),denoise_grid),COND))
"grid-snap denoise rmse=2.087 mae=1.561 p90=2.713 p95=3.587 max=5.009
cd /app && timeout 295 python3 -c "
import sys; sys.path.insert(0,'/tmp')
from dn import ALL, COND, y, metrics, pr, denoise, GROUPS
import numpy as np, pandas as pd
from sklearn.model_selection import GroupKFold
from sklearn.ensemble import ExtraTreesRegressor
def make_fe(cols):
def fe(df):
f=df['frequency'].astype(float); a=df['attack_angle'].astype(float)
c=df['chord_length'].astype(float); v=df['free_stream_velocity'].astype(float)
t=df['suction_side_displacement_thickness'].astype(float)
d=dict(logf=np.log10(f), a=a, logc=np.log10(c), v=v, logt=np.log10(t),
St=np.log10(f*t/v), logfc=np.log10(f*c), logft=np.log10(f*t),
a2=a*a, asep=np.maximum(0,a-8.0), a_logt=a*np.log10(t), St2=np.log10(f*t/v)**2,
Re=np.log10(v*c), aSt=a*np.log10(f*t/v))
return pd.DataFrame({k:d[k] for k in cols}, index=df.index)
return fe
def cv(mk,fe,n=10):
oof=np.full(len(ALL),np.nan); gkf=GroupKFold(n)
for tr,te in gkf.split(ALL,y,GROUPS):
Xtr=fe(denoise(ALL.iloc[tr])); Xte=fe(denoise(ALL.iloc[te]))
m=mk(); m.fit(Xtr,y[tr]); oof[te]=m.predict(Xte)
return oof
f8=['logf','a','logc','v','logt','St','logfc','logft']
sets={'f8':f8, 'f8+a2asep':f8+['a2','asep','a_logt','St2'], 'f8+Re':f8+['Re'], 'f8+aSt':f8+['aSt'], 'f9min':['logf','a','logc','v','logt','St','logfc','logft','a_logt']}
for name,cols in sets.items():
pr(name, metrics(y,cv(lambda:ExtraTreesRegressor(n_estimators=800,n_jobs=-1,random_state=0),make_fe(cols)),COND))
"f8 rmse=2.078 mae=1.554 p90=2.923 p95=3.569 max=4.855 f8+a2asep rmse=2.065 mae=1.547 p90=2.682 p95=3.553 max=4.963 f8+Re rmse=2.118 mae=1.575 p90=3.006 p95=3.557 max=5.178 f8+aSt rmse=2.122 mae=1.576 p90=2.827 p95=3.694 max=4.730 f9min rmse=2.078 mae=1.564 p90=2.692 p95=3.607 max=4.927
cd /app && timeout 295 python3 -c "
import sys; sys.path.insert(0,'/tmp')
from dn import ALL, COND, y, metrics, pr, denoise, fe, GROUPS
from evalharness import cond_ids
import numpy as np, pandas as pd
from sklearn.model_selection import GroupKFold
from sklearn.ensemble import ExtraTreesRegressor
# multi-seed averaged ET in CV
def cv_multiseed(seeds, n=10):
gkf=GroupKFold(n); oof=np.zeros(len(ALL))
for tr,te in gkf.split(ALL,y,GROUPS):
Xtr=fe(denoise(ALL.iloc[tr])); Xte=fe(denoise(ALL.iloc[te]))
acc=np.zeros(len(te))
for s in seeds:
m=ExtraTreesRegressor(n_estimators=600,n_jobs=-1,random_state=s); m.fit(Xtr,y[tr]); acc+=m.predict(Xte)
oof[te]=acc/len(seeds)
return oof
pr('ET 5-seed avg', metrics(y,cv_multiseed([0,1,2,3,4]),COND))
# realistic: train on train.csv denoised, predict val.csv denoised
train=pd.read_csv('/app/data/train.csv'); val=pd.read_csv('/app/data/validation.csv')
Xtr=fe(denoise(train)); Xval=fe(denoise(val))
acc=np.zeros(len(val))
for s in range(5):
m=ExtraTreesRegressor(n_estimators=600,n_jobs=-1,random_state=s); m.fit(Xtr,train['scaled_sound_pressure_level'].values); acc+=m.predict(Xval)
acc/=5
pr('REAL train->val(15cond)', metrics(val['scaled_sound_pressure_level'].values, acc, cond_ids(val)))
"ET 5-seed avg rmse=2.075 mae=1.554 p90=2.718 p95=3.615 max=4.969 REAL train->val(15cond) rmse=2.055 mae=1.557 p90=2.815 p95=3.182 max=3.960
cd /app && timeout 250 python3 -c "
import sys; sys.path.insert(0,'/tmp'); import warnings; warnings.filterwarnings('ignore')
from dn import ALL, COND, y, denoise
import numpy as np, pandas as pd
from sklearn.model_selection import LeaveOneGroupOut
from sklearn.svm import SVR
from sklearn.kernel_ridge import KernelRidge
from sklearn.gaussian_process import GaussianProcessRegressor
from sklearn.gaussian_process.kernels import Matern, WhiteKernel, ConstantKernel as C
from sklearn.preprocessing import StandardScaler, PolynomialFeatures
from sklearn.pipeline import make_pipeline
from sklearn.linear_model import Ridge
from sklearn.ensemble import ExtraTreesRegressor
dd=denoise(ALL); dd['cid']=COND; dd['y']=y
clev=dd.groupby('cid').agg(a=('attack_angle','mean'),c=('chord_length','mean'),
v=('free_stream_velocity','mean'),t=('suction_side_displacement_thickness','mean'),
ymean=('y','mean')).reset_index()
clev['logc']=np.log10(clev.c); clev['logt']=np.log10(clev.t); clev['Re']=np.log10(clev.v*clev.c)
X=clev[['a','logc','v','logt','Re']].values; yc=clev.ymean.values; gid=np.arange(len(clev))
logo=LeaveOneGroupOut()
def ev(name,mk):
oof=np.full(len(clev),np.nan)
for tr,te in logo.split(X,yc,gid):
m=mk(); m.fit(X[tr],yc[tr]); oof[te]=m.predict(X[te])
e=oof-yc; print('%-20s RMSE=%.3f MAE=%.3f max=%.3f'%(name,np.sqrt(np.mean(e**2)),np.mean(np.abs(e)),np.max(np.abs(e))))
return oof
ev('ET', lambda:ExtraTreesRegressor(n_estimators=500,n_jobs=-1,random_state=0))
for C_ in [20,50]:
for g in ['scale',0.2]:
ev('SVR C%s g%s'%(C_,g), lambda C_=C_,g=g:make_pipeline(StandardScaler(),SVR(C=C_,gamma=g,epsilon=0.1)))
ev('KRR', lambda:make_pipeline(StandardScaler(),KernelRidge(kernel='rbf',alpha=0.1,gamma=0.2)))
ev('poly2ridge', lambda:make_pipeline(PolynomialFeatures(2),StandardScaler(),Ridge(1.0)))
ev('GP', lambda:make_pipeline(StandardScaler(),GaussianProcessRegressor(kernel=C(30)*Matern(length_scale=2.0,nu=2.5)+WhiteKernel(1.0),optimizer=None,normalize_y=True)))
"ET RMSE=1.629 MAE=1.275 max=4.927 SVR C20 gscale RMSE=1.995 MAE=1.594 max=5.507 SVR C20 g0.2 RMSE=1.995 MAE=1.594 max=5.507 SVR C50 gscale RMSE=2.205 MAE=1.792 max=6.815 SVR C50 g0.2 RMSE=2.205 MAE=1.792 max=6.815 KRR RMSE=6.817 MAE=4.767 max=29.656 poly2ridge RMSE=2.549 MAE=1.983 max=7.962 GP RMSE=2.052 MAE=1.640 max=5.771
cd /app && timeout 250 python3 -c "
import sys; sys.path.insert(0,'/tmp')
from dn import ALL, COND, y, metrics, pr, denoise, fe, GROUPS
import numpy as np, pandas as pd
from sklearn.model_selection import GroupKFold
from sklearn.ensemble import ExtraTreesRegressor
# thickness distribution
dd=denoise(ALL); dd['cid']=COND
ct=dd.groupby('cid')['suction_side_displacement_thickness'].mean().sort_values()
print('thickness range across conditions: %.5f .. %.5f'%(ct.min(),ct.max()))
print('worst cond (0.1524,39.6,12.6) thickness:', round(ct.get((0.1524,39.6,12.6),-1),5))
print('thickness 95th pct:', round(ct.quantile(0.95),5), ' n cond > 0.05:', (ct>0.05).sum())
print()
def cv(mk,n=10):
oof=np.full(len(ALL),np.nan); gkf=GroupKFold(n)
for tr,te in gkf.split(ALL,y,GROUPS):
Xtr=fe(denoise(ALL.iloc[tr])); Xte=fe(denoise(ALL.iloc[te]))
m=mk(); m.fit(Xtr,y[tr]); oof[te]=m.predict(Xte)
return oof
pr('ET msl1 (base)', metrics(y,cv(lambda:ExtraTreesRegressor(n_estimators=800,n_jobs=-1,random_state=0)),COND))
pr('ET bootstrap', metrics(y,cv(lambda:ExtraTreesRegressor(n_estimators=800,n_jobs=-1,random_state=0,bootstrap=True,max_samples=0.8)),COND))
pr('ET msl2', metrics(y,cv(lambda:ExtraTreesRegressor(n_estimators=800,n_jobs=-1,random_state=0,min_samples_leaf=2)),COND))
"thickness range across conditions: 0.00040 .. 0.05843 worst cond (0.1524,39.6,12.6) thickness: 0.05843 thickness 95th pct: 0.04183 n cond > 0.05: 2 ET msl1 (base) rmse=2.065 mae=1.547 p90=2.682 p95=3.553 max=4.963 ET bootstrap rmse=2.174 mae=1.621 p90=3.007 p95=4.054 max=5.142 ET msl2 rmse=2.117 mae=1.590 p90=2.786 p95=3.860 max=4.993
cd /app && timeout 290 python3 -c "
import sys; sys.path.insert(0,'/tmp')
from dn import ALL, COND, y, metrics, pr, denoise, fe, GROUPS
from evalharness import cond_ids
import numpy as np, pandas as pd
from sklearn.model_selection import GroupKFold
from sklearn.ensemble import ExtraTreesRegressor
SEEDS=[0,1,2,3,4,5]; NEST=500
def cv():
oof=np.zeros(len(ALL)); gkf=GroupKFold(10)
for tr,te in gkf.split(ALL,y,GROUPS):
Xtr=fe(denoise(ALL.iloc[tr])); Xte=fe(denoise(ALL.iloc[te])); acc=np.zeros(len(te))
for s in SEEDS:
m=ExtraTreesRegressor(n_estimators=NEST,n_jobs=-1,random_state=s); m.fit(Xtr,y[tr]); acc+=m.predict(Xte)
oof[te]=acc/len(SEEDS)
return oof
pr('FINAL 6seed x500 CV(82cond)', metrics(y,cv(),COND))
# realistic train->val
train=pd.read_csv('/app/data/train.csv'); val=pd.read_csv('/app/data/validation.csv')
Xtr=fe(denoise(train)); Xv=fe(denoise(val)); acc=np.zeros(len(val))
for s in SEEDS:
m=ExtraTreesRegressor(n_estimators=NEST,n_jobs=-1,random_state=s); m.fit(Xtr,train['scaled_sound_pressure_level'].values); acc+=m.predict(Xv)
pr('FINAL train->val(15cond)', metrics(val['scaled_sound_pressure_level'].values, acc/len(SEEDS), cond_ids(val)))
"FINAL 6seed x500 CV(82cond) rmse=2.076 mae=1.554 p90=2.732 p95=3.630 max=4.971 FINAL train->val(15cond) rmse=2.054 mae=1.556 p90=2.804 p95=3.172 max=3.962
command: create
path: /app/train_and_predict.py
file_text: #!/usr/bin/env python3
"""Airfoil Self-Noise surrogate.
Strategy (developed against a grouped, condition-held-out validation that
mirrors the hidden split):
1. The public features are a *jittered* version of the discrete NASA/UCI
airfoil experiment grid. Within one aerodynamic condition
``(attack_angle, chord_length, free_stream_velocity)`` the angle, chord,
velocity and suction-side displacement thickness are physically constant;
only the 1/3-octave ``frequency`` genuinely varies. The per-row jitter is
therefore pure input noise. We remove it by grouping rows into their
nominal condition and replacing the condition-constant features with the
group mean, and by snapping ``frequency`` to its nearest 1/3-octave band.
This denoising consistently lowered every held-out metric.
2. Physics-motivated features (log frequency / thickness / chord, the
Strouhal number ``f*delta/U`` that sets the spectral-peak location, and a
few interaction / separation terms) are fed to an ExtraTrees regressor.
Among the available sklearn models ExtraTrees generalised best across
entirely unseen conditions; smooth global models (SVR/GP/spline-ridge) and
boosted trees were weaker, and polynomial bases extrapolated catastrophically.
The model is trained on all labelled data (train + validation) so the hidden
conditions are as interior as possible to the training range.
"""
from pathlib import Path
import numpy as np
import pandas as pd
from sklearn.ensemble import ExtraTreesRegressor
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",
]
CONDITION_FEATURES = [
"attack_angle",
"chord_length",
"free_stream_velocity",
"suction_side_displacement_thickness",
]
# Nominal experiment grids (used only to *group* jittered rows into their
# underlying condition; the denoised feature value is the group mean of the
# actual values, never the nominal value, so genuinely new settings are fine).
NOMINAL_CHORD = np.array([0.0254, 0.0508, 0.1016, 0.1524, 0.2286, 0.3048])
NOMINAL_VELOCITY = np.array([31.7, 39.6, 55.5, 71.3])
NOMINAL_ANGLE = np.array(
[0.0, 1.5, 2.0, 3.0, 3.3, 4.0, 4.8, 5.3, 6.7, 7.3, 8.4, 8.9,
9.5, 9.9, 11.2, 12.3, 12.6, 15.4, 15.6, 17.4, 19.7, 22.2]
)
NOMINAL_FREQ = np.array(
[200.0, 250, 315, 400, 500, 630, 800, 1000, 1250, 1600, 2000, 2500,
3150, 4000, 5000, 6300, 8000, 10000, 12500, 16000, 20000]
)
LOG_NOMINAL_FREQ = np.log10(NOMINAL_FREQ)
def _snap(values: np.ndarray, grid: np.ndarray) -> np.ndarray:
values = np.asarray(values, dtype=float)
idx = np.argmin(np.abs(values[:, None] - grid[None, :]), axis=1)
return grid[idx]
def _snap_frequency(freq: np.ndarray) -> np.ndarray:
logf = np.log10(np.asarray(freq, dtype=float))
idx = np.argmin(np.abs(logf[:, None] - LOG_NOMINAL_FREQ[None, :]), axis=1)
return NOMINAL_FREQ[idx]
def _condition_key(frame: pd.DataFrame) -> list:
"""Group jittered rows into their underlying aerodynamic condition."""
chord = _snap(frame["chord_length"].to_numpy(), NOMINAL_CHORD)
velocity = _snap(frame["free_stream_velocity"].to_numpy(), NOMINAL_VELOCITY)
angle = _snap(frame["attack_angle"].to_numpy(), NOMINAL_ANGLE)
return list(zip(np.round(chord, 4), np.round(velocity, 1), np.round(angle, 2)))
def denoise(frame: pd.DataFrame) -> pd.DataFrame:
"""Remove per-row jitter: condition-constant features -> group mean;
frequency -> nearest 1/3-octave band."""
out = frame.copy()
out["__cond__"] = _condition_key(out)
for col in CONDITION_FEATURES:
out[col] = out.groupby("__cond__")[col].transform("mean")
out["frequency"] = _snap_frequency(out["frequency"].to_numpy())
…[truncated]Existing file was successfully overwritten at /app/train_and_predict.py
cd /app && rm -f output/predictions.csv && timeout 200 python3 train_and_predict.py && echo "=== EXIT OK ===" && echo "--- head ---" && head -5 output/predictions.csv && echo "--- rows (incl header) ---" && wc -l output/predictions.csv && echo "--- test_features rows ---" && wc -l data/test_features.csv
=== EXIT OK === --- head --- sample_id,predicted_scaled_sound_pressure_level af_val_0000,115.94460818293676 af_val_0001,124.1615657179699 af_val_0002,133.6362469839564 af_val_0003,124.03263614950433 --- rows (incl header) --- 41 output/predictions.csv --- test_features rows --- 41 data/test_features.csv
cd /app && timeout 60 python3 -c "
import sys; sys.path.insert(0,'/tmp')
from evalharness import cond_ids, metrics
import numpy as np, pandas as pd
pred=pd.read_csv('output/predictions.csv')
val=pd.read_csv('data/validation.csv'); test=pd.read_csv('data/test_features.csv')
# schema checks
assert list(pred.columns)==['sample_id','predicted_scaled_sound_pressure_level'], pred.columns
assert len(pred)==len(test), (len(pred),len(test))
assert set(pred.sample_id)==set(test.sample_id.astype(str)), 'id mismatch'
assert pred.predicted_scaled_sound_pressure_level.notna().all()
assert np.isfinite(pred.predicted_scaled_sound_pressure_level).all()
assert pred.predicted_scaled_sound_pressure_level.nunique()>5, 'not constant'
print('ALL SCHEMA CHECKS PASSED. n=',len(pred))
print('pred range: %.2f .. %.2f std=%.2f'%(pred.predicted_scaled_sound_pressure_level.min(),
pred.predicted_scaled_sound_pressure_level.max(), pred.predicted_scaled_sound_pressure_level.std()))
# metrics vs known val labels
m=pred.merge(val[['sample_id','scaled_sound_pressure_level']],on='sample_id')
assert len(m)==len(pred)
cond=cond_ids(m.rename(columns={}).assign()) # need feature cols
mm=pred.merge(val,on='sample_id')
met=metrics(mm['scaled_sound_pressure_level'], mm['predicted_scaled_sound_pressure_level'], cond_ids(mm))
print('On placeholder (40 rows, subset of val conditions):')
print(' rmse=%.3f mae=%.3f p90=%.3f p95=%.3f max=%.3f ncond=%d'%(met['rmse'],met['mae'],met['p90'],met['p95'],met['mx'],met['ncond']))
"ALL SCHEMA CHECKS PASSED. n= 40
pred range: 114.76 .. 137.90 std=6.60
[stderr]
Traceback (most recent call last):
File "/usr/local/lib/python3.12/dist-packages/pandas/core/indexes/base.py", line 3805, in get_loc
return self._engine.get_loc(casted_key)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "index.pyx", line 167, in pandas._libs.index.IndexEngine.get_loc
File "index.pyx", line 196, in pandas._libs.index.IndexEngine.get_loc
File "pandas/_libs/hashtable_class_helper.pxi", line 7081, in pandas._libs.hashtable.PyObjectHashTable.get_item
File "pandas/_libs/hashtable_class_helper.pxi", line 7089, in pandas._libs.hashtable.PyObjectHashTable.get_item
KeyError: 'chord_length'
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "<string>", line 20, in <module>
File "/tmp/evalharness.py", line 10, in cond_ids
c = nn(df['chord_length'].values, chords)
~~^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/dist-packages/pandas/core/frame.py", line 4102, in __getitem__
indexer = self.columns.get_loc(key)
^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/dist-packages/pandas/core/indexes/base.py", line 3812, in get_loc
raise KeyError(key) from err
KeyError: 'chord_length'cd /app && timeout 60 python3 -c "
import sys; sys.path.insert(0,'/tmp')
from evalharness import cond_ids, metrics
import numpy as np, pandas as pd
pred=pd.read_csv('output/predictions.csv')
val=pd.read_csv('data/validation.csv')
mm=pred.merge(val,on='sample_id')
assert len(mm)==len(pred)
met=metrics(mm['scaled_sound_pressure_level'], mm['predicted_scaled_sound_pressure_level'], cond_ids(mm))
print('On placeholder (40 rows, %d conditions):'%met['ncond'])
print(' rmse=%.3f mae=%.3f p90=%.3f p95=%.3f max=%.3f'%(met['rmse'],met['mae'],met['p90'],met['p95'],met['mx']))
"On placeholder (40 rows, 15 conditions): rmse=0.294 mae=0.214 p90=0.347 p95=0.440 max=0.548
cd /app && timeout 295 python3 -c "
import sys; sys.path.insert(0,'/tmp')
from dn import ALL, COND, y, metrics, denoise, fe, GROUPS
import numpy as np
from sklearn.model_selection import GroupKFold
from sklearn.ensemble import ExtraTreesRegressor
def cv(seeds, nest):
oof=np.zeros(len(ALL)); gkf=GroupKFold(10)
for tr,te in gkf.split(ALL,y,GROUPS):
Xtr=fe(denoise(ALL.iloc[tr])); Xte=fe(denoise(ALL.iloc[te])); acc=np.zeros(len(te))
for s in seeds:
m=ExtraTreesRegressor(n_estimators=nest,max_features=1.0,n_jobs=-1,random_state=s)
m.fit(Xtr,y[tr]); acc+=m.predict(Xte)
oof[te]=acc/len(seeds)
return metrics(y,oof,COND)
for label,seeds,nest in [('single s0 x1200',[0],1200),('3seed x600',[0,1,2],600),('4seed x400',[0,1,2,3],400)]:
m=cv(seeds,nest)
print('%-18s rmse=%.3f mae=%.3f p90=%.3f p95=%.3f max=%.3f [%d cond>2.35]'%(
label,m['rmse'],m['mae'],m['p90'],m['p95'],m['mx'],-1))
"single s0 x1200 rmse=2.070 mae=1.550 p90=2.679 p95=3.577 max=4.959 [-1 cond>2.35] 3seed x600 rmse=2.074 mae=1.553 p90=2.704 p95=3.596 max=4.971 [-1 cond>2.35] 4seed x400 rmse=2.074 mae=1.552 p90=2.703 p95=3.616 max=4.960 [-1 cond>2.35]
command: str_replace
path: /app/train_and_predict.py
old_str: def engineer(frame: pd.DataFrame) -> pd.DataFrame:
f = frame["frequency"].astype(float)
a = frame["attack_angle"].astype(float)
c = frame["chord_length"].astype(float)
v = frame["free_stream_velocity"].astype(float)
t = frame["suction_side_displacement_thickness"].astype(float)
new_str: def engineer(frame: pd.DataFrame) -> pd.DataFrame:
# Positive, finite guards so log10 / ratios never produce NaN/inf even on
# unexpected hidden inputs (the clean benchmark data never hits these).
f = frame["frequency"].astype(float).clip(lower=1e-6)
a = frame["attack_angle"].astype(float)
c = frame["chord_length"].astype(float).clip(lower=1e-9)
v = frame["free_stream_velocity"].astype(float).clip(lower=1e-6)
t = frame["suction_side_displacement_thickness"].astype(float).clip(lower=1e-9)The file /app/train_and_predict.py has been edited successfully.
command: str_replace
path: /app/train_and_predict.py
old_str: X_train = engineer(denoise(labeled[FEATURES]))
y_train = labeled[TARGET].to_numpy(dtype=float)
X_eval = engineer(denoise(evaluation[FEATURES]))
model = build_model()
new_str: X_train = engineer(denoise(labeled[FEATURES]))
y_train = labeled[TARGET].to_numpy(dtype=float)
X_eval = engineer(denoise(evaluation[FEATURES]))
# Impute any non-finite engineered values with training-column means so the
# estimator never receives NaN/inf (clean benchmark data is unaffected).
X_train = X_train.replace([np.inf, -np.inf], np.nan)
X_eval = X_eval.replace([np.inf, -np.inf], np.nan)
col_means = X_train.mean(numeric_only=True)
X_train = X_train.fillna(col_means)
X_eval = X_eval.fillna(col_means)
model = build_model()The file /app/train_and_predict.py has been edited successfully.
cd /app && rm -f output/predictions.csv && timeout 200 python3 train_and_predict.py && cp output/predictions.csv /tmp/run1.csv && timeout 200 python3 train_and_predict.py && cp output/predictions.csv /tmp/run2.csv && echo "=== determinism check ===" && if diff -q /tmp/run1.csv /tmp/run2.csv >/dev/null; then echo "IDENTICAL across runs (deterministic)"; else echo "DIFFERS!"; fi && echo "--- sample ---" && head -3 output/predictions.csv && python3 -c "
import pandas as pd, numpy as np
p=pd.read_csv('output/predictions.csv')
print('rows',len(p),'finite',bool(np.isfinite(p.iloc[:,1]).all()),'nunique',p.iloc[:,1].nunique())
"=== determinism check === DIFFERS! --- sample --- sample_id,predicted_scaled_sound_pressure_level af_val_0000,115.94460818293676 af_val_0001,124.1615657179699 rows 40 finite True nunique 40
cd /app && python3 -c "
import pandas as pd, numpy as np
a=pd.read_csv('/tmp/run1.csv'); b=pd.read_csv('/tmp/run2.csv')
d=np.abs(a.iloc[:,1].values-b.iloc[:,1].values)
print('max abs diff between runs: %.3e'%d.max())
print('mean abs diff: %.3e'%d.mean())
"max abs diff between runs: 5.684e-14 mean abs diff: 2.842e-15
cd /app && timeout 295 python3 -c "
import sys; sys.path.insert(0,'/tmp')
from dn import ALL, COND, y, metrics, denoise, fe
import numpy as np, pandas as pd
from sklearn.ensemble import ExtraTreesRegressor
conds=sorted(set(COND)); cond_arr=np.array(COND,dtype=object)
rng=np.random.default_rng(42)
import warnings; warnings.filterwarnings('ignore')
res=[]
for rep in range(25):
rng.shuffle(conds)
hidden=set(conds[:15]); mask=np.array([c in hidden for c in COND])
tr=ALL[~mask]; te=ALL[mask]
Xtr=fe(denoise(tr)); Xte=fe(denoise(te))
m=ExtraTreesRegressor(n_estimators=300,max_features=1.0,n_jobs=-1,random_state=0)
m.fit(Xtr, y[~mask]); p=m.predict(Xte)
met=metrics(y[mask], p, list(cond_arr[mask]))
res.append([met['rmse'],met['mae'],met['p90'],met['p95'],met['mx']])
res=np.array(res)
names=['rmse','mae','p90','p95','max']; tgt=[2.25,1.55,2.35,4.75,5.60]
print('Monte Carlo over 25 random 15-condition hidden draws (train on other 67):')
for i,(nm,tg) in enumerate(zip(names,tgt)):
col=res[:,i]; print(' %-4s mean=%.3f median=%.3f p25=%.3f p75=%.3f frac<=target=%.0f%%'%(
nm, col.mean(), np.median(col), np.percentile(col,25), np.percentile(col,75), 100*np.mean(col<=tg)))
allpass=np.all(res<=np.array(tgt),axis=1)
print(' ALL 5 metrics pass simultaneously: %.0f%% of draws'%(100*allpass.mean()))
"[stderr]
Traceback (most recent call last):
File "/usr/local/lib/python3.12/dist-packages/pandas/core/groupby/groupby.py", line 1824, in apply
result = self._python_apply_general(f, self._selected_obj)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/dist-packages/pandas/core/groupby/groupby.py", line 1885, in _python_apply_general
values, mutated = self._grouper.apply_groupwise(f, data, self.axis)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/dist-packages/pandas/core/groupby/ops.py", line 902, in apply_groupwise
splitter = self._get_splitter(data, axis=axis)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/dist-packages/pandas/core/groupby/ops.py", line 629, in _get_splitter
ids, _, ngroups = self.group_info
^^^^^^^^^^^^^^^
File "properties.pyx", line 36, in pandas._libs.properties.CachedProperty.__get__
File "/usr/local/lib/python3.12/dist-packages/pandas/core/groupby/ops.py", line 745, in group_info
comp_ids, obs_group_ids = self._get_compressed_codes()
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/dist-packages/pandas/core/groupby/ops.py", line 769, in _get_compressed_codes
return ping.codes, np.arange(len(ping._group_index), dtype=np.intp)
^^^^^^^^^^
File "/usr/local/lib/python3.12/dist-packages/pandas/core/groupby/grouper.py", line 691, in codes
return self._codes_and_uniques[0]
^^^^^^^^^^^^^^^^^^^^^^^
File "properties.pyx", line 36, in pandas._libs.properties.CachedProperty.__get__
File "/usr/local/lib/python3.12/dist-packages/pandas/core/groupby/grouper.py", line 835, in _codes_and_uniques
codes, uniques = algorithms.factorize( # type: ignore[assignment]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/dist-packages/pandas/core/algorithms.py", line 795, in factorize
codes, uniques = factorize_array(
^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/dist-packages/pandas/core/algorithms.py", line 595, in factorize_array
uniques, codes = table.factorize(
^^^^^^^^^^^^^^^^
File "pandas/_libs/hashtable_class_helper.pxi", line 7281, in pandas._libs.hashtable.PyObjectHashTable.factorize
File "pandas/_libs/hashtable_class_helper.pxi", line 7195, in pandas._libs.hashtable.PyObjectHashTable._unique
TypeError: unhashable type: 'numpy.ndarray'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<string>", line 17, in <module>
File "/tmp/evalharness.py", line 21, in metrics
crmse = df.groupby('c')['e'].apply(lambda s: np.sqrt(np.mean(s**2)))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/dist-packages/pandas/core/groupby/generic.py", line 230, in apply
return super().apply(func, *args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/dist-packages/pandas/core/groupby/groupby.py", line 1846, in apply
return self._python_apply_general(f, self._obj_with_exclusions)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/dist-packages/pandas/core/groupby/groupby.py", line 1885, in _python_apply_general
values, mutated = self._grouper.apply_groupwise(f, data, self.axis)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/dist-packages/pandas/core/groupby/ops.py", line 902, in apply_groupwise
splitter = self._get_splitter(data, axis=axis)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/dist-packages/pandas/core/groupby/ops.py", line 629, in _get_splitter
ids, _, ngroups = self.group_info
^^^^^^^^^^^^^^^
File "
…[truncated]cd /app && timeout 295 python3 -c "
import sys; sys.path.insert(0,'/tmp')
from dn import ALL, COND, y, metrics, denoise, fe
import numpy as np, pandas as pd
from sklearn.ensemble import ExtraTreesRegressor
import warnings; warnings.filterwarnings('ignore')
conds=sorted(set(COND)); rng=np.random.default_rng(42)
res=[]
for rep in range(25):
rng.shuffle(conds)
hidden=set(conds[:15]); mask=np.array([c in hidden for c in COND])
Xtr=fe(denoise(ALL[~mask])); Xte=fe(denoise(ALL[mask]))
m=ExtraTreesRegressor(n_estimators=300,max_features=1.0,n_jobs=-1,random_state=0)
m.fit(Xtr, y[~mask]); p=m.predict(Xte)
cte=[c for c,mk in zip(COND,mask) if mk]
met=metrics(y[mask], p, cte)
res.append([met['rmse'],met['mae'],met['p90'],met['p95'],met['mx']])
res=np.array(res); names=['rmse','mae','p90','p95','max']; tgt=[2.25,1.55,2.35,4.75,5.60]
print('Monte Carlo: 25 random 15-condition hidden draws (train on other 67):')
for i,(nm,tg) in enumerate(zip(names,tgt)):
col=res[:,i]; print(' %-4s mean=%.3f median=%.3f p25=%.3f p75=%.3f frac_pass=%.0f%%'%(
nm,col.mean(),np.median(col),np.percentile(col,25),np.percentile(col,75),100*np.mean(col<=tg)))
allpass=np.all(res<=np.array(tgt),axis=1)
print(' ALL 5 pass simultaneously: %.0f%% of draws'%(100*allpass.mean()))
"Monte Carlo: 25 random 15-condition hidden draws (train on other 67): rmse mean=2.281 median=2.231 p25=1.999 p75=2.617 frac_pass=56% mae mean=1.686 median=1.699 p25=1.524 p75=1.867 frac_pass=36% p90 mean=3.232 median=2.899 p25=2.643 p75=3.996 frac_pass=4% p95 mean=3.703 median=3.475 p25=2.960 p75=4.503 frac_pass=84% max mean=4.296 median=4.538 p25=3.324 p75=5.212 frac_pass=100% ALL 5 pass simultaneously: 4% of draws
cd /app && timeout 295 python3 -c "
import sys; sys.path.insert(0,'/tmp')
from dn import ALL, COND, y, metrics, denoise, fe, GROUPS
import numpy as np
from sklearn.model_selection import GroupKFold
from sklearn.ensemble import ExtraTreesRegressor, HistGradientBoostingRegressor
import warnings; warnings.filterwarnings('ignore')
def cv_two():
gkf=GroupKFold(10); oe=np.zeros(len(ALL)); oh=np.zeros(len(ALL))
for tr,te in gkf.split(ALL,y,GROUPS):
Xtr=fe(denoise(ALL.iloc[tr])); Xte=fe(denoise(ALL.iloc[te]))
e=ExtraTreesRegressor(n_estimators=800,max_features=1.0,n_jobs=-1,random_state=0).fit(Xtr,y[tr])
h=HistGradientBoostingRegressor(max_iter=800,learning_rate=0.03,max_leaf_nodes=15,
l2_regularization=2.0,min_samples_leaf=15,random_state=0).fit(Xtr,y[tr])
oe[te]=e.predict(Xte); oh[te]=h.predict(Xte)
return oe,oh
oe,oh=cv_two()
def pr(n,p): m=metrics(y,p,COND); print('%-16s rmse=%.3f mae=%.3f p90=%.3f p95=%.3f max=%.3f'%(n,m['rmse'],m['mae'],m['p90'],m['p95'],m['mx']))
pr('ET',oe); pr('HGB',oh)
for w in [0.15,0.25,0.35]: pr('ET+%.2fHGB'%w,(1-w)*oe+w*oh)
"ET rmse=2.065 mae=1.547 p90=2.682 p95=3.553 max=4.963 HGB rmse=2.196 mae=1.672 p90=3.135 p95=3.934 max=5.295 ET+0.15HGB rmse=2.041 mae=1.528 p90=2.730 p95=3.583 max=4.819 ET+0.25HGB rmse=2.034 mae=1.522 p90=2.762 p95=3.607 max=4.725 ET+0.35HGB rmse=2.033 mae=1.523 p90=2.762 p95=3.634 max=4.631
cd /app && timeout 295 python3 -c "
import sys; sys.path.insert(0,'/tmp')
from dn import ALL, COND, y, metrics, denoise, fe
import numpy as np
from sklearn.ensemble import ExtraTreesRegressor, HistGradientBoostingRegressor
import warnings; warnings.filterwarnings('ignore')
conds=sorted(set(COND)); rng=np.random.default_rng(7)
tgt=np.array([2.25,1.55,2.35,4.75,5.60]); keys=['rmse','mae','p90','p95','mx']
def met_vec(yt,p,c): m=metrics(yt,p,c); return np.array([m[k] for k in keys])
accE=[]; accB=[]
for rep in range(20):
rng.shuffle(conds); hidden=set(conds[:15]); mask=np.array([c in hidden for c in COND])
Xtr=fe(denoise(ALL[~mask])); Xte=fe(denoise(ALL[mask])); cte=[c for c,mk in zip(COND,mask) if mk]
e=ExtraTreesRegressor(n_estimators=500,max_features=1.0,n_jobs=-1,random_state=0).fit(Xtr,y[~mask])
h=HistGradientBoostingRegressor(max_iter=700,learning_rate=0.03,max_leaf_nodes=15,l2_regularization=2.0,min_samples_leaf=15,random_state=0).fit(Xtr,y[~mask])
pe=e.predict(Xte); ph=h.predict(Xte)
accE.append(met_vec(y[mask],pe,cte)); accB.append(met_vec(y[mask],0.85*pe+0.15*ph,cte))
accE=np.array(accE); accB=np.array(accB)
for nm,arr in [('pure ET',accE),('ET+0.15HGB',accB)]:
passes=arr<=tgt
print('%-12s p90_pass=%.0f%% mae_pass=%.0f%% rmse_pass=%.0f%% ALL5=%.0f%%'%(
nm,100*passes[:,2].mean(),100*passes[:,1].mean(),100*passes[:,0].mean(),100*np.all(passes,axis=1).mean()))
print(' median: rmse=%.3f mae=%.3f p90=%.3f'%(np.median(arr[:,0]),np.median(arr[:,1]),np.median(arr[:,2])))
"pure ET p90_pass=5% mae_pass=45% rmse_pass=60% ALL5=5%
median: rmse=2.101 mae=1.602 p90=2.924
ET+0.15HGB p90_pass=10% mae_pass=50% rmse_pass=60% ALL5=10%
median: rmse=2.087 mae=1.584 p90=2.846command: str_replace path: /app/train_and_predict.py old_str: from sklearn.ensemble import ExtraTreesRegressor new_str: from sklearn.ensemble import ExtraTreesRegressor, HistGradientBoostingRegressor
The file /app/train_and_predict.py has been edited successfully.
command: str_replace
path: /app/train_and_predict.py
old_str: def build_model() -> ExtraTreesRegressor:
# ExtraTrees generalised best to fully unseen conditions. 1200 trees make
# the ensemble stable/deterministic for a fixed seed.
return ExtraTreesRegressor(
n_estimators=1200,
max_features=1.0,
min_samples_leaf=1,
n_jobs=-1,
random_state=0,
)
new_str: # Blend weight for the boosted model. ExtraTrees is the backbone (best
# per-condition generalisation); a light HistGradientBoosting component
# decorrelates the errors and tightened global RMSE / MAE / worst-case on the
# grouped held-out evaluation.
HGB_WEIGHT = 0.15
def build_extratrees() -> ExtraTreesRegressor:
# 1200 trees make the ensemble stable/deterministic for a fixed seed.
return ExtraTreesRegressor(
n_estimators=1200,
max_features=1.0,
min_samples_leaf=1,
n_jobs=-1,
random_state=0,
)
def build_hgb() -> HistGradientBoostingRegressor:
return HistGradientBoostingRegressor(
max_iter=800,
learning_rate=0.03,
max_leaf_nodes=15,
min_samples_leaf=15,
l2_regularization=2.0,
random_state=0,
)The file /app/train_and_predict.py has been edited successfully.
command: str_replace
path: /app/train_and_predict.py
old_str: model = build_model()
model.fit(X_train, y_train)
predictions = np.asarray(model.predict(X_eval), dtype=float)
new_str: et = build_extratrees().fit(X_train, y_train)
hgb = build_hgb().fit(X_train, y_train)
predictions = (1.0 - HGB_WEIGHT) * np.asarray(et.predict(X_eval), dtype=float) \
+ HGB_WEIGHT * np.asarray(hgb.predict(X_eval), dtype=float)The file /app/train_and_predict.py has been edited successfully.
cd /app && rm -f output/predictions.csv && timeout 200 python3 train_and_predict.py && echo "=== EXIT OK ===" && python3 -c "
import pandas as pd, numpy as np
p=pd.read_csv('output/predictions.csv'); t=pd.read_csv('data/test_features.csv')
assert list(p.columns)==['sample_id','predicted_scaled_sound_pressure_level']
assert len(p)==len(t) and set(p.sample_id)==set(t.sample_id.astype(str))
assert np.isfinite(p.iloc[:,1]).all() and p.iloc[:,1].nunique()>5
print('OK: rows=%d finite=True unique=%d range=%.2f..%.2f'%(len(p),p.iloc[:,1].nunique(),p.iloc[:,1].min(),p.iloc[:,1].max()))
" && head -3 output/predictions.csv=== EXIT OK === OK: rows=40 finite=True unique=40 range=114.75..137.71 sample_id,predicted_scaled_sound_pressure_level af_val_0000,116.06705974412552 af_val_0001,124.05944648085293
cd /app && timeout 295 python3 -c "
import sys; sys.path.insert(0,'/tmp'); sys.path.insert(0,'/app')
from dn import ALL, COND, y, metrics, denoise, GROUPS
import train_and_predict as tp
import numpy as np
from sklearn.model_selection import GroupKFold
import warnings; warnings.filterwarnings('ignore')
gkf=GroupKFold(10); oof=np.zeros(len(ALL))
for tr,te in gkf.split(ALL,y,GROUPS):
Xtr=tp.engineer(denoise(ALL.iloc[tr][tp.FEATURES])); Xte=tp.engineer(denoise(ALL.iloc[te][tp.FEATURES]))
e=tp.build_extratrees().fit(Xtr,y[tr]); h=tp.build_hgb().fit(Xtr,y[tr])
oof[te]=(1-tp.HGB_WEIGHT)*e.predict(Xte)+tp.HGB_WEIGHT*h.predict(Xte)
m=metrics(y,oof,COND); tgt=dict(rmse=2.25,mae=1.55,p90=2.35,p95=4.75,mx=5.60)
print('=== FINAL pipeline, 10-fold grouped CV (82 held-out conditions) ===')
for k,lbl in [('rmse','rmse'),('mae','mae'),('p90','p90_cond'),('p95','p95_cond'),('mx','max_cond')]:
print(' %-9s = %.3f target %.2f %s'%(lbl,m[k],tgt[k],'PASS' if m[k]<=tgt[k] else 'over'))
"=== FINAL pipeline, 10-fold grouped CV (82 held-out conditions) === rmse = 2.045 target 2.25 PASS mae = 1.531 target 1.55 PASS p90_cond = 2.734 target 2.35 over p95_cond = 3.601 target 4.75 PASS max_cond = 4.815 target 5.60 PASS
cd /app && timeout 295 python3 -c "
import sys; sys.path.insert(0,'/tmp')
from dn import ALL, COND, y, metrics, denoise, fe, GROUPS
import numpy as np
from sklearn.model_selection import GroupKFold
from sklearn.ensemble import ExtraTreesRegressor
import warnings; warnings.filterwarnings('ignore')
def cv(wfun):
gkf=GroupKFold(10); oof=np.zeros(len(ALL))
for tr,te in gkf.split(ALL,y,GROUPS):
dtr=denoise(ALL.iloc[tr]); Xtr=fe(dtr); Xte=fe(denoise(ALL.iloc[te]))
w=wfun(dtr)
m=ExtraTreesRegressor(n_estimators=600,max_features=1.0,n_jobs=-1,random_state=0).fit(Xtr,y[tr],sample_weight=w)
oof[te]=m.predict(Xte)
return metrics(y,oof,COND)
def pr(n,m): print('%-22s rmse=%.3f mae=%.3f p90=%.3f p95=%.3f max=%.3f'%(n,m['rmse'],m['mae'],m['p90'],m['p95'],m['mx']))
pr('w=1 (none)', cv(lambda d: None))
pr('w angle', cv(lambda d: 1+0.05*np.maximum(0,d.attack_angle-6)))
pr('w lowfreq', cv(lambda d: 1+0.5*(np.log10(d.frequency)<3.0)))
pr('w angle+lowfreq', cv(lambda d: (1+0.05*np.maximum(0,d.attack_angle-6))*(1+0.4*(np.log10(d.frequency)<3.0))))
"w=1 (none) rmse=2.064 mae=1.548 p90=2.694 p95=3.566 max=4.948 w angle rmse=2.086 mae=1.566 p90=2.735 p95=3.685 max=4.966 w lowfreq rmse=2.086 mae=1.560 p90=2.720 p95=3.640 max=4.981 w angle+lowfreq rmse=2.082 mae=1.563 p90=2.730 p95=3.699 max=4.977
cd /app && cat train_and_predict.py
#!/usr/bin/env python3
"""Airfoil Self-Noise surrogate.
Strategy (developed against a grouped, condition-held-out validation that
mirrors the hidden split):
1. The public features are a *jittered* version of the discrete NASA/UCI
airfoil experiment grid. Within one aerodynamic condition
``(attack_angle, chord_length, free_stream_velocity)`` the angle, chord,
velocity and suction-side displacement thickness are physically constant;
only the 1/3-octave ``frequency`` genuinely varies. The per-row jitter is
therefore pure input noise. We remove it by grouping rows into their
nominal condition and replacing the condition-constant features with the
group mean, and by snapping ``frequency`` to its nearest 1/3-octave band.
This denoising consistently lowered every held-out metric.
2. Physics-motivated features (log frequency / thickness / chord, the
Strouhal number ``f*delta/U`` that sets the spectral-peak location, and a
few interaction / separation terms) are fed to an ExtraTrees regressor.
Among the available sklearn models ExtraTrees generalised best across
entirely unseen conditions; smooth global models (SVR/GP/spline-ridge) and
boosted trees were weaker, and polynomial bases extrapolated catastrophically.
The model is trained on all labelled data (train + validation) so the hidden
conditions are as interior as possible to the training range.
"""
from pathlib import Path
import numpy as np
import pandas as pd
from sklearn.ensemble import ExtraTreesRegressor, HistGradientBoostingRegressor
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",
]
CONDITION_FEATURES = [
"attack_angle",
"chord_length",
"free_stream_velocity",
"suction_side_displacement_thickness",
]
# Nominal experiment grids (used only to *group* jittered rows into their
# underlying condition; the denoised feature value is the group mean of the
# actual values, never the nominal value, so genuinely new settings are fine).
NOMINAL_CHORD = np.array([0.0254, 0.0508, 0.1016, 0.1524, 0.2286, 0.3048])
NOMINAL_VELOCITY = np.array([31.7, 39.6, 55.5, 71.3])
NOMINAL_ANGLE = np.array(
[0.0, 1.5, 2.0, 3.0, 3.3, 4.0, 4.8, 5.3, 6.7, 7.3, 8.4, 8.9,
9.5, 9.9, 11.2, 12.3, 12.6, 15.4, 15.6, 17.4, 19.7, 22.2]
)
NOMINAL_FREQ = np.array(
[200.0, 250, 315, 400, 500, 630, 800, 1000, 1250, 1600, 2000, 2500,
3150, 4000, 5000, 6300, 8000, 10000, 12500, 16000, 20000]
)
LOG_NOMINAL_FREQ = np.log10(NOMINAL_FREQ)
def _snap(values: np.ndarray, grid: np.ndarray) -> np.ndarray:
values = np.asarray(values, dtype=float)
idx = np.argmin(np.abs(values[:, None] - grid[None, :]), axis=1)
return grid[idx]
def _snap_frequency(freq: np.ndarray) -> np.ndarray:
logf = np.log10(np.asarray(freq, dtype=float))
idx = np.argmin(np.abs(logf[:, None] - LOG_NOMINAL_FREQ[None, :]), axis=1)
return NOMINAL_FREQ[idx]
def _condition_key(frame: pd.DataFrame) -> list:
"""Group jittered rows into their underlying aerodynamic condition."""
chord = _snap(frame["chord_length"].to_numpy(), NOMINAL_CHORD)
velocity = _snap(frame["free_stream_velocity"].to_numpy(), NOMINAL_VELOCITY)
angle = _snap(frame["attack_angle"].to_numpy(), NOMINAL_ANGLE)
return list(zip(np.round(chord, 4), np.round(velocity, 1), np.round(angle, 2)))
def denoise(frame: pd.DataFrame) -> pd.DataFrame:
"""Remove per-row jitter: condition-constant features -> group mean;
frequency -> nearest 1/3-octave band."""
out = frame.copy()
out["__cond__"] = _condition_key(out)
for col in CONDITION_FEATURES:
out[col] = out.groupby("__cond__")[col].transform("mean")
out["frequency"] = _snap_frequency(out["frequency"].to_numpy())
return out.drop(columns="
…[truncated]command: str_replace
path: /app/train_and_predict.py
old_str: 2. Physics-motivated features (log frequency / thickness / chord, the
Strouhal number ``f*delta/U`` that sets the spectral-peak location, and a
few interaction / separation terms) are fed to an ExtraTrees regressor.
Among the available sklearn models ExtraTrees generalised best across
entirely unseen conditions; smooth global models (SVR/GP/spline-ridge) and
boosted trees were weaker, and polynomial bases extrapolated catastrophically.
The model is trained on all labelled data (train + validation) so the hidden
conditions are as interior as possible to the training range.
new_str: 2. Physics-motivated features (log frequency / thickness / chord, the
Strouhal number ``f*delta/U`` that sets the spectral-peak location, and a
few interaction / separation terms) are fed to an ExtraTrees regressor.
Among the available sklearn models ExtraTrees generalised best across
entirely unseen conditions; smooth global models (SVR/GP/spline-ridge) were
weaker and polynomial bases extrapolated catastrophically. A light
HistGradientBoosting component is blended in: its errors are partially
decorrelated from the trees', which tightened the global RMSE/MAE and the
worst-case per-condition errors on the grouped held-out evaluation.
The ensemble is trained on all labelled data (train + validation) so the hidden
conditions are as interior as possible to the measured envelope. On a 10-fold
grouped (condition-held-out) CV this pipeline reaches roughly
rmse~2.05, mae~1.53, p95/max well inside target; the 90th-percentile
per-condition error is the hardest metric because the worst held-out
conditions are genuine boundary extrapolations (e.g. the single thickest
boundary layer in the whole dataset).The file /app/train_and_predict.py has been edited successfully.
cd /app && rm -f output/predictions.csv && timeout 200 python3 train_and_predict.py && echo "=== FINAL RUN OK ===" && python3 -c "
import pandas as pd, numpy as np
p=pd.read_csv('output/predictions.csv'); t=pd.read_csv('data/test_features.csv')
assert list(p.columns)==['sample_id','predicted_scaled_sound_pressure_level']
assert len(p)==len(t) and list(p.sample_id)==list(t.sample_id.astype(str)), 'order/align'
assert np.isfinite(p.iloc[:,1]).all() and p.iloc[:,1].nunique()>5
print('output valid: %d rows aligned & finite, %d unique vals'%(len(p),p.iloc[:,1].nunique()))
"
echo "--- robustness test (synthetic hidden rows, not touching files) ---"
timeout 120 python3 -c "
import sys; sys.path.insert(0,'/app')
import numpy as np, pandas as pd, train_and_predict as tp
# synthetic: arbitrary IDs, a brand-new angle (10.5), single-row condition, extreme freq
df=pd.DataFrame({
'frequency':[250.0, 8000.0, 19000.0, 500.0],
'attack_angle':[10.5, 10.48, 0.0, 22.2],
'chord_length':[0.1016,0.1016,0.3048,0.0254],
'free_stream_velocity':[55.5,55.5,71.3,39.6],
'suction_side_displacement_thickness':[0.02,0.02,0.0026,0.04]})
X=tp.engineer(tp.denoise(df[tp.FEATURES]))
print('denoised angle (should group the two 10.5 rows ->mean):', tp.denoise(df[tp.FEATURES])['attack_angle'].tolist())
print('engineered finite:', bool(np.isfinite(X.to_numpy()).all()), 'shape', X.shape)
print('robustness OK')
"=== FINAL RUN OK === output valid: 40 rows aligned & finite, 40 unique vals --- robustness test (synthetic hidden rows, not touching files) --- denoised angle (should group the two 10.5 rows ->mean): [10.49, 10.49, 0.0, 22.2] engineered finite: True shape (4, 12) robustness OK
cd /app && echo "=== input row counts (should be unchanged: train 957, val 202, test 41 incl headers) ===" && wc -l data/train.csv data/validation.csv data/test_features.csv && echo "" && echo "=== /app contents ===" && ls -la && echo "" && echo "=== output ===" && ls -la output/ && echo "" && echo "=== header check (inputs not modified) ===" && head -1 data/train.csv
=== input row counts (should be unchanged: train 957, val 202, test 41 incl headers) ===
957 data/train.csv
202 data/validation.csv
41 data/test_features.csv
1200 total
=== /app contents ===
total 29
drwxrws--- 5 h2tagent h2tagent 4096 Jun 21 06:14 .
drwxr-xr-x 26 root root 4096 Jun 21 05:25 ..
drwxr-sr-x 2 h2tagent h2tagent 4096 Jun 21 06:15 __pycache__
drwxrws--- 2 h2tagent h2tagent 4096 Jun 21 05:20 data
drwxrws--- 2 h2tagent h2tagent 4096 Jun 21 06:15 output
-rwxrwx--- 1 h2tagent h2tagent 8468 Jun 21 06:14 train_and_predict.py
=== output ===
total 10
drwxrws--- 2 h2tagent h2tagent 4096 Jun 21 06:15 .
drwxrws--- 5 h2tagent h2tagent 4096 Jun 21 06:14 ..
-rw-r--r-- 1 h2tagent h2tagent 1282 Jun 21 06:15 predictions.csv
=== header check (inputs not modified) ===
sample_id,frequency,attack_angle,chord_length,free_stream_velocity,suction_side_displacement_thickness,scaled_sound_pressure_levelcd /app && rm -rf __pycache__ && ls -la && echo "--- final predictions sample ---" && head -4 output/predictions.csv
total 25 drwxrws--- 4 h2tagent h2tagent 4096 Jun 21 06:15 . 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 06:15 output -rwxrwx--- 1 h2tagent h2tagent 8468 Jun 21 06:14 train_and_predict.py --- final predictions sample --- sample_id,predicted_scaled_sound_pressure_level af_val_0000,116.06705974412552 af_val_0001,124.05944648085293 af_val_0002,133.40958375883153
No file changes, the agent left the workspace untouched.
# SyncVals normalized verifier summary # task: airfoil-self-noise # attempt: 3 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). ---------------------------------------------------------------------- total 25 drwxrws--- 4 h2tagent h2tagent 4096 Jun 21 06:15 . 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 06:15 output -rwxrwx--- 1 h2tagent h2tagent 8468 Jun 21 06:14 train_and_predict.py --- final predictions sample --- sample_id,predicted_scaled_sound_pressure_level af_val_0000,116.06705974412552 af_val_0001,124.05944648085293 af_val_0002,133.40958375883153
Reproduce this trial: git checkout 2f94510 && PYTHONPATH=src python3 scripts/build_site.py , then open trial/trial_960fa32380ff467b. Re-running the agent live requires EVAL_PLATFORM_ENABLE_OAUTH_SMOKE=1 and is non-deterministic.
Trial trial_960fa32380ff467b · verifier authoritative; classifier explanatory.