From 910dba0a7dcf0d4275068f159870a0b5a0635611 Mon Sep 17 00:00:00 2001 From: Daniel Rosel Date: Mon, 23 Mar 2026 21:47:04 +0100 Subject: [PATCH 01/44] chore: updated figure models and scripts --- .../final/final_focus_alpha_deltas.csv | 2 +- .../final/final_focus_alpha_mode_summary.csv | 2 +- .../final/final_focus_headline_summary.json | 17 +- .../final/final_focus_zone_summary.csv | 2 +- .../plots/final_focus_revenue_by_alpha.pdf | Bin 17520 -> 17518 bytes .../final/plots/final_focus_revenue_delta.pdf | Bin 19800 -> 19801 bytes .../final/plots/final_focus_risk_deltas.pdf | Bin 19553 -> 19550 bytes .../final/revenue_alpha_diagnostics.json | 24 + .../final/revenue_alpha_filtered.csv | 96 ++++ .../final/revenue_alpha_fixed_effects.json | 31 ++ .../final/revenue_alpha_per_sweep.csv | 2 + .../revenue_alpha_sample_accounting.json | 37 ++ .../final/revenue_alpha_simple_ols.json | 31 ++ .../figures/results/process_final_sweeps.py | 35 +- .../figures/results/revenue_alpha_analysis.py | 454 ++++++++++++++++++ .../figures/results/revenue_alpha_classic.py | 63 +++ 16 files changed, 783 insertions(+), 13 deletions(-) create mode 100644 paper/src/chapters/figures/results/generated/final/revenue_alpha_diagnostics.json create mode 100644 paper/src/chapters/figures/results/generated/final/revenue_alpha_filtered.csv create mode 100644 paper/src/chapters/figures/results/generated/final/revenue_alpha_fixed_effects.json create mode 100644 paper/src/chapters/figures/results/generated/final/revenue_alpha_per_sweep.csv create mode 100644 paper/src/chapters/figures/results/generated/final/revenue_alpha_sample_accounting.json create mode 100644 paper/src/chapters/figures/results/generated/final/revenue_alpha_simple_ols.json create mode 100644 paper/src/chapters/figures/results/revenue_alpha_analysis.py create mode 100644 paper/src/chapters/figures/results/revenue_alpha_classic.py diff --git a/paper/src/chapters/figures/results/generated/final/final_focus_alpha_deltas.csv b/paper/src/chapters/figures/results/generated/final/final_focus_alpha_deltas.csv index 32bbd73..84557d3 100644 --- a/paper/src/chapters/figures/results/generated/final/final_focus_alpha_deltas.csv +++ b/paper/src/chapters/figures/results/generated/final/final_focus_alpha_deltas.csv @@ -3,7 +3,7 @@ alpha,revenue_delta,revenue_delta_pct,reward_delta,reward_delta_pct,volatility_d 0.1,-14962.041501283413,-4.410637208586118,-14303.760282736213,-4.531344436782669,0.0011858665298920962,0.0,-0.004133727080174038 0.2,-16153.416666167905,-4.826514761457546,-15398.621298776357,-4.9418165571901715,0.00200624274016295,0.0,-0.0033201883450373615 0.3,-17294.9275360335,-5.382423616385397,-16544.91845114401,-5.533399709364953,-0.0011022484400295268,0.0,-0.0029151149203366505 -0.4,-19661.294346174283,-6.250307313590199,-18728.35578200908,-6.3953153560217535,3.582812967113658e-05,0.0,-0.0038123361988749577 +0.4,-19543.8750398212,-6.215299839915013,-18613.487687777204,-6.35858461426586,-2.7530592947980215e-05,0.0,-0.0038561140856475523 0.5,-16411.03168918495,-5.3630681206030015,-15638.77510066732,-5.4888928630525315,0.00015428950526953644,0.0,-0.00439661338956944 0.6,-14729.668247641937,-5.069964928178309,-13912.22417824401,-5.148827377884945,-0.002735776807082743,0.0,-0.004310129386364658 0.7,-21160.81910514756,-7.351404104505076,-20171.762105623755,-7.525169314210056,-0.0008903632602569461,0.0,-0.0026198461183787186 diff --git a/paper/src/chapters/figures/results/generated/final/final_focus_alpha_mode_summary.csv b/paper/src/chapters/figures/results/generated/final/final_focus_alpha_mode_summary.csv index 50051aa..9ab9a0c 100644 --- a/paper/src/chapters/figures/results/generated/final/final_focus_alpha_mode_summary.csv +++ b/paper/src/chapters/figures/results/generated/final/final_focus_alpha_mode_summary.csv @@ -7,7 +7,7 @@ alpha,mode,runs,revenue_mean,reward_mean,supra_mean,volatility_mean,coi_leakage_ 0.2,defended,35,318527.35122792586,296199.77820822067,0.0,0.07048630468445288,0.11265850300394666,137.2758153292305 0.3,baseline,30,321322.30327214615,299000.9636054795,0.0,0.07085669473747759,0.11527347603412934,136.4452630715689 0.3,defended,44,304027.37573611265,282456.0451543355,0.0,0.06975444629744806,0.11235836111379269,136.4704115371568 -0.4,baseline,33,314565.2423109539,292844.914432166,0.0,0.07031811881503117,0.11300307992768284,136.72547178046122 +0.4,baseline,33,314447.8230046008,292730.04633793415,0.0,0.07038147753765028,0.11304685781445543,136.70817144219887 0.4,defended,38,294903.9479647796,274116.55865015695,0.0,0.0703539469447023,0.10919074372880788,136.75671002806396 0.5,baseline,33,306000.80625751516,284916.7489847879,0.0,0.06938663916591635,0.11118137138243217,136.9528780620641 0.5,defended,35,289589.7745683302,269277.9738841206,0.0,0.06954092867118589,0.10678475799286273,136.65018588845163 diff --git a/paper/src/chapters/figures/results/generated/final/final_focus_headline_summary.json b/paper/src/chapters/figures/results/generated/final/final_focus_headline_summary.json index e257560..3ab4253 100644 --- a/paper/src/chapters/figures/results/generated/final/final_focus_headline_summary.json +++ b/paper/src/chapters/figures/results/generated/final/final_focus_headline_summary.json @@ -1,11 +1,14 @@ { - "bundle": "engine/studies/results/wandb_sweep_bundles/bundle_20260317_093826", + "bundle": "engine/studies/results/wandb_sweep_bundles/bundle_20260317_122818", "focus_cohort": "max_alpha_coverage", + "focus_sweep_id": "i88nw811", + "focus_run_count": 768, + "git_commit": "e62e842faad79b143f5555d187075e85c8926363", "alpha_cells": 11, "alpha_min": 0.0, "alpha_max": 1.0, - "mean_revenue_delta_pct": -4.787221975639986, - "mean_reward_delta_pct": -4.91730667541704, + "mean_revenue_delta_pct": -4.784039478033151, + "mean_reward_delta_pct": -4.913967517075595, "zone_summary": [ { "zone": "high_alpha_0_7_plus", @@ -18,10 +21,10 @@ { "zone": "low_alpha_below_0_7", "alpha_cells": 7, - "revenue_delta_pct_mean": -5.201949225367208, - "reward_delta_pct_mean": -5.324947138914036, - "coi_leakage_delta_mean": -0.0037041938968711296, - "volatility_delta_mean": 0.00011102505536893832 + "revenue_delta_pct_mean": -5.196948157699325, + "reward_delta_pct_mean": -5.319699890091765, + "coi_leakage_delta_mean": -0.003710447880695786, + "volatility_delta_mean": 0.00010197380928049306 } ] } diff --git a/paper/src/chapters/figures/results/generated/final/final_focus_zone_summary.csv b/paper/src/chapters/figures/results/generated/final/final_focus_zone_summary.csv index 224a022..7ae6bf5 100644 --- a/paper/src/chapters/figures/results/generated/final/final_focus_zone_summary.csv +++ b/paper/src/chapters/figures/results/generated/final/final_focus_zone_summary.csv @@ -1,3 +1,3 @@ zone,alpha_cells,revenue_delta_pct_mean,reward_delta_pct_mean,coi_leakage_delta_mean,volatility_delta_mean high_alpha_0_7_plus,4,-4.0614492886173466,-4.2039358642972955,-0.0018236753956396637,0.00026289072427068336 -low_alpha_below_0_7,7,-5.201949225367208,-5.324947138914036,-0.0037041938968711296,0.00011102505536893832 +low_alpha_below_0_7,7,-5.196948157699325,-5.319699890091765,-0.003710447880695786,0.00010197380928049306 diff --git a/paper/src/chapters/figures/results/generated/final/plots/final_focus_revenue_by_alpha.pdf b/paper/src/chapters/figures/results/generated/final/plots/final_focus_revenue_by_alpha.pdf index 343539eb657d0ac16452d3182e9f79695454bfa2..0ec609b669ef8a4a4c7e4ae917a01866e3e1a25a 100644 GIT binary patch delta 2071 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 B$*KSV diff --git a/paper/src/chapters/figures/results/generated/final/plots/final_focus_revenue_delta.pdf b/paper/src/chapters/figures/results/generated/final/plots/final_focus_revenue_delta.pdf index 157fc7ab6e663fdeea307044de8933a8493c17bf..5bbe5614d06e1f1d10f47c254233b211a5010354 100644 GIT binary patch delta 2832 zcmZuwdpMM7A2zeeS#mz)kXU8Vdzg9WouxwS9HL1>IZoqDh-TXkvnV7jnXp(Ir%NQ~ zCdaJHP*GbB8HALU+8UBIA-+C+`(595&HFunJ@5VePWOHP-jO`qXdbS%2*)~!u33B= zP1mz@DxUKCC@cCiVH|IG>z+ z*AdU#Q%DKdlpkQnjY%T|&8(@2@|$JzS*krDA;+`5Jm>vQ3wKvdghh5sN8h|cjo;sB zeNWHyOj)-#;k*)D_t};y$ea`}Z?7BL`Jgw6Gx}!=emKL4;W6$l##7n*yjOxZCXrXt zB`~&b4ta8f_^7DH*s(0P1ea5m)tKX>HLG|?-*8vP4#necT|FGdP4z6F%@G9&1q+9p zM@DfDnraaj=d~#VG}d&vppqA{&`~qDr^KZ`U+;?bO^rvFGTS~> zkhT?2d%XJaDntqXC9ST3_!dy#-{@k_wkjVh3{_aXbbAkr$>j#2S?zwF&)`qJKFgKdpywXK!_y^N!V~%FFMVatx_sk% zdCSBv9#F~&^;PxV$^{=f{f7C`+dK9>9t$S~1}s*DJ^7c`pzF%zwIpS+OonBe!J{2w z-zC2p9)DS*xI^6V?LZS;)h;w4I>@qz-VVN<5kxn9F8p5M-mG`ycdWf(=he1A2uk!gj(+E#S}+R%}&X9 z19kS)WGnyWa%D86$D}ikNv;!wkuCTtp>o=iwCnFOfs1%u?aUpneMUMh_%y{BGur~0 zo<*aPBDb;5n#F{-)q0dcg;$CNcl`!bf;FaES=9!z?Pmq%!`x7T`KkHl*V0odM*$D% zm8jL{=N4&aW2vd^eQG*ryXvR;rPrKx&CjdLAr`H=>oHKl_+DD- zWUkJ8v36?>}n;CcU-?{iaAi$Lekc=6hS+AGU>c%eB$34ZGRD;3Zp4t_}P! zO{m{>JcKkKKAFp`puMkF<}cd*d_{2S&`58vFtKgPXajNKc9HGE{LRx-BhlZlo2`Xpu$EkQUP{Z{hNI?$ z*zP8A)sM}U2i%H&Qh2AKL3rRVGgRaGoxulH;v+SJcXcY)Kl+?IzdrGddgwlTVjPXEa2-*S(bp~}f}F>1_3UfR41eE--ug0K2i zj%%7kr>E+Zsp-KoNcN_=#H<%Nu^(pZ-2L3Q91eQt zWn~b`XtBT3jM!@1ok(QdwrNPr+&b19GhS?{YUn)qkhGe!sKJ zbZ2t?>c@w3D`lDi(oXEP&HA_&wyp%3WfAnVUM4;ap$|BuCtLOU#W8eAKX6z{Z9NHM zbj!-(+E{C`Z|>_wQv81<_6HQE9OvEh%n#g5FH!bp97_`T`1#wW@#BB7sYoU68nqt9 zKb(xMs%TcCafcOSchKdGR~dsv<-#GYS=o9}iYApR9nGl_) z3nNWS#b`Wu5SKdya z86C1RKc{|~-N9?kAMH}##a~wUkAe4RF6sD|*ZNG3@3DArC$9syjjBJ#4wuxyp$(TM z1NKQmFi3&O5QV*D=fZ&WfGd|@ymjKNTdg2hPKT$Bv5Z`hmT5cVs3 zBNhphLqSjx41hsIG!B3$nA!k@KxENeA_QXUK@f=i9jE&bQ3OCR1W0V$0VNg*hOsn2 zh=^@P6paIjBovFGkf?kB2>?LRdL$V7LYwuq9{`CAih2Yf01?9*fl+K68Ndup1`*6K zWa8Iee(Aq)$Yc^mNHPV+U?_}WhCvbdzk>fyZvZ7D7{(}tg2|<@U)U?LC@{7Yg$!Uu zr=Vm}iXZ?IMLU525yr-mD5740z(!+H4?qwkiH-?^Fd4Hx;0A9IQ$Yy$KNyVh6x{eD zzmoQE4?qaTHi02jR9g^+$yf}5L>B@i0z@nZW40J1?jun!1&L$?gOLCtMphI;A-1Hm Oy(B1YXlUbVEB$ZM_$j~u delta 2690 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 delta 3270 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 diff --git a/paper/src/chapters/figures/results/generated/final/revenue_alpha_diagnostics.json b/paper/src/chapters/figures/results/generated/final/revenue_alpha_diagnostics.json new file mode 100644 index 0000000..dc23990 --- /dev/null +++ b/paper/src/chapters/figures/results/generated/final/revenue_alpha_diagnostics.json @@ -0,0 +1,24 @@ +{ + "normality": { + "test": "jarque_bera", + "available": true, + "statistic": 362.38850707984324, + "p_value": 2.0339278125496517e-79 + }, + "heteroskedasticity": { + "test": "breusch_pagan", + "available": true, + "lm_stat": 6.0366025380616275, + "df": 1, + "p_value": 0.014012224810767138 + }, + "influence": { + "max_leverage": 0.03769234230180875, + "mean_leverage": 0.021052631578947392, + "high_leverage_threshold": 0.042105263157894736, + "high_leverage_count": 0, + "max_cooks_distance": 0.29121755538277183, + "high_cooks_threshold": 0.042105263157894736, + "high_cooks_count": 6 + } +} diff --git a/paper/src/chapters/figures/results/generated/final/revenue_alpha_filtered.csv b/paper/src/chapters/figures/results/generated/final/revenue_alpha_filtered.csv new file mode 100644 index 0000000..f5b3176 --- /dev/null +++ b/paper/src/chapters/figures/results/generated/final/revenue_alpha_filtered.csv @@ -0,0 +1,96 @@ +sweep_id,sweep_full_id,run_id,run_name,state,run_url,created_at,runtime,downloaded_files,history_rows,selected_for_clone,download_error,alpha,n_products,eta_ux,lambda_coi,baseline_mode,no_robust,study_mode,eval_revenue_mean,eval_reward_mean,eval_stress_revenue_worst,eval_stress_reward_worst,eval_supra_share_mean,eval_supra_penalty_mean,eval_volatility_mean,eval_upward_volatility_mean,eval_coi_level_mean,eval_coi_leakage_mean,objective_score,mode +i88nw811,lusiana/capstone_tpu/i88nw811,0yph6ddt,sweep/ppo/sb3/cpu/default/a0.7/baseline/s1337,finished,https://wandb.ai/lusiana/capstone_tpu/runs/0yph6ddt,2026-03-15T13:48:47Z,7579.766959963,0,0,0,,0.7,100.0,0.0,0.05,True,True,baseline,285875.15518050164,266287.2051805016,274356.50146499986,255620.24146499988,0.0,0.0,0.0711188680417482,0.0,137.42722406640746,0.1099719716550294,255620.24146499988,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,bjwmxlf4,sweep/ppo/sb3/cpu/default/a0.9/baseline/s7777,finished,https://wandb.ai/lusiana/capstone_tpu/runs/bjwmxlf4,2026-03-15T13:48:49Z,7514.003863569,0,0,0,,0.9,100.0,0.0,0.05,True,True,baseline,267194.6114143838,248902.78141438385,258791.60782635584,241079.0878263559,0.0,0.0,0.0706779448814682,0.0,137.4716591479769,0.1060063717489262,241079.0878263559,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,afod7srx,sweep/ppo/sb3/cpu/default/a0/baseline/s1337,finished,https://wandb.ai/lusiana/capstone_tpu/runs/afod7srx,2026-03-15T13:48:55Z,8428.923550896,0,0,0,,0.0,100.0,0.0,0.15,True,True,baseline,331626.71399641165,307929.2839964116,301903.22363424243,278909.22363424255,0.0,0.0,0.0699106903089938,0.0,134.44341240328637,0.1239456985672444,278909.22363424255,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,czbwbw4o,sweep/ppo/sb3/cpu/default/a0.3/baseline/s7777,finished,https://wandb.ai/lusiana/capstone_tpu/runs/czbwbw4o,2026-03-15T13:48:55Z,8019.834460958,0,0,0,,0.3,100.0,0.0,0.05,True,True,baseline,325062.60932028474,302657.9893202848,313580.73955351143,292103.1195535114,0.0,0.0,0.0700934793925504,0.0,137.30226556155992,0.1156304945350146,292103.1195535114,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,spncr5i5,sweep/ppo/sb3/cpu/default/a0.4/baseline/s7777,finished,https://wandb.ai/lusiana/capstone_tpu/runs/spncr5i5,2026-03-15T13:48:57Z,7984.536208498,0,0,0,,0.4,100.0,0.0,0.3,True,True,baseline,313890.156459866,292317.566459866,301905.6061551721,281189.2661551722,0.0,0.0,0.0700585666613017,0.0,137.27393385978286,0.1140225013120235,281189.2661551722,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,9utcbgal,sweep/ppo/sb3/cpu/default/a0.6/baseline/s42,finished,https://wandb.ai/lusiana/capstone_tpu/runs/9utcbgal,2026-03-15T13:48:58Z,7794.573495005,0,0,0,,0.6,100.0,0.0,0.3,True,True,baseline,296881.4938150014,276559.4338150014,282693.0664052287,263321.0864052287,0.0,0.0,0.0689497793839256,0.0,137.65459475595475,0.1116745762120893,263321.0864052287,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,6uhc0zfi,sweep/ppo/sb3/cpu/default/a0.1/baseline/s42,finished,https://wandb.ai/lusiana/capstone_tpu/runs/6uhc0zfi,2026-03-15T13:48:59Z,8739.343652451,5,5000,1,,0.1,100.0,0.0,0.3,True,True,baseline,345607.36851277394,321934.388512774,330271.9018417394,307619.2418417394,0.0,0.0,0.0688978199434404,0.0,137.65927138408344,0.1180576040723697,307619.2418417394,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,mid9h16o,sweep/ppo/sb3/cpu/default/a0.3/baseline/s1337,finished,https://wandb.ai/lusiana/capstone_tpu/runs/mid9h16o,2026-03-15T13:48:59Z,7934.709025792,0,0,0,,0.3,100.0,0.0,0.15,True,True,baseline,321120.1030044527,298922.9430044526,312002.2572538445,290604.6972538445,0.0,0.0,0.0725338635316591,0.0,136.9642983472208,0.1152504371251349,290604.6972538445,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,hm8geh95,sweep/ppo/sb3/cpu/default/a0.3/baseline/s1337,finished,https://wandb.ai/lusiana/capstone_tpu/runs/hm8geh95,2026-03-15T13:49:01Z,8324.170881475,0,0,0,,0.3,100.0,0.0,0.05,True,True,baseline,321120.1030044527,298922.9430044526,312002.2572538445,290604.6972538445,0.0,0.0,0.0725338635316591,0.0,136.9642983472208,0.1152504371251349,290604.6972538445,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,2k3bx48e,sweep/ppo/sb3/cpu/default/a0.7/baseline/s42,finished,https://wandb.ai/lusiana/capstone_tpu/runs/2k3bx48e,2026-03-15T13:49:03Z,7579.046562713,0,0,0,,0.7,100.0,0.0,0.3,True,True,baseline,288003.5379862045,268208.7279862045,274205.49798255006,255466.81798255,0.0,0.0,0.0732015803628115,0.0,137.25851714050424,0.1065894678006264,255466.81798255,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,mlcllxuf,sweep/ppo/sb3/cpu/default/a0.3/baseline/s7777,finished,https://wandb.ai/lusiana/capstone_tpu/runs/mlcllxuf,2026-03-15T15:28:13Z,8048.447950291,0,0,0,,0.3,100.0,0.0,0.05,True,True,baseline,325062.60932028474,302657.9893202848,313580.73955351143,292103.1195535114,0.0,0.0,0.0700934793925504,0.0,137.30226556155992,0.1156304945350146,292103.1195535114,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,gsx5p3xl,sweep/ppo/sb3/cpu/default/a0.7/baseline/s7777,finished,https://wandb.ai/lusiana/capstone_tpu/runs/gsx5p3xl,2026-03-15T15:29:00Z,7666.062008427,0,0,0,,0.7,100.0,0.0,0.3,True,True,baseline,286859.8032779717,267231.9932779717,273198.5349293896,254530.3349293896,0.0,0.0,0.0694378534785247,0.0,137.6169536272908,0.1086813731317916,254530.3349293896,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,dh2sidg0,sweep/ppo/sb3/cpu/default/a0.8/baseline/s1337,finished,https://wandb.ai/lusiana/capstone_tpu/runs/dh2sidg0,2026-03-15T15:31:51Z,7450.114589126,0,0,0,,0.8,100.0,0.0,0.3,True,True,baseline,277537.1135308166,258574.23353081665,260525.6140973399,242761.4740973399,0.0,0.0,0.0691119185711536,0.0,137.63850710873982,0.1055234893030045,242761.4740973399,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,izb1xfjn,sweep/ppo/sb3/cpu/default/a0.4/baseline/s7777,finished,https://wandb.ai/lusiana/capstone_tpu/runs/izb1xfjn,2026-03-15T15:38:35Z,8138.431632101,0,0,0,,0.4,100.0,0.0,0.05,True,True,baseline,313890.156459866,292317.566459866,301905.6061551721,281189.2661551722,0.0,0.0,0.0700585666613017,0.0,137.27393385978286,0.1140225013120235,281189.2661551722,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,h5v0bjkk,sweep/ppo/sb3/cpu/default/a1/baseline/s42,finished,https://wandb.ai/lusiana/capstone_tpu/runs/h5v0bjkk,2026-03-15T15:53:08Z,7430.137394885,0,0,0,,1.0,100.0,0.0,0.05,True,True,baseline,258250.4083985968,240558.37839859675,257579.27605596423,239906.35605596425,0.0,0.0,0.0710781742010645,0.0,137.43891114039735,0.1034797519569495,239906.35605596425,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,oo9x7mtj,sweep/ppo/sb3/cpu/default/a0/baseline/s1337,finished,https://wandb.ai/lusiana/capstone_tpu/runs/oo9x7mtj,2026-03-15T17:08:57Z,8434.676111878,0,0,0,,0.0,100.0,0.0,0.15,True,True,baseline,331626.71399641165,307929.2839964116,301903.22363424243,278909.22363424255,0.0,0.0,0.0699106903089938,0.0,134.44341240328637,0.1239456985672444,278909.22363424255,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,2tnqjvsr,sweep/ppo/sb3/cpu/default/a0.2/baseline/s42,finished,https://wandb.ai/lusiana/capstone_tpu/runs/2tnqjvsr,2026-03-15T17:10:41Z,8326.316856098,0,0,0,,0.2,100.0,0.0,0.3,True,True,baseline,333463.32883383776,310606.38883383776,322375.37087837915,300349.6308783791,0.0,0.0,0.0694238399850746,0.0,137.6206723870474,0.1176551945750585,300349.6308783791,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,uwl4b1t4,sweep/ppo/sb3/cpu/default/a0.6/baseline/s7777,finished,https://wandb.ai/lusiana/capstone_tpu/runs/uwl4b1t4,2026-03-15T17:11:41Z,7730.138244902,0,0,0,,0.6,100.0,0.0,0.15,True,True,baseline,293934.0132863448,273673.5532863448,278235.2158621181,259045.3158621181,0.0,0.0,0.0702286844227449,0.0,137.02187396075487,0.1108792101893818,259045.3158621181,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,mq08631s,sweep/ppo/sb3/cpu/default/a0.7/baseline/s7777,finished,https://wandb.ai/lusiana/capstone_tpu/runs/mq08631s,2026-03-15T17:11:46Z,7830.903683379,0,0,0,,0.7,100.0,0.0,0.3,True,True,baseline,286859.8032779717,267231.9932779717,273198.5349293896,254530.3349293896,0.0,0.0,0.0694378534785247,0.0,137.6169536272908,0.1086813731317916,254530.3349293896,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,oenf81vs,sweep/ppo/sb3/cpu/default/a0.9/baseline/s42,finished,https://wandb.ai/lusiana/capstone_tpu/runs/oenf81vs,2026-03-15T17:14:03Z,7571.420325966,0,0,0,,0.9,100.0,0.0,0.15,True,True,baseline,268129.28805568966,249777.98805568964,259354.03651639624,241657.8165163962,0.0,0.0,0.0692141212557269,0.0,137.56737533812094,0.1028102128114812,241657.8165163962,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,imvig8ea,sweep/ppo/sb3/cpu/default/a0.9/baseline/s1337,finished,https://wandb.ai/lusiana/capstone_tpu/runs/imvig8ea,2026-03-15T17:26:17Z,7548.356923917,0,0,0,,0.9,100.0,0.0,0.05,True,True,baseline,269095.26288012683,250709.3028801269,257985.06236888352,240343.2023688835,0.0,0.0,0.0687681637998595,0.0,137.63174822647662,0.1040919495927453,240343.2023688835,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,kc46mwot,sweep/ppo/sb3/cpu/default/a0.9/baseline/s1337,finished,https://wandb.ai/lusiana/capstone_tpu/runs/kc46mwot,2026-03-15T17:36:54Z,7402.437478922,0,0,0,,0.9,100.0,0.0,0.3,True,True,baseline,269095.26288012683,250709.3028801269,257985.06236888352,240343.2023688835,0.0,0.0,0.0687681637998595,0.0,137.63174822647662,0.1040919495927453,240343.2023688835,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,6c5g20m0,sweep/ppo/sb3/cpu/default/a0.4/baseline/s42,finished,https://wandb.ai/lusiana/capstone_tpu/runs/6c5g20m0,2026-03-15T17:39:15Z,7987.751960449,0,0,0,,0.4,100.0,0.0,0.05,True,True,baseline,314792.9405088838,293199.96050888376,304000.02795477153,283160.5079547715,0.0,0.0,0.0706474903672308,0.0,137.54347765167836,0.1134114537317883,283160.5079547715,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,zmfirgme,sweep/ppo/sb3/cpu/default/a0.6/baseline/s42,finished,https://wandb.ai/lusiana/capstone_tpu/runs/zmfirgme,2026-03-15T17:39:38Z,7729.43292327,0,0,0,,0.6,100.0,0.0,0.3,True,True,baseline,296881.4938150014,276559.4338150014,282693.0664052287,263321.0864052287,0.0,0.0,0.0689497793839256,0.0,137.65459475595475,0.1116745762120893,263321.0864052287,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,5w978f6n,sweep/ppo/sb3/cpu/default/a0.2/baseline/s7777,finished,https://wandb.ai/lusiana/capstone_tpu/runs/5w978f6n,2026-03-15T17:42:23Z,8196.563842857,0,0,0,,0.2,100.0,0.0,0.3,True,True,baseline,328662.28105387173,305848.95105387166,316489.4913151873,294621.8913151873,0.0,0.0,0.0726481757500429,0.0,136.60489081120323,0.115056283050696,294621.8913151873,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,v6yuq532,sweep/ppo/sb3/cpu/default/a0.3/baseline/s42,finished,https://wandb.ai/lusiana/capstone_tpu/runs/v6yuq532,2026-03-15T18:27:32Z,8171.524047551,0,0,0,,0.3,100.0,0.0,0.3,True,True,baseline,325536.3728999571,303203.77289995714,311530.19009115506,290169.93009115505,0.0,0.0,0.0690101249418158,0.0,137.57976469566975,0.115140125484157,290169.93009115505,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,wzs4h708,sweep/ppo/sb3/cpu/default/a1/baseline/s42,finished,https://wandb.ai/lusiana/capstone_tpu/runs/wzs4h708,2026-03-15T18:44:40Z,7213.500579862,0,0,0,,1.0,100.0,0.0,0.3,True,True,baseline,258250.4083985968,240558.37839859675,257579.27605596423,239906.35605596425,0.0,0.0,0.0710781742010645,0.0,137.43891114039735,0.1034797519569495,239906.35605596425,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,drjegsa8,sweep/ppo/sb3/cpu/default/a0.8/baseline/s42,finished,https://wandb.ai/lusiana/capstone_tpu/runs/drjegsa8,2026-03-15T18:53:51Z,7642.750902648,0,0,0,,0.8,100.0,0.0,0.05,True,True,baseline,278042.9708277731,258987.21082777312,265119.53279206343,246979.39279206347,0.0,0.0,0.069699479796535,0.0,137.47635104131075,0.1063946886684759,246979.39279206347,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,np3fvzwt,sweep/ppo/sb3/cpu/default/a0.9/baseline/s1337,finished,https://wandb.ai/lusiana/capstone_tpu/runs/np3fvzwt,2026-03-15T18:57:50Z,7300.325366337,0,0,0,,0.9,100.0,0.0,0.3,True,True,baseline,269095.26288012683,250709.3028801269,257985.06236888352,240343.2023688835,0.0,0.0,0.0687681637998595,0.0,137.63174822647662,0.1040919495927453,240343.2023688835,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,kk0sqa97,sweep/ppo/sb3/cpu/default/a0.1/baseline/s1337,finished,https://wandb.ai/lusiana/capstone_tpu/runs/kk0sqa97,2026-03-15T19:06:17Z,8525.177181009,0,0,0,,0.1,100.0,0.0,0.3,True,True,baseline,341404.1205957663,317885.0305957663,329505.50925893825,306817.3492589383,0.0,0.0,0.0685274095002656,0.0,137.33021724658855,0.1206998447923596,306817.3492589383,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,i0rpx1kf,sweep/ppo/sb3/cpu/default/a0.2/baseline/s42,finished,https://wandb.ai/lusiana/capstone_tpu/runs/i0rpx1kf,2026-03-15T19:20:36Z,8356.73493734,0,0,0,,0.2,100.0,0.0,0.05,True,True,baseline,333463.32883383776,310606.38883383776,322375.37087837915,300349.6308783791,0.0,0.0,0.0694238399850746,0.0,137.6206723870474,0.1176551945750585,300349.6308783791,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,lqmaq5g2,sweep/ppo/sb3/cpu/default/a1/baseline/s1337,finished,https://wandb.ai/lusiana/capstone_tpu/runs/lqmaq5g2,2026-03-15T20:02:28Z,7470.274064026,0,0,0,,1.0,100.0,0.0,0.05,True,True,baseline,246584.29279154172,229303.12279154177,244564.78814724492,227386.888147245,0.0,0.0,0.0692074374069363,0.0,135.2844805658817,0.1093837602765936,227386.888147245,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,2umearxm,sweep/ppo/sb3/cpu/default/a0.5/baseline/s1337,finished,https://wandb.ai/lusiana/capstone_tpu/runs/2umearxm,2026-03-15T20:09:56Z,7829.406313163,0,0,0,,0.5,100.0,0.0,0.3,True,True,baseline,303325.5596877454,282520.29968774534,291965.65710567136,271937.69710567134,0.0,0.0,0.0686525035124021,0.0,137.57073544790862,0.1132342695408356,271937.69710567134,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,k7pirqxy,sweep/ppo/sb3/cpu/default/a1/baseline/s7777,finished,https://wandb.ai/lusiana/capstone_tpu/runs/k7pirqxy,2026-03-15T20:33:53Z,7216.626889631,0,0,0,,1.0,100.0,0.0,0.15,True,True,baseline,254537.24517731377,236935.99517731369,254471.2696855663,236912.16968556636,0.0,0.0,0.0703905833083271,0.0,136.6143424312229,0.1038838810036006,236912.16968556636,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,algnjce4,sweep/ppo/sb3/cpu/default/a0.6/baseline/s42,finished,https://wandb.ai/lusiana/capstone_tpu/runs/algnjce4,2026-03-15T20:54:24Z,7739.30650029,0,0,0,,0.6,100.0,0.0,0.05,True,True,baseline,296881.4938150014,276559.4338150014,282693.0664052287,263321.0864052287,0.0,0.0,0.0689497793839256,0.0,137.65459475595475,0.1116745762120893,263321.0864052287,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,vqe2dmcq,sweep/ppo/sb3/cpu/default/a0.4/baseline/s1337,finished,https://wandb.ai/lusiana/capstone_tpu/runs/vqe2dmcq,2026-03-15T21:08:22Z,7815.774646473,0,0,0,,0.4,100.0,0.0,0.05,True,True,baseline,316543.04043212667,294899.01043212664,299980.59649797506,279386.7564979751,0.0,0.0,0.067603468946279,0.0,137.7846896269947,0.1128739206843639,279386.7564979751,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,0xlvpawh,sweep/ppo/sb3/cpu/default/a0.3/baseline/s7777,finished,https://wandb.ai/lusiana/capstone_tpu/runs/0xlvpawh,2026-03-15T21:16:04Z,7997.68392245,0,0,0,,0.3,100.0,0.0,0.15,True,True,baseline,325062.60932028474,302657.9893202848,313580.73955351143,292103.1195535114,0.0,0.0,0.0700934793925504,0.0,137.30226556155992,0.1156304945350146,292103.1195535114,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,bofuxayn,sweep/ppo/sb3/cpu/default/a0.7/baseline/s1337,finished,https://wandb.ai/lusiana/capstone_tpu/runs/bofuxayn,2026-03-15T21:18:05Z,7486.102336723,0,0,0,,0.7,100.0,0.0,0.05,True,True,baseline,285875.15518050164,266287.2051805016,274356.50146499986,255620.24146499988,0.0,0.0,0.0711188680417482,0.0,137.42722406640746,0.1099719716550294,255620.24146499988,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,rujnezt7,sweep/ppo/sb3/cpu/default/a0.5/baseline/s7777,finished,https://wandb.ai/lusiana/capstone_tpu/runs/rujnezt7,2026-03-15T21:20:23Z,7936.01356938,0,0,0,,0.5,100.0,0.0,0.15,True,True,baseline,305342.590984541,284402.02098454104,287794.11179162114,267934.8717916211,0.0,0.0,0.0698329564541014,0.0,137.34875112178105,0.1110975441706762,267934.8717916211,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,f9e6wtv0,sweep/ppo/sb3/cpu/default/a0.7/baseline/s42,finished,https://wandb.ai/lusiana/capstone_tpu/runs/f9e6wtv0,2026-03-15T22:07:04Z,8030.825365422,0,0,0,,0.7,100.0,0.0,0.05,True,True,baseline,288003.5379862045,268208.7279862045,274205.49798255006,255466.81798255,0.0,0.0,0.0732015803628115,0.0,137.25851714050424,0.1065894678006264,255466.81798255,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,r8hsz3ko,sweep/ppo/sb3/cpu/default/a0.7/baseline/s7777,finished,https://wandb.ai/lusiana/capstone_tpu/runs/r8hsz3ko,2026-03-15T22:13:06Z,7691.998775531,0,0,0,,0.7,100.0,0.0,0.3,True,True,baseline,286859.8032779717,267231.9932779717,273198.5349293896,254530.3349293896,0.0,0.0,0.0694378534785247,0.0,137.6169536272908,0.1086813731317916,254530.3349293896,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,yukg46hv,sweep/ppo/sb3/cpu/default/a1/baseline/s7777,finished,https://wandb.ai/lusiana/capstone_tpu/runs/yukg46hv,2026-03-15T23:03:27Z,7094.861108483,0,0,0,,1.0,100.0,0.0,0.15,True,True,baseline,254537.24517731377,236935.99517731369,254471.2696855663,236912.16968556636,0.0,0.0,0.0703905833083271,0.0,136.6143424312229,0.1038838810036006,236912.16968556636,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,e5tciezz,sweep/ppo/sb3/cpu/default/a0.7/baseline/s1337,finished,https://wandb.ai/lusiana/capstone_tpu/runs/e5tciezz,2026-03-16T00:16:08Z,7569.145925588,0,0,0,,0.7,100.0,0.0,0.05,True,True,baseline,285875.15518050164,266287.2051805016,274356.50146499986,255620.24146499988,0.0,0.0,0.0711188680417482,0.0,137.42722406640746,0.1099719716550294,255620.24146499988,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,1rop5sf9,sweep/ppo/sb3/cpu/default/a0.3/baseline/s1337,finished,https://wandb.ai/lusiana/capstone_tpu/runs/1rop5sf9,2026-03-16T00:21:00Z,8354.617713686,0,0,0,,0.3,100.0,0.0,0.05,True,True,baseline,321120.1030044527,298922.9430044526,312002.2572538445,290604.6972538445,0.0,0.0,0.0725338635316591,0.0,136.9642983472208,0.1152504371251349,290604.6972538445,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,7muxpseb,sweep/ppo/sb3/cpu/default/a0.2/baseline/s42,finished,https://wandb.ai/lusiana/capstone_tpu/runs/7muxpseb,2026-03-16T00:21:21Z,8514.602541985,0,0,0,,0.2,100.0,0.0,0.05,True,True,baseline,333463.32883383776,310606.38883383776,322375.37087837915,300349.6308783791,0.0,0.0,0.0694238399850746,0.0,137.6206723870474,0.1176551945750585,300349.6308783791,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,304dyypp,sweep/ppo/sb3/cpu/default/a0.4/baseline/s7777,finished,https://wandb.ai/lusiana/capstone_tpu/runs/304dyypp,2026-03-16T00:37:04Z,7949.736292204,0,0,0,,0.4,100.0,0.0,0.3,True,True,baseline,313890.156459866,292317.566459866,301905.6061551721,281189.2661551722,0.0,0.0,0.0700585666613017,0.0,137.27393385978286,0.1140225013120235,281189.2661551722,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,zbw7nmeo,sweep/ppo/sb3/cpu/default/a0.1/baseline/s7777,finished,https://wandb.ai/lusiana/capstone_tpu/runs/zbw7nmeo,2026-03-16T00:53:02Z,8423.598177489,0,0,0,,0.1,100.0,0.0,0.05,True,True,baseline,340941.7898046945,317438.6698046944,328185.5337341634,305593.15373416344,0.0,0.0,0.0709483560344898,0.0,137.21682561970587,0.1186714838821206,305593.15373416344,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,oxu7rm37,sweep/ppo/sb3/cpu/default/a0.9/baseline/s42,finished,https://wandb.ai/lusiana/capstone_tpu/runs/oxu7rm37,2026-03-16T00:53:31Z,7464.830361968,0,0,0,,0.9,100.0,0.0,0.3,True,True,baseline,268129.28805568966,249777.98805568964,259354.03651639624,241657.8165163962,0.0,0.0,0.0692141212557269,0.0,137.56737533812094,0.1028102128114812,241657.8165163962,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,m78p26vk,sweep/ppo/sb3/cpu/default/a0/baseline/s7777,finished,https://wandb.ai/lusiana/capstone_tpu/runs/m78p26vk,2026-03-16T00:56:58Z,8717.289024041,5,1004,1,,0.0,100.0,0.0,0.15,True,True,baseline,348861.1454509751,324713.0754509751,335967.6160126648,312660.3160126648,0.0,0.0,0.0674835742466741,0.0,136.8813175598437,0.118985751213389,312660.3160126648,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,65zzmszh,sweep/ppo/sb3/cpu/default/a1/baseline/s1337,finished,https://wandb.ai/lusiana/capstone_tpu/runs/65zzmszh,2026-03-16T01:14:03Z,7326.553384609,0,0,0,,1.0,100.0,0.0,0.3,True,True,baseline,246584.29279154172,229303.12279154177,244564.78814724492,227386.888147245,0.0,0.0,0.0692074374069363,0.0,135.2844805658817,0.1093837602765936,227386.888147245,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,47xraqt6,sweep/ppo/sb3/cpu/default/a0.9/baseline/s1337,finished,https://wandb.ai/lusiana/capstone_tpu/runs/47xraqt6,2026-03-16T01:22:01Z,7299.814264453,0,0,0,,0.9,100.0,0.0,0.3,True,True,baseline,269095.26288012683,250709.3028801269,257985.06236888352,240343.2023688835,0.0,0.0,0.0687681637998595,0.0,137.63174822647662,0.1040919495927453,240343.2023688835,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,mibyt0bf,sweep/ppo/sb3/cpu/default/a0.9/baseline/s7777,finished,https://wandb.ai/lusiana/capstone_tpu/runs/mibyt0bf,2026-03-16T01:34:44Z,7541.153639959,0,0,0,,0.9,100.0,0.0,0.3,True,True,baseline,267194.6114143838,248902.78141438385,258791.60782635584,241079.0878263559,0.0,0.0,0.0706779448814682,0.0,137.4716591479769,0.1060063717489262,241079.0878263559,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,8ww25eu1,sweep/ppo/sb3/cpu/default/a0.4/baseline/s7777,finished,https://wandb.ai/lusiana/capstone_tpu/runs/8ww25eu1,2026-03-16T01:45:51Z,8003.812511886,0,0,0,,0.4,100.0,0.0,0.3,True,True,baseline,313890.156459866,292317.566459866,301905.6061551721,281189.2661551722,0.0,0.0,0.0700585666613017,0.0,137.27393385978286,0.1140225013120235,281189.2661551722,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,cxdz0iyj,sweep/ppo/sb3/cpu/default/a0.6/baseline/s7777,finished,https://wandb.ai/lusiana/capstone_tpu/runs/cxdz0iyj,2026-03-16T01:50:19Z,7623.493600288,0,0,0,,0.6,100.0,0.0,0.3,True,True,baseline,293934.0132863448,273673.5532863448,278235.2158621181,259045.3158621181,0.0,0.0,0.0702286844227449,0.0,137.02187396075487,0.1108792101893818,259045.3158621181,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,1aeqr4sw,sweep/ppo/sb3/cpu/default/a1/baseline/s7777,finished,https://wandb.ai/lusiana/capstone_tpu/runs/1aeqr4sw,2026-03-16T01:58:10Z,7156.375097998,0,0,0,,1.0,100.0,0.0,0.3,True,True,baseline,254537.24517731377,236935.99517731369,254471.2696855663,236912.16968556636,0.0,0.0,0.0703905833083271,0.0,136.6143424312229,0.1038838810036006,236912.16968556636,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,7sgqchvk,sweep/ppo/sb3/cpu/default/a0.9/baseline/s7777,finished,https://wandb.ai/lusiana/capstone_tpu/runs/7sgqchvk,2026-03-16T02:09:14Z,7268.202978965,0,0,0,,0.9,100.0,0.0,0.15,True,True,baseline,267194.6114143838,248902.78141438385,258791.60782635584,241079.0878263559,0.0,0.0,0.0706779448814682,0.0,137.4716591479769,0.1060063717489262,241079.0878263559,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,3s777ena,sweep/ppo/sb3/cpu/default/a0.5/baseline/s1337,finished,https://wandb.ai/lusiana/capstone_tpu/runs/3s777ena,2026-03-16T02:14:54Z,7762.769931002,0,0,0,,0.5,100.0,0.0,0.05,True,True,baseline,303325.5596877454,282520.29968774534,291965.65710567136,271937.69710567134,0.0,0.0,0.0686525035124021,0.0,137.57073544790862,0.1132342695408356,271937.69710567134,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,oxsvuh5p,sweep/ppo/sb3/cpu/default/a0.1/baseline/s7777,finished,https://wandb.ai/lusiana/capstone_tpu/runs/oxsvuh5p,2026-03-16T02:27:01Z,8529.692612353,0,0,0,,0.1,100.0,0.0,0.15,True,True,baseline,340941.7898046945,317438.6698046944,328185.5337341634,305593.15373416344,0.0,0.0,0.0709483560344898,0.0,137.21682561970587,0.1186714838821206,305593.15373416344,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,4unnwl9l,sweep/ppo/sb3/cpu/default/a0.7/baseline/s7777,finished,https://wandb.ai/lusiana/capstone_tpu/runs/4unnwl9l,2026-03-16T02:34:01Z,7780.065361146,0,0,0,,0.7,100.0,0.0,0.15,True,True,baseline,286859.8032779717,267231.9932779717,273198.5349293896,254530.3349293896,0.0,0.0,0.0694378534785247,0.0,137.6169536272908,0.1086813731317916,254530.3349293896,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,qlfu6ts4,sweep/ppo/sb3/cpu/default/a0.1/baseline/s7777,finished,https://wandb.ai/lusiana/capstone_tpu/runs/qlfu6ts4,2026-03-16T02:46:52Z,8357.276406226,0,0,0,,0.1,100.0,0.0,0.3,True,True,baseline,340941.7898046945,317438.6698046944,328185.5337341634,305593.15373416344,0.0,0.0,0.0709483560344898,0.0,137.21682561970587,0.1186714838821206,305593.15373416344,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,ya2bb56z,sweep/ppo/sb3/cpu/default/a1/baseline/s7777,finished,https://wandb.ai/lusiana/capstone_tpu/runs/ya2bb56z,2026-03-16T03:04:37Z,7161.126998896,0,0,0,,1.0,100.0,0.0,0.15,True,True,baseline,254537.24517731377,236935.99517731369,254471.2696855663,236912.16968556636,0.0,0.0,0.0703905833083271,0.0,136.6143424312229,0.1038838810036006,236912.16968556636,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,9hrjmcaf,sweep/ppo/sb3/cpu/default/a0.1/baseline/s42,finished,https://wandb.ai/lusiana/capstone_tpu/runs/9hrjmcaf,2026-03-16T03:13:29Z,8543.819880598,5,1004,1,,0.1,100.0,0.0,0.15,True,True,baseline,345607.36851277394,321934.388512774,330271.9018417394,307619.2418417394,0.0,0.0,0.0688978199434404,0.0,137.65927138408344,0.1180576040723697,307619.2418417394,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,bdz7jpg9,sweep/ppo/sb3/cpu/default/a0.4/baseline/s7777,finished,https://wandb.ai/lusiana/capstone_tpu/runs/bdz7jpg9,2026-03-16T03:19:29Z,8156.512730959,0,0,0,,0.4,100.0,0.0,0.15,True,True,baseline,313890.156459866,292317.566459866,301905.6061551721,281189.2661551722,0.0,0.0,0.0700585666613017,0.0,137.27393385978286,0.1140225013120235,281189.2661551722,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,4e8bw9fr,sweep/ppo/sb3/cpu/default/a0.4/baseline/s7777,finished,https://wandb.ai/lusiana/capstone_tpu/runs/4e8bw9fr,2026-03-16T03:23:44Z,7900.988162577,0,0,0,,0.4,100.0,0.0,0.3,True,True,baseline,313890.156459866,292317.566459866,301905.6061551721,281189.2661551722,0.0,0.0,0.0700585666613017,0.0,137.27393385978286,0.1140225013120235,281189.2661551722,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,rudposqg,sweep/ppo/sb3/cpu/default/a0.8/baseline/s7777,finished,https://wandb.ai/lusiana/capstone_tpu/runs/rudposqg,2026-03-16T04:16:36Z,7803.944972672,0,0,0,,0.8,100.0,0.0,0.15,True,True,baseline,277186.5585556976,258169.5585556976,260819.58418764165,242908.9641876417,0.0,0.0,0.0684627361221973,0.0,137.3260908975896,0.1077409453905398,242908.9641876417,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,r24xwwl9,sweep/ppo/sb3/cpu/default/a0.1/baseline/s7777,finished,https://wandb.ai/lusiana/capstone_tpu/runs/r24xwwl9,2026-03-16T04:43:43Z,8571.635566955,0,0,0,,0.1,100.0,0.0,0.15,True,True,baseline,340941.7898046945,317438.6698046944,328185.5337341634,305593.15373416344,0.0,0.0,0.0709483560344898,0.0,137.21682561970587,0.1186714838821206,305593.15373416344,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,34c0wzgt,sweep/ppo/sb3/cpu/default/a0.5/baseline/s42,finished,https://wandb.ai/lusiana/capstone_tpu/runs/34c0wzgt,2026-03-16T04:43:54Z,7912.776898111,0,0,0,,0.5,100.0,0.0,0.05,True,True,baseline,306631.1127310434,285624.6727310434,292140.0218133485,272205.32181334845,0.0,0.0,0.0706121906603894,0.0,137.48236407441985,0.112886126809283,272205.32181334845,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,7bvonhab,sweep/ppo/sb3/cpu/default/a0.2/baseline/s42,finished,https://wandb.ai/lusiana/capstone_tpu/runs/7bvonhab,2026-03-16T04:59:24Z,8276.510250338,0,0,0,,0.2,100.0,0.0,0.15,True,True,baseline,333463.32883383776,310606.38883383776,322375.37087837915,300349.6308783791,0.0,0.0,0.0694238399850746,0.0,137.6206723870474,0.1176551945750585,300349.6308783791,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,4f7j1z4p,sweep/ppo/sb3/cpu/default/a0/baseline/s42,finished,https://wandb.ai/lusiana/capstone_tpu/runs/4f7j1z4p,2026-03-16T05:37:06Z,8672.519975981,5,1004,1,,0.0,100.0,0.0,0.3,True,True,baseline,352771.72255003714,328513.3625500371,337718.8770159761,314393.4970159762,0.0,0.0,0.0709252720738168,0.0,137.49769422651883,0.1192149910017191,314393.4970159762,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,c33cyjv9,sweep/ppo/sb3/cpu/default/a0.4/baseline/s42,finished,https://wandb.ai/lusiana/capstone_tpu/runs/c33cyjv9,2026-03-16T05:38:08Z,8164.154912737,0,0,0,,0.4,100.0,0.0,0.15,True,True,baseline,314792.9405088838,293199.96050888376,304000.02795477153,283160.5079547715,0.0,0.0,0.0706474903672308,0.0,137.54347765167836,0.1134114537317883,283160.5079547715,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,i0pylqm1,sweep/ppo/sb3/cpu/default/a0.6/baseline/s7777,finished,https://wandb.ai/lusiana/capstone_tpu/runs/i0pylqm1,2026-03-16T05:54:46Z,7692.357589996,0,0,0,,0.6,100.0,0.0,0.15,True,True,baseline,293934.0132863448,273673.5532863448,278235.2158621181,259045.3158621181,0.0,0.0,0.0702286844227449,0.0,137.02187396075487,0.1108792101893818,259045.3158621181,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,p1lrhc1t,sweep/ppo/sb3/cpu/default/a0.5/baseline/s1337,finished,https://wandb.ai/lusiana/capstone_tpu/runs/p1lrhc1t,2026-03-16T06:06:24Z,7906.656203638,0,0,0,,0.5,100.0,0.0,0.15,True,True,baseline,304711.516143744,283789.716143744,290536.18598250934,270609.3259825093,0.0,0.0,0.0700712626186499,0.0,137.43043602946972,0.1112796769387625,270609.3259825093,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,lkhtnobk,sweep/ppo/sb3/cpu/default/a0.9/baseline/s1337,finished,https://wandb.ai/lusiana/capstone_tpu/runs/lkhtnobk,2026-03-16T06:25:11Z,7304.77470818,0,0,0,,0.9,100.0,0.0,0.3,True,True,baseline,269095.26288012683,250709.3028801269,257985.06236888352,240343.2023688835,0.0,0.0,0.0687681637998595,0.0,137.63174822647662,0.1040919495927453,240343.2023688835,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,dvf0av6p,sweep/ppo/sb3/cpu/default/a0/baseline/s1337,finished,https://wandb.ai/lusiana/capstone_tpu/runs/dvf0av6p,2026-03-16T06:34:22Z,8568.236301103,0,0,0,,0.0,100.0,0.0,0.3,True,True,baseline,331626.71399641165,307929.2839964116,301903.22363424243,278909.22363424255,0.0,0.0,0.0699106903089938,0.0,134.44341240328637,0.1239456985672444,278909.22363424255,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,k6dz4he1,sweep/ppo/sb3/cpu/default/a0/baseline/s1337,finished,https://wandb.ai/lusiana/capstone_tpu/runs/k6dz4he1,2026-03-16T06:38:33Z,8384.405275426,0,0,0,,0.0,100.0,0.0,0.05,True,True,baseline,331626.71399641165,307929.2839964116,301903.22363424243,278909.22363424255,0.0,0.0,0.0699106903089938,0.0,134.44341240328637,0.1239456985672444,278909.22363424255,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,3afj9zm5,sweep/ppo/sb3/cpu/default/a0.4/baseline/s7777,finished,https://wandb.ai/lusiana/capstone_tpu/runs/3afj9zm5,2026-03-16T06:51:33Z,7947.433015786,0,0,0,,0.4,100.0,0.0,0.3,True,True,baseline,313890.156459866,292317.566459866,301905.6061551721,281189.2661551722,0.0,0.0,0.0700585666613017,0.0,137.27393385978286,0.1140225013120235,281189.2661551722,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,lvlojvjv,sweep/ppo/sb3/cpu/default/a0.5/baseline/s7777,finished,https://wandb.ai/lusiana/capstone_tpu/runs/lvlojvjv,2026-03-16T07:17:09Z,8072.460782252,0,0,0,,0.5,100.0,0.0,0.05,True,True,baseline,305342.590984541,284402.02098454104,287794.11179162114,267934.8717916211,0.0,0.0,0.0698329564541014,0.0,137.34875112178105,0.1110975441706762,267934.8717916211,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,e6xtq7h5,sweep/ppo/sb3/cpu/default/a0.5/baseline/s42,finished,https://wandb.ai/lusiana/capstone_tpu/runs/e6xtq7h5,2026-03-16T07:20:29Z,8062.476629606,0,0,0,,0.5,100.0,0.0,0.05,True,True,baseline,306631.1127310434,285624.6727310434,292140.0218133485,272205.32181334845,0.0,0.0,0.0706121906603894,0.0,137.48236407441985,0.112886126809283,272205.32181334845,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,6yrs8xci,sweep/ppo/sb3/cpu/default/a0.6/baseline/s7777,finished,https://wandb.ai/lusiana/capstone_tpu/runs/6yrs8xci,2026-03-16T07:50:01Z,7609.609823102,0,0,0,,0.6,100.0,0.0,0.15,True,True,baseline,293934.0132863448,273673.5532863448,278235.2158621181,259045.3158621181,0.0,0.0,0.0702286844227449,0.0,137.02187396075487,0.1108792101893818,259045.3158621181,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,16l3qjpm,sweep/ppo/sb3/cpu/default/a0/baseline/s7777,finished,https://wandb.ai/lusiana/capstone_tpu/runs/16l3qjpm,2026-03-16T07:50:41Z,8443.503878801,5,1004,1,,0.0,100.0,0.0,0.15,True,True,baseline,348861.1454509751,324713.0754509751,335967.6160126648,312660.3160126648,0.0,0.0,0.0674835742466741,0.0,136.8813175598437,0.118985751213389,312660.3160126648,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,rg98ht1b,sweep/ppo/sb3/cpu/default/a0/baseline/s7777,finished,https://wandb.ai/lusiana/capstone_tpu/runs/rg98ht1b,2026-03-16T07:55:36Z,8843.938343818,5,1004,1,,0.0,100.0,0.0,0.05,True,True,baseline,348861.1454509751,324713.0754509751,335967.6160126648,312660.3160126648,0.0,0.0,0.0674835742466741,0.0,136.8813175598437,0.118985751213389,312660.3160126648,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,mxd3i6wr,sweep/ppo/sb3/cpu/default/a0.2/baseline/s42,finished,https://wandb.ai/lusiana/capstone_tpu/runs/mxd3i6wr,2026-03-16T07:58:03Z,8393.28184472,0,0,0,,0.2,100.0,0.0,0.15,True,True,baseline,333463.32883383776,310606.38883383776,322375.37087837915,300349.6308783791,0.0,0.0,0.0694238399850746,0.0,137.6206723870474,0.1176551945750585,300349.6308783791,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,0xvyhpg2,sweep/ppo/sb3/cpu/default/a0.9/baseline/s42,finished,https://wandb.ai/lusiana/capstone_tpu/runs/0xvyhpg2,2026-03-16T08:01:43Z,7441.092473369,0,0,0,,0.9,100.0,0.0,0.05,True,True,baseline,268129.28805568966,249777.98805568964,259354.03651639624,241657.8165163962,0.0,0.0,0.0692141212557269,0.0,137.56737533812094,0.1028102128114812,241657.8165163962,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,eull6lat,sweep/ppo/sb3/cpu/default/a0.2/baseline/s42,finished,https://wandb.ai/lusiana/capstone_tpu/runs/eull6lat,2026-03-16T08:03:08Z,8338.76018915,0,0,0,,0.2,100.0,0.0,0.05,True,True,baseline,333463.32883383776,310606.38883383776,322375.37087837915,300349.6308783791,0.0,0.0,0.0694238399850746,0.0,137.6206723870474,0.1176551945750585,300349.6308783791,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,5zekml75,sweep/ppo/sb3/cpu/default/a0.8/baseline/s1337,finished,https://wandb.ai/lusiana/capstone_tpu/runs/5zekml75,2026-03-16T08:06:29Z,7265.4990034,0,0,0,,0.8,100.0,0.0,0.15,True,True,baseline,277537.1135308166,258574.23353081665,260525.6140973399,242761.4740973399,0.0,0.0,0.0691119185711536,0.0,137.63850710873982,0.1055234893030045,242761.4740973399,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,fed0y4px,sweep/ppo/sb3/cpu/default/a0.7/baseline/s7777,finished,https://wandb.ai/lusiana/capstone_tpu/runs/fed0y4px,2026-03-16T08:13:55Z,7800.555020283,0,0,0,,0.7,100.0,0.0,0.05,True,True,baseline,286859.8032779717,267231.9932779717,273198.5349293896,254530.3349293896,0.0,0.0,0.0694378534785247,0.0,137.6169536272908,0.1086813731317916,254530.3349293896,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,byifn20j,sweep/ppo/sb3/cpu/default/a0.4/baseline/s1337,finished,https://wandb.ai/lusiana/capstone_tpu/runs/byifn20j,2026-03-16T08:20:55Z,8108.199462596,0,0,0,,0.4,100.0,0.0,0.3,True,True,baseline,316543.04043212667,294899.01043212664,299980.59649797506,279386.7564979751,0.0,0.0,0.067603468946279,0.0,137.7846896269947,0.1128739206843639,279386.7564979751,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,35rb8529,sweep/ppo/sb3/cpu/default/a0.5/baseline/s1337,finished,https://wandb.ai/lusiana/capstone_tpu/runs/35rb8529,2026-03-16T08:24:52Z,7749.649896228,0,0,0,,0.5,100.0,0.0,0.05,True,True,baseline,304711.516143744,283789.716143744,290536.18598250934,270609.3259825093,0.0,0.0,0.0700712626186499,0.0,137.43043602946972,0.1112796769387625,270609.3259825093,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,foinu2r1,sweep/ppo/sb3/cpu/default/a0.5/baseline/s42,finished,https://wandb.ai/lusiana/capstone_tpu/runs/foinu2r1,2026-03-16T08:51:50Z,7924.351691656,0,0,0,,0.5,100.0,0.0,0.05,True,True,baseline,306631.1127310434,285624.6727310434,292140.0218133485,272205.32181334845,0.0,0.0,0.0706121906603894,0.0,137.48236407441985,0.112886126809283,272205.32181334845,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,nsg7m2ud,sweep/ppo/sb3/cpu/default/a0.5/baseline/s1337,finished,https://wandb.ai/lusiana/capstone_tpu/runs/nsg7m2ud,2026-03-16T09:06:10Z,7732.794663489,0,0,0,,0.5,100.0,0.0,0.3,True,True,baseline,303325.5596877454,282520.29968774534,291965.65710567136,271937.69710567134,0.0,0.0,0.0686525035124021,0.0,137.57073544790862,0.1132342695408356,271937.69710567134,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,gpririem,sweep/ppo/sb3/cpu/default/a0.2/baseline/s42,finished,https://wandb.ai/lusiana/capstone_tpu/runs/gpririem,2026-03-16T09:20:57Z,8532.119121611,0,0,0,,0.2,100.0,0.0,0.3,True,True,baseline,333463.32883383776,310606.38883383776,322375.37087837915,300349.6308783791,0.0,0.0,0.0694238399850746,0.0,137.6206723870474,0.1176551945750585,300349.6308783791,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,9bmbalnk,sweep/ppo/sb3/cpu/default/a0.7/baseline/s1337,finished,https://wandb.ai/lusiana/capstone_tpu/runs/9bmbalnk,2026-03-16T10:05:49Z,7576.93090345,0,0,0,,0.7,100.0,0.0,0.15,True,True,baseline,285875.15518050164,266287.2051805016,274356.50146499986,255620.24146499988,0.0,0.0,0.0711188680417482,0.0,137.42722406640746,0.1099719716550294,255620.24146499988,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,9ma76sch,sweep/ppo/sb3/cpu/default/a0.1/baseline/s1337,finished,https://wandb.ai/lusiana/capstone_tpu/runs/9ma76sch,2026-03-16T10:23:59Z,8544.8427845,0,0,0,,0.1,100.0,0.0,0.3,True,True,baseline,341404.1205957663,317885.0305957663,329505.50925893825,306817.3492589383,0.0,0.0,0.0685274095002656,0.0,137.33021724658855,0.1206998447923596,306817.3492589383,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,cvrztiyb,sweep/ppo/sb3/cpu/default/a0.2/baseline/s42,finished,https://wandb.ai/lusiana/capstone_tpu/runs/cvrztiyb,2026-03-16T10:27:26Z,8353.396268583,0,0,0,,0.2,100.0,0.0,0.3,True,True,baseline,333463.32883383776,310606.38883383776,322375.37087837915,300349.6308783791,0.0,0.0,0.0694238399850746,0.0,137.6206723870474,0.1176551945750585,300349.6308783791,baseline +i88nw811,lusiana/capstone_tpu/i88nw811,7z9spcc6,sweep/ppo/sb3/cpu/default/a0/baseline/s7777,finished,https://wandb.ai/lusiana/capstone_tpu/runs/7z9spcc6,2026-03-16T10:29:46Z,8444.449882423,5,1004,1,,0.0,100.0,0.0,0.3,True,True,baseline,348861.1454509751,324713.0754509751,335967.6160126648,312660.3160126648,0.0,0.0,0.0674835742466741,0.0,136.8813175598437,0.118985751213389,312660.3160126648,baseline diff --git a/paper/src/chapters/figures/results/generated/final/revenue_alpha_fixed_effects.json b/paper/src/chapters/figures/results/generated/final/revenue_alpha_fixed_effects.json new file mode 100644 index 0000000..53c6e23 --- /dev/null +++ b/paper/src/chapters/figures/results/generated/final/revenue_alpha_fixed_effects.json @@ -0,0 +1,31 @@ +{ + "n": 95, + "k": 2, + "dof": 93, + "df_t": 93, + "cov_type": "hc1", + "clusters": null, + "r2": 0.9759651432807543, + "adj_r2": 0.9757067039611925, + "sse": 1872600419.7223544, + "coefficients": [ + { + "name": "intercept", + "coef": 348823.4131652292, + "std_error": 1383.3660823209932, + "t_stat": 252.15553397115096, + "p_value": 0.0, + "ci95_low": 346076.3222890517, + "ci95_high": 351570.5040414067 + }, + { + "name": "alpha", + "coef": -90140.52744561416, + "std_error": 2185.134882447838, + "t_stat": -41.25169945785529, + "p_value": 0.0, + "ci95_low": -94479.77225976942, + "ci95_high": -85801.2826314589 + } + ] +} diff --git a/paper/src/chapters/figures/results/generated/final/revenue_alpha_per_sweep.csv b/paper/src/chapters/figures/results/generated/final/revenue_alpha_per_sweep.csv new file mode 100644 index 0000000..9dfc53c --- /dev/null +++ b/paper/src/chapters/figures/results/generated/final/revenue_alpha_per_sweep.csv @@ -0,0 +1,2 @@ +sweep_id,n,alpha_coef,alpha_std_error,alpha_t_stat,alpha_p_value,alpha_ci95_low,alpha_ci95_high,r2 +i88nw811,95,-90140.52744561416,2185.134882447838,-41.25169945785529,0.0,-94479.77225976942,-85801.2826314589,0.9759651432807543 diff --git a/paper/src/chapters/figures/results/generated/final/revenue_alpha_sample_accounting.json b/paper/src/chapters/figures/results/generated/final/revenue_alpha_sample_accounting.json new file mode 100644 index 0000000..689e768 --- /dev/null +++ b/paper/src/chapters/figures/results/generated/final/revenue_alpha_sample_accounting.json @@ -0,0 +1,37 @@ +{ + "bundle_dir": "/home/velocitatem/Documents/Projects/PHANTOM/engine/studies/results/wandb_sweep_bundles/bundle_20260317_122818", + "git_commit": "e62e842faad79b143f5555d187075e85c8926363", + "cohort_name": "original_n95_baseline_n100", + "filters": { + "sweep_id": [ + "i88nw811" + ], + "mode": "baseline", + "n_products": 100.0, + "eta_ux": 0.0, + "lambda_coi": null, + "alpha_min": 0.0, + "alpha_max": 1.0 + }, + "n_rows": 95, + "n_sweeps": 1, + "alpha_unique": [ + 0.0, + 0.1, + 0.2, + 0.3, + 0.4, + 0.5, + 0.6, + 0.7, + 0.8, + 0.9, + 1.0 + ], + "rows_by_sweep": { + "i88nw811": 95 + }, + "rows_by_mode": { + "baseline": 95 + } +} diff --git a/paper/src/chapters/figures/results/generated/final/revenue_alpha_simple_ols.json b/paper/src/chapters/figures/results/generated/final/revenue_alpha_simple_ols.json new file mode 100644 index 0000000..74dc241 --- /dev/null +++ b/paper/src/chapters/figures/results/generated/final/revenue_alpha_simple_ols.json @@ -0,0 +1,31 @@ +{ + "n": 95, + "k": 2, + "dof": 93, + "df_t": 93, + "cov_type": "iid", + "clusters": null, + "r2": 0.9759651432807543, + "adj_r2": 0.9757067039611925, + "sse": 1872600419.7223544, + "coefficients": [ + { + "name": "intercept", + "coef": 348823.4131652292, + "std_error": 860.7176431608721, + "t_stat": 405.2704344298337, + "p_value": 0.0, + "ci95_low": 347114.1985078009, + "ci95_high": 350532.6278226575 + }, + { + "name": "alpha", + "coef": -90140.52744561416, + "std_error": 1466.838282353916, + "t_stat": -61.452259959401054, + "p_value": 0.0, + "ci95_low": -93053.37756806448, + "ci95_high": -87227.67732316385 + } + ] +} diff --git a/paper/src/chapters/figures/results/process_final_sweeps.py b/paper/src/chapters/figures/results/process_final_sweeps.py index f874e2b..63270d1 100644 --- a/paper/src/chapters/figures/results/process_final_sweeps.py +++ b/paper/src/chapters/figures/results/process_final_sweeps.py @@ -3,6 +3,7 @@ from __future__ import annotations import argparse import json from pathlib import Path +import subprocess from typing import Any import matplotlib @@ -37,6 +38,20 @@ def _default_plot_dir(output_dir: Path) -> Path: return output_dir / "plots" +def _git_commit() -> str: + try: + result = subprocess.run( + ["git", "rev-parse", "HEAD"], + check=True, + text=True, + capture_output=True, + cwd=_project_root(), + ) + except Exception: + return "unknown" + return result.stdout.strip() + + def _truthy(value: Any) -> bool: if isinstance(value, bool): return value @@ -297,9 +312,16 @@ def _write_include(path: Path, figure_rel_path: str, width: str) -> Path: return path -def run(bundle_dir: Path, output_dir: Path, plot_dir: Path) -> list[Path]: +def run( + bundle_dir: Path, + output_dir: Path, + plot_dir: Path, + focus_sweep_id: str | None = None, +) -> list[Path]: all_runs = _load_runs(bundle_dir) - focus_id = _focus_sweep(all_runs) + focus_id = str(focus_sweep_id) if focus_sweep_id else _focus_sweep(all_runs) + if focus_id not in set(all_runs["sweep_id"].astype(str).unique()): + raise ValueError(f"Requested focus sweep_id not found: {focus_id}") focus_runs = all_runs[all_runs["sweep_id"] == focus_id].copy() alpha_mode = _alpha_mode_summary(focus_runs) deltas = _alpha_deltas(alpha_mode) @@ -324,6 +346,9 @@ def run(bundle_dir: Path, output_dir: Path, plot_dir: Path) -> list[Path]: headline = { "bundle": str(bundle_dir), "focus_cohort": "max_alpha_coverage", + "focus_sweep_id": focus_id, + "focus_run_count": int(len(focus_runs)), + "git_commit": _git_commit(), "alpha_cells": int(deltas["alpha"].nunique()) if not deltas.empty else 0, "alpha_min": float(deltas["alpha"].min()) if not deltas.empty else None, "alpha_max": float(deltas["alpha"].max()) if not deltas.empty else None, @@ -390,6 +415,7 @@ def main() -> None: parser.add_argument("--bundle-dir", type=Path, default=_default_bundle_dir()) parser.add_argument("--output-dir", type=Path, default=_default_output_dir()) parser.add_argument("--plot-dir", type=Path, default=None) + parser.add_argument("--focus-sweep-id", type=str, default=None) args = parser.parse_args() _configure_style() @@ -399,7 +425,10 @@ def main() -> None: else _default_plot_dir(args.output_dir) ) outputs = run( - bundle_dir=args.bundle_dir, output_dir=args.output_dir, plot_dir=plot_dir + bundle_dir=args.bundle_dir, + output_dir=args.output_dir, + plot_dir=plot_dir, + focus_sweep_id=args.focus_sweep_id, ) for path in outputs: print(path) diff --git a/paper/src/chapters/figures/results/revenue_alpha_analysis.py b/paper/src/chapters/figures/results/revenue_alpha_analysis.py new file mode 100644 index 0000000..f576880 --- /dev/null +++ b/paper/src/chapters/figures/results/revenue_alpha_analysis.py @@ -0,0 +1,454 @@ +from __future__ import annotations + +import argparse +import json +import subprocess +from pathlib import Path +from typing import Iterable + +import numpy as np +import pandas as pd +from scipy import stats + + +def _project_root() -> Path: + return Path(__file__).resolve().parents[5] + + +def _default_bundle_dir() -> Path: + base = _project_root() / "engine" / "studies" / "results" / "wandb_sweep_bundles" + bundles = sorted( + [path for path in base.glob("bundle_*") if path.is_dir()], + key=lambda path: path.stat().st_mtime, + reverse=True, + ) + if not bundles: + raise FileNotFoundError(f"No sweep bundle directories found in {base}") + return bundles[0] + + +def _bundle_dir_from_id(bundle_id: str) -> Path: + token = str(bundle_id).strip() + name = token if token.startswith("bundle_") else f"bundle_{token}" + path = ( + _project_root() + / "engine" + / "studies" + / "results" + / "wandb_sweep_bundles" + / name + ) + if not path.exists(): + raise FileNotFoundError(f"Bundle not found: {path}") + return path + + +def _default_output_dir() -> Path: + return Path(__file__).resolve().parent / "generated" / "final" + + +def _truthy(value: object) -> bool: + if isinstance(value, bool): + return value + if value is None: + return False + return str(value).strip().lower() in {"1", "true", "yes", "on"} + + +def _mode_of(row: pd.Series) -> str: + mode_hint = str(row.get("study_mode", "")).strip().lower() + if mode_hint in {"baseline", "no_robust"}: + return "baseline" + if mode_hint in {"defended", "robust"}: + return "defended" + if _truthy(row.get("baseline_mode")) or _truthy(row.get("no_robust")): + return "baseline" + return "defended" + + +def _coerce_numeric(frame: pd.DataFrame, columns: Iterable[str]) -> None: + for column in columns: + if column in frame.columns: + frame[column] = pd.to_numeric(frame[column], errors="coerce") + + +def _load_runs(bundle_dir: Path) -> pd.DataFrame: + path = bundle_dir / "runs_finished.csv" + if not path.exists(): + raise FileNotFoundError(f"Missing required file: {path}") + frame = pd.read_csv(path) + frame["mode"] = frame.apply(_mode_of, axis=1) + _coerce_numeric( + frame, + [ + "alpha", + "n_products", + "eta_ux", + "lambda_coi", + "eval_revenue_mean", + ], + ) + frame = frame[frame["mode"].isin({"baseline", "defended"})].copy() + return frame + + +def _get_git_commit() -> str: + try: + result = subprocess.run( + ["git", "rev-parse", "HEAD"], + check=True, + text=True, + capture_output=True, + cwd=_project_root(), + ) + except Exception: + return "unknown" + return result.stdout.strip() + + +def _apply_filters(frame: pd.DataFrame, args: argparse.Namespace) -> pd.DataFrame: + data = frame.copy() + if args.sweep_id: + allowed = {str(value) for value in args.sweep_id} + data = data[data["sweep_id"].astype(str).isin(allowed)] + if args.mode != "all": + data = data[data["mode"] == args.mode] + if args.n_products is not None: + data = data[data["n_products"] == float(args.n_products)] + if args.eta_ux is not None: + data = data[data["eta_ux"] == float(args.eta_ux)] + if args.lambda_coi is not None: + data = data[data["lambda_coi"] == float(args.lambda_coi)] + data = data[data["alpha"].notna() & data["eval_revenue_mean"].notna()] + data = data[data["alpha"] >= float(args.alpha_min)] + data = data[data["alpha"] <= float(args.alpha_max)] + return data.reset_index(drop=True) + + +def _design_matrix( + frame: pd.DataFrame, + *, + include_sweep_fixed_effects: bool, +) -> tuple[np.ndarray, np.ndarray, list[str]]: + y = frame["eval_revenue_mean"].to_numpy(dtype=float) + x_alpha = frame["alpha"].to_numpy(dtype=float) + columns = ["intercept", "alpha"] + blocks = [np.ones_like(x_alpha), x_alpha] + if include_sweep_fixed_effects: + dummies = pd.get_dummies( + frame["sweep_id"].astype(str), prefix="sweep", drop_first=True + ) + if not dummies.empty: + blocks.append(dummies.to_numpy(dtype=float).T) + columns.extend(dummies.columns.tolist()) + X = np.vstack(blocks).T + return X, y, columns + + +def _covariance_hc1(X: np.ndarray, residuals: np.ndarray) -> np.ndarray: + n, k = X.shape + xtx_inv = np.linalg.pinv(X.T @ X) + xr = X * residuals[:, None] + meat = xr.T @ xr + scale = float(n) / max(n - k, 1) + return scale * (xtx_inv @ meat @ xtx_inv) + + +def _covariance_cluster( + X: np.ndarray, residuals: np.ndarray, groups: pd.Series +) -> tuple[np.ndarray, int]: + xtx_inv = np.linalg.pinv(X.T @ X) + unique = pd.Series(groups).astype(str).dropna().unique().tolist() + g = len(unique) + n, k = X.shape + if g <= 1: + return _covariance_hc1(X, residuals), g + meat = np.zeros((k, k), dtype=float) + for value in unique: + mask = pd.Series(groups).astype(str).to_numpy() == value + Xg = X[mask] + ug = residuals[mask] + xu = Xg.T @ ug + meat += np.outer(xu, xu) + c = (g / (g - 1.0)) * ((n - 1.0) / max(n - k, 1.0)) + return c * (xtx_inv @ meat @ xtx_inv), g + + +def _fit_ols( + X: np.ndarray, + y: np.ndarray, + columns: list[str], + *, + cov_type: str, + groups: pd.Series | None = None, +) -> dict[str, object]: + n, k = X.shape + beta, _, _, _ = np.linalg.lstsq(X, y, rcond=None) + fitted = X @ beta + residuals = y - fitted + dof = max(n - k, 1) + sse = float(np.sum(residuals**2)) + y_centered = y - float(np.mean(y)) + sst = float(np.sum(y_centered**2)) + r2 = float(1.0 - sse / sst) if sst > 0 else 0.0 + adj_r2 = float(1.0 - (1.0 - r2) * ((n - 1.0) / max(n - k, 1.0))) + + if cov_type == "iid": + sigma2 = sse / dof + cov = sigma2 * np.linalg.pinv(X.T @ X) + df_t = dof + clusters = None + elif cov_type == "hc1": + cov = _covariance_hc1(X, residuals) + df_t = dof + clusters = None + elif cov_type == "cluster": + if groups is None: + raise ValueError("groups are required when cov_type='cluster'") + cov, clusters = _covariance_cluster(X, residuals, groups) + df_t = max(clusters - 1, 1) + else: + raise ValueError(f"Unsupported cov_type: {cov_type}") + + se = np.sqrt(np.clip(np.diag(cov), 0.0, np.inf)) + t_stats = np.divide(beta, se, out=np.zeros_like(beta), where=se > 0) + p_values = 2.0 * (1.0 - stats.t.cdf(np.abs(t_stats), df=df_t)) + t_crit = float(stats.t.ppf(0.975, df=df_t)) + ci_low = beta - t_crit * se + ci_high = beta + t_crit * se + + coef_rows = [] + for idx, name in enumerate(columns): + coef_rows.append( + { + "name": name, + "coef": float(beta[idx]), + "std_error": float(se[idx]), + "t_stat": float(t_stats[idx]), + "p_value": float(p_values[idx]), + "ci95_low": float(ci_low[idx]), + "ci95_high": float(ci_high[idx]), + } + ) + + return { + "n": int(n), + "k": int(k), + "dof": int(dof), + "df_t": int(df_t), + "cov_type": cov_type, + "clusters": int(clusters) if clusters is not None else None, + "r2": r2, + "adj_r2": adj_r2, + "sse": sse, + "coefficients": coef_rows, + "residuals": residuals, + "fitted": fitted, + "beta": beta, + } + + +def _diagnostics( + X: np.ndarray, y: np.ndarray, fit: dict[str, object] +) -> dict[str, object]: + residuals = np.asarray(fit["residuals"], dtype=float) + n, k = X.shape + if residuals.size < 8: + normality = {"test": "jarque_bera", "available": False} + else: + jb_stat, jb_p = stats.jarque_bera(residuals) + normality = { + "test": "jarque_bera", + "available": True, + "statistic": float(jb_stat), + "p_value": float(jb_p), + } + + if k <= 1: + hetero = {"test": "breusch_pagan", "available": False} + else: + u2 = residuals**2 + aux = _fit_ols(X, u2, [f"x{i}" for i in range(k)], cov_type="iid") + lm = float(len(u2) * float(aux["r2"])) + df_bp = k - 1 + p_bp = float(1.0 - stats.chi2.cdf(lm, df_bp)) + hetero = { + "test": "breusch_pagan", + "available": True, + "lm_stat": lm, + "df": int(df_bp), + "p_value": p_bp, + } + + xtx_inv = np.linalg.pinv(X.T @ X) + leverages = np.sum((X @ xtx_inv) * X, axis=1) + mse = float(np.sum(residuals**2) / max(n - k, 1)) + if mse <= 0: + cooks = np.zeros(n, dtype=float) + else: + denom = np.clip((1.0 - leverages) ** 2, 1e-10, np.inf) + cooks = ((residuals**2) / (k * mse)) * (leverages / denom) + + return { + "normality": normality, + "heteroskedasticity": hetero, + "influence": { + "max_leverage": float(np.max(leverages)) if leverages.size else 0.0, + "mean_leverage": float(np.mean(leverages)) if leverages.size else 0.0, + "high_leverage_threshold": float(2.0 * k / max(n, 1)), + "high_leverage_count": int(np.sum(leverages > (2.0 * k / max(n, 1)))), + "max_cooks_distance": float(np.max(cooks)) if cooks.size else 0.0, + "high_cooks_threshold": float(4.0 / max(n, 1)), + "high_cooks_count": int(np.sum(cooks > (4.0 / max(n, 1)))), + }, + } + + +def run(args: argparse.Namespace) -> list[Path]: + output_dir = Path(args.output_dir) + output_dir.mkdir(parents=True, exist_ok=True) + + runs = _load_runs(Path(args.bundle_dir)) + filtered = _apply_filters(runs, args) + if len(filtered) < 3: + raise ValueError("Filtered cohort must contain at least 3 rows") + if filtered["alpha"].nunique() < 2: + raise ValueError("Filtered cohort must contain at least 2 unique alpha values") + + filtered_csv = output_dir / "revenue_alpha_filtered.csv" + filtered.to_csv(filtered_csv, index=False) + + sample_accounting = { + "bundle_dir": str(Path(args.bundle_dir)), + "git_commit": _get_git_commit(), + "cohort_name": str(args.cohort_name), + "filters": { + "sweep_id": args.sweep_id, + "mode": args.mode, + "n_products": args.n_products, + "eta_ux": args.eta_ux, + "lambda_coi": args.lambda_coi, + "alpha_min": args.alpha_min, + "alpha_max": args.alpha_max, + }, + "n_rows": int(len(filtered)), + "n_sweeps": int(filtered["sweep_id"].nunique()), + "alpha_unique": sorted( + float(v) for v in filtered["alpha"].dropna().unique().tolist() + ), + "rows_by_sweep": filtered.groupby("sweep_id").size().astype(int).to_dict(), + "rows_by_mode": filtered.groupby("mode").size().astype(int).to_dict(), + } + sample_path = output_dir / "revenue_alpha_sample_accounting.json" + sample_path.write_text(json.dumps(sample_accounting, indent=2) + "\n") + + X_simple, y, cols_simple = _design_matrix( + filtered, include_sweep_fixed_effects=False + ) + fit_simple = _fit_ols(X_simple, y, cols_simple, cov_type="iid") + simple_path = output_dir / "revenue_alpha_simple_ols.json" + simple_path.write_text( + json.dumps( + { + k: v + for k, v in fit_simple.items() + if k not in {"residuals", "fitted", "beta"} + }, + indent=2, + ) + + "\n" + ) + + X_fe, y_fe, cols_fe = _design_matrix(filtered, include_sweep_fixed_effects=True) + cov_type = "cluster" if filtered["sweep_id"].nunique() > 1 else "hc1" + fit_fe = _fit_ols( + X_fe, y_fe, cols_fe, cov_type=cov_type, groups=filtered["sweep_id"] + ) + fe_path = output_dir / "revenue_alpha_fixed_effects.json" + fe_path.write_text( + json.dumps( + { + k: v + for k, v in fit_fe.items() + if k not in {"residuals", "fitted", "beta"} + }, + indent=2, + ) + + "\n" + ) + + per_sweep_rows: list[dict[str, float | str | int]] = [] + for sweep_id, group in filtered.groupby("sweep_id"): + if len(group) < 3 or group["alpha"].nunique() < 2: + continue + X_sw, y_sw, cols_sw = _design_matrix(group, include_sweep_fixed_effects=False) + fit_sw = _fit_ols(X_sw, y_sw, cols_sw, cov_type="hc1") + alpha_row = next( + row for row in fit_sw["coefficients"] if row["name"] == "alpha" + ) + per_sweep_rows.append( + { + "sweep_id": str(sweep_id), + "n": int(fit_sw["n"]), + "alpha_coef": float(alpha_row["coef"]), + "alpha_std_error": float(alpha_row["std_error"]), + "alpha_t_stat": float(alpha_row["t_stat"]), + "alpha_p_value": float(alpha_row["p_value"]), + "alpha_ci95_low": float(alpha_row["ci95_low"]), + "alpha_ci95_high": float(alpha_row["ci95_high"]), + "r2": float(fit_sw["r2"]), + } + ) + per_sweep_frame = pd.DataFrame(per_sweep_rows) + if not per_sweep_frame.empty: + per_sweep_frame = per_sweep_frame.sort_values("sweep_id").reset_index(drop=True) + per_sweep_path = output_dir / "revenue_alpha_per_sweep.csv" + per_sweep_frame.to_csv(per_sweep_path, index=False) + + fit_for_diagnostics = fit_fe if cov_type == "cluster" else fit_simple + X_for_diagnostics = X_fe if cov_type == "cluster" else X_simple + diagnostics = _diagnostics(X_for_diagnostics, y, fit_for_diagnostics) + diagnostics_path = output_dir / "revenue_alpha_diagnostics.json" + diagnostics_path.write_text(json.dumps(diagnostics, indent=2) + "\n") + + return [ + filtered_csv, + sample_path, + simple_path, + fe_path, + per_sweep_path, + diagnostics_path, + ] + + +def main() -> None: + parser = argparse.ArgumentParser( + description="Reproducible contamination-vs-revenue analysis from a sweep bundle" + ) + parser.add_argument("--bundle-dir", type=Path, default=None) + parser.add_argument("--bundle-id", type=str, default=None) + parser.add_argument("--output-dir", type=Path, default=_default_output_dir()) + parser.add_argument("--cohort-name", type=str, default="custom") + parser.add_argument("--sweep-id", action="append", default=[]) + parser.add_argument( + "--mode", choices=["all", "baseline", "defended"], default="all" + ) + parser.add_argument("--n-products", type=float, default=None) + parser.add_argument("--eta-ux", type=float, default=None) + parser.add_argument("--lambda-coi", type=float, default=None) + parser.add_argument("--alpha-min", type=float, default=0.0) + parser.add_argument("--alpha-max", type=float, default=1.0) + args = parser.parse_args() + + if args.bundle_id: + args.bundle_dir = _bundle_dir_from_id(args.bundle_id) + elif args.bundle_dir is None: + args.bundle_dir = _default_bundle_dir() + + outputs = run(args) + for path in outputs: + print(path) + + +if __name__ == "__main__": + main() diff --git a/paper/src/chapters/figures/results/revenue_alpha_classic.py b/paper/src/chapters/figures/results/revenue_alpha_classic.py new file mode 100644 index 0000000..a91f2f6 --- /dev/null +++ b/paper/src/chapters/figures/results/revenue_alpha_classic.py @@ -0,0 +1,63 @@ +from pathlib import Path + +import numpy as np +import pandas as pd +from scipy import stats + + +root = Path(__file__).resolve().parents[5] +runs = ( + root + / "engine/studies/results/wandb_sweep_bundles/bundle_20260317_122818/runs_finished.csv" +) + +df = pd.read_csv(runs) +df = df[ + (df["sweep_id"].astype(str) == "i88nw811") + & (df["study_mode"].astype(str) == "baseline") + & (pd.to_numeric(df["n_products"], errors="coerce") == 100.0) + & (pd.to_numeric(df["eta_ux"], errors="coerce") == 0.0) +].copy() + +alpha = pd.to_numeric(df["alpha"], errors="coerce") +revenue = pd.to_numeric(df["eval_revenue_mean"], errors="coerce") +mask = alpha.notna() & revenue.notna() +alpha = alpha[mask].to_numpy(dtype=float) +revenue = revenue[mask].to_numpy(dtype=float) + +if len(alpha) < 3 or np.unique(alpha).size < 2: + raise ValueError("Not enough data for regression") + +fit = stats.linregress(alpha, revenue) +n = len(alpha) +dof = n - 2 +t_stat = fit.slope / fit.stderr +p_val = 2.0 * stats.t.sf(abs(t_stat), df=dof) +r2 = fit.rvalue**2 +t_crit = stats.t.ppf(0.975, dof) +slope_ci = (fit.slope - t_crit * fit.stderr, fit.slope + t_crit * fit.stderr) + +x = np.column_stack([np.ones(n), alpha]) +beta = np.linalg.lstsq(x, revenue, rcond=None)[0] +resid = revenue - x @ beta +xtx_inv = np.linalg.pinv(x.T @ x) +meat = (x * resid[:, None]).T @ (x * resid[:, None]) +cov_hc1 = (n / (n - x.shape[1])) * (xtx_inv @ meat @ xtx_inv) +se_hc1 = np.sqrt(np.diag(cov_hc1)) +t_hc1 = beta[1] / se_hc1[1] +p_hc1 = 2.0 * stats.t.sf(abs(t_hc1), df=dof) +slope_ci_hc1 = (beta[1] - t_crit * se_hc1[1], beta[1] + t_crit * se_hc1[1]) + +print("Contamination-Revenue Slope") +print( + "cohort: bundle_20260317_122818, sweep=i88nw811, mode=baseline, n_products=100, eta_ux=0.0" +) +print(f"n={n}") +print(f"model: revenue = {fit.intercept:.2f} {fit.slope:+.2f} * alpha") +print( + f"OLS: t({dof})={t_stat:.2f}, p={p_val:.3e}, R^2={r2:.3f}, slope_95CI=[{slope_ci[0]:.2f}, {slope_ci[1]:.2f}]" +) +print( + f"HC1: t={t_hc1:.2f}, p={p_hc1:.3e}, slope_95CI=[{slope_ci_hc1[0]:.2f}, {slope_ci_hc1[1]:.2f}]" +) +print(f"effect: +0.1 alpha -> {0.1 * fit.slope:.2f} revenue units") From 220b6ce8c15aa48da8ea4537e3665c1d7166df51 Mon Sep 17 00:00:00 2001 From: Daniel Rosel Date: Mon, 23 Mar 2026 21:47:31 +0100 Subject: [PATCH 02/44] unified separability writing --- engine/lib/coi.py | 18 +++-- lib/separability.py | 88 ++++++++++++---------- sim/case/thesis_simplified/separability.py | 45 ++++++++--- sim/rl/jax_core/separability.py | 44 ++++++++--- 4 files changed, 129 insertions(+), 66 deletions(-) diff --git a/engine/lib/coi.py b/engine/lib/coi.py index ed18672..7b6ecd2 100644 --- a/engine/lib/coi.py +++ b/engine/lib/coi.py @@ -1,12 +1,15 @@ import numpy as np from typing import Dict +from lib.agent_probability import DEFAULT_AGENT_PRIOR, estimate_agent_probability + def compute_agent_probability( trajectory: list, human_transitions: Dict, agent_transitions: Dict, temperature: float = 1.0, + prior_agent: float = DEFAULT_AGENT_PRIOR, ) -> float: """estimate agent probability via KL divergence between trajectory transitions and reference models @@ -18,10 +21,10 @@ def compute_agent_probability( agent_transitions: reference transition dict from agent MDP (event->event->prob) returns: - agent probability in [0, 1] via softmax over KL divergences + agent probability in [0, 1] via sigma((delta_h - delta_a) / T) """ if len(trajectory) < 2: - return 0.0 # insufficient data, assume human + return float(prior_agent) # build empirical transition distribution from trajectory trans_counts = {} @@ -54,11 +57,12 @@ def compute_agent_probability( kl_human = kl_div(empirical, human_transitions) kl_agent = kl_div(empirical, agent_transitions) - # convert to probability via softmax (lower KL = higher prob) - t = float(max(temperature, 1e-6)) - exp_h = np.exp(-kl_human / t) - exp_a = np.exp(-kl_agent / t) - return float(exp_a / (exp_h + exp_a + 1e-10)) + return estimate_agent_probability( + delta_h=kl_human, + delta_a=kl_agent, + temperature=temperature, + prior_agent=prior_agent, + ) def extract_purchases(trajectories: list) -> Dict[int, int]: diff --git a/lib/separability.py b/lib/separability.py index a93ddeb..410d7db 100644 --- a/lib/separability.py +++ b/lib/separability.py @@ -7,10 +7,9 @@ from dataclasses import dataclass from pathlib import Path from typing import Dict, Iterable, List, Sequence -import joblib import numpy as np -from experiments.ml.arch import featurize_trajectory +from lib.agent_probability import DEFAULT_AGENT_PRIOR, estimate_agent_probability DEFAULT_ARTIFACT_DIR = Path("data/separability") @@ -18,11 +17,7 @@ DEFAULT_ARTIFACT_DIR = Path("data/separability") @dataclass class SeparabilityArtifacts: - scaler: object - classifier: object - states: List[str] event_transitions: Dict[str, Dict[str, float]] - feature_dim: int def _normalize_events(raw_events: Sequence[object]) -> List[object]: @@ -36,7 +31,9 @@ def _normalize_events(raw_events: Sequence[object]) -> List[object]: return events -def _event_transition_distribution(events: Sequence[object]) -> Dict[str, Dict[str, float]]: +def _event_transition_distribution( + events: Sequence[object], +) -> Dict[str, Dict[str, float]]: counts: Dict[str, Dict[str, int]] = {} for src_evt, dst_evt in zip(events, events[1:]): src_name = getattr(src_evt, "eventName", "unknown") @@ -47,11 +44,15 @@ def _event_transition_distribution(events: Sequence[object]) -> Dict[str, Dict[s distribution: Dict[str, Dict[str, float]] = {} for src, dsts in counts.items(): total = float(sum(dsts.values())) - distribution[src] = {dst: val / total for dst, val in dsts.items()} if total else {} + distribution[src] = ( + {dst: val / total for dst, val in dsts.items()} if total else {} + ) return distribution -def _kl_divergence(p: Dict[str, Dict[str, float]], q: Dict[str, Dict[str, float]]) -> float: +def _kl_divergence( + p: Dict[str, Dict[str, float]], q: Dict[str, Dict[str, float]] +) -> float: eps = 1e-10 total = 0.0 for src, dsts in p.items(): @@ -61,28 +62,28 @@ def _kl_divergence(p: Dict[str, Dict[str, float]], q: Dict[str, Dict[str, float] return float(total) -def load_artifacts(artifact_dir: Path | str = DEFAULT_ARTIFACT_DIR) -> SeparabilityArtifacts: +def load_artifacts( + artifact_dir: Path | str = DEFAULT_ARTIFACT_DIR, +) -> SeparabilityArtifacts: artifact_dir = Path(artifact_dir) - scaler_path = artifact_dir / "scaler.joblib" - model_path = artifact_dir / "classifier.joblib" metadata_path = artifact_dir / "metadata.json" - if not (scaler_path.exists() and model_path.exists() and metadata_path.exists()): + if not metadata_path.exists(): raise FileNotFoundError( - f"Separability artifacts not found in {artifact_dir}. Run sim.strong_learner.train first." + f"Separability metadata not found in {artifact_dir}. Provide metadata.json with event transitions." ) - scaler = joblib.load(scaler_path) - classifier = joblib.load(model_path) with open(metadata_path, "r", encoding="utf-8") as fin: metadata = json.load(fin) + transitions = metadata.get("event_transitions") + if not isinstance(transitions, dict): + raise ValueError( + "metadata.json must contain an 'event_transitions' object with 'human' and 'agent' kernels" + ) + return SeparabilityArtifacts( - scaler=scaler, - classifier=classifier, - states=list(metadata["reference_states"]), - event_transitions=metadata["event_transitions"], - feature_dim=int(metadata["feature_dim"]), + event_transitions=transitions, ) @@ -92,37 +93,44 @@ def score_session( ) -> dict: events = _normalize_events(raw_events) if not events: - return {"prob_agent": 0.0, "delta_h": 0.0, "delta_a": 0.0} - - reference_mdp = {"states": artifacts.states} - features = featurize_trajectory(events, mdp=reference_mdp, input_dim=artifacts.feature_dim) - scaled = artifacts.scaler.transform(features.reshape(1, -1)) - prob_agent = float(artifacts.classifier.predict_proba(scaled)[0, 1]) + return { + "prob_agent": float(DEFAULT_AGENT_PRIOR), + "delta_h": 0.0, + "delta_a": 0.0, + "gap": 0.0, + } session_dist = _event_transition_distribution(events) delta_h = _kl_divergence(session_dist, artifacts.event_transitions.get("human", {})) delta_a = _kl_divergence(session_dist, artifacts.event_transitions.get("agent", {})) + gap = float(delta_h - delta_a) + prob_agent = estimate_agent_probability(delta_h=delta_h, delta_a=delta_a) return { "prob_agent": prob_agent, "delta_h": delta_h, "delta_a": delta_a, + "gap": gap, } -def estimate_alpha(prob_agent: float, delta_h: float, delta_a: float, temperature: float = 1.0) -> float: - divergence_mass = delta_h + delta_a - if divergence_mass <= 1e-8: - return float(prob_agent) - - ratio = delta_a / divergence_mass - blended = 0.5 * prob_agent + 0.5 * ratio - if temperature <= 0: - return float(np.clip(blended, 0.0, 1.0)) - - scaled = 1.0 / (1.0 + np.exp(-temperature * (blended - 0.5))) - return float(np.clip(scaled, 0.0, 1.0)) +def estimate_alpha( + prob_agent: float, + delta_h: float, + delta_a: float, + temperature: float = 1.0, + prior_agent: float = DEFAULT_AGENT_PRIOR, +) -> float: + _ = prob_agent + return estimate_agent_probability( + delta_h=delta_h, + delta_a=delta_a, + temperature=temperature, + prior_agent=prior_agent, + ) -def score_sessions(raw_sessions: Iterable[Sequence[object]], artifacts: SeparabilityArtifacts) -> List[dict]: +def score_sessions( + raw_sessions: Iterable[Sequence[object]], artifacts: SeparabilityArtifacts +) -> List[dict]: return [score_session(events, artifacts) for events in raw_sessions] diff --git a/sim/case/thesis_simplified/separability.py b/sim/case/thesis_simplified/separability.py index eaabaa3..74ece46 100644 --- a/sim/case/thesis_simplified/separability.py +++ b/sim/case/thesis_simplified/separability.py @@ -3,10 +3,13 @@ Computes divergence signals delta_H, delta_A from session trajectories using transition kernel estimation and KL divergence to prototype behavioral profiles. """ + from __future__ import annotations from typing import Dict, List, Tuple, TYPE_CHECKING import numpy as np +from lib.agent_probability import DEFAULT_AGENT_PRIOR, estimate_agent_probability + if TYPE_CHECKING: from .simplified import Event, Session @@ -32,7 +35,10 @@ TRANS_A = { def kl_div(p: Dict[str, float], q: Dict[str, float], eps: float = 1e-10) -> float: """KL divergence D_KL(p || q) for discrete distributions.""" keys = set(p.keys()) | set(q.keys()) - return sum(p.get(k, eps) * np.log((p.get(k, eps) + eps) / (q.get(k, eps) + eps)) for k in keys) + return sum( + p.get(k, eps) * np.log((p.get(k, eps) + eps) / (q.get(k, eps) + eps)) + for k in keys + ) def build_kernel(events: List["Event"]) -> Dict[str, Dict[str, float]]: @@ -44,7 +50,11 @@ def build_kernel(events: List["Event"]) -> Dict[str, Dict[str, float]]: trans.setdefault(prev, {}) trans[prev][curr] = trans[prev].get(curr, 0) + 1 prev = curr - return {s: {d: c / sum(dsts.values()) for d, c in dsts.items()} for s, dsts in trans.items() if sum(dsts.values()) > 0} + return { + s: {d: c / sum(dsts.values()) for d, c in dsts.items()} + for s, dsts in trans.items() + if sum(dsts.values()) > 0 + } def compute_divergence(session: "Session") -> Tuple[float, float]: @@ -55,18 +65,35 @@ def compute_divergence(session: "Session") -> Tuple[float, float]: """ kernel = build_kernel(session.events) if not kernel: - return 0.5, 0.5 - delta_h = sum(kl_div(kernel.get(s, {}), TRANS_H.get(s, {})) for s in kernel) / len(kernel) - delta_a = sum(kl_div(kernel.get(s, {}), TRANS_A.get(s, {})) for s in kernel) / len(kernel) + return 0.0, 0.0 + delta_h = sum(kl_div(kernel.get(s, {}), TRANS_H.get(s, {})) for s in kernel) / len( + kernel + ) + delta_a = sum(kl_div(kernel.get(s, {}), TRANS_A.get(s, {})) for s in kernel) / len( + kernel + ) return delta_h, delta_a -def estimate_alpha(session: "Session", beta: float = 2.0) -> float: - """Per-session contamination estimate alpha_hat = sigma(beta*(delta_H - delta_A)). +def estimate_alpha( + session: "Session", + beta: float = 2.0, + prior_agent: float = DEFAULT_AGENT_PRIOR, +) -> float: + """Per-session contamination estimate alpha_hat = sigma((delta_H - delta_A) / T). Returns probability session is agent-generated based on behavioral divergence. """ dh, da = compute_divergence(session) if (dh + da) <= 0: - return 0.5 - return 1.0 / (1.0 + np.exp(-beta * (dh - da))) + return float(prior_agent) + if beta <= 0: + return estimate_agent_probability( + dh, da, temperature=1.0, prior_agent=prior_agent + ) + return estimate_agent_probability( + delta_h=dh, + delta_a=da, + temperature=1.0 / beta, + prior_agent=prior_agent, + ) diff --git a/sim/rl/jax_core/separability.py b/sim/rl/jax_core/separability.py index c0c0293..c4165a7 100644 --- a/sim/rl/jax_core/separability.py +++ b/sim/rl/jax_core/separability.py @@ -1,14 +1,24 @@ """Vectorized KL divergence for separability scoring.""" + import numpy as np from typing import Tuple +from lib.agent_probability import ( + DEFAULT_AGENT_PRIOR, + estimate_agent_probability_batch, +) + try: import jax.numpy as jnp from jax import jit + JAX_AVAILABLE = True except ImportError: jnp, JAX_AVAILABLE = np, False - def jit(f): return f + + def jit(f): + return f + @jit def batch_kl(P, Q_human, Q_agent, eps=1e-10): @@ -20,10 +30,15 @@ def batch_kl(P, Q_human, Q_agent, eps=1e-10): delta_a = jnp.sum(p * jnp.log(p / qa), axis=(1, 2)) return delta_h, delta_a -def compute_divergences(session_trans: np.ndarray, ref_human: np.ndarray, ref_agent: np.ndarray) -> Tuple[np.ndarray, np.ndarray]: + +def compute_divergences( + session_trans: np.ndarray, ref_human: np.ndarray, ref_agent: np.ndarray +) -> Tuple[np.ndarray, np.ndarray]: """Compute KL divergence of each session from human/agent prototypes.""" if JAX_AVAILABLE: - dh, da = batch_kl(jnp.array(session_trans), jnp.array(ref_human), jnp.array(ref_agent)) + dh, da = batch_kl( + jnp.array(session_trans), jnp.array(ref_human), jnp.array(ref_agent) + ) return np.asarray(dh), np.asarray(da) # numpy fallback eps = 1e-10 @@ -34,10 +49,19 @@ def compute_divergences(session_trans: np.ndarray, ref_human: np.ndarray, ref_ag delta_a = np.sum(p * np.log(p / qa), axis=(1, 2)) return delta_h, delta_a -def estimate_alpha_batch(prob_agent: np.ndarray, delta_h: np.ndarray, delta_a: np.ndarray, temp: float = 1.0) -> np.ndarray: - """Vectorized alpha estimation from classifier probs and divergences.""" - mass = delta_h + delta_a - ratio = np.where(mass > 1e-8, delta_a / mass, 0.5) - blended = 0.5 * prob_agent + 0.5 * ratio - if temp <= 0: return np.clip(blended, 0.0, 1.0) - return np.clip(1.0 / (1.0 + np.exp(-temp * (blended - 0.5))), 0.0, 1.0) + +def estimate_alpha_batch( + prob_agent: np.ndarray, + delta_h: np.ndarray, + delta_a: np.ndarray, + temp: float = 1.0, + prior_agent: float = DEFAULT_AGENT_PRIOR, +) -> np.ndarray: + """Vectorized alpha estimation using divergence gap mapping.""" + _ = prob_agent + return estimate_agent_probability_batch( + delta_h=np.asarray(delta_h, dtype=float), + delta_a=np.asarray(delta_a, dtype=float), + temperature=temp, + prior_agent=prior_agent, + ) From 105b01497600fd31ec07ae49271e680517321cba Mon Sep 17 00:00:00 2001 From: Daniel Rosel Date: Mon, 23 Mar 2026 21:47:45 +0100 Subject: [PATCH 03/44] feat: initial paper update remarks --- paper/src/auto/main.el | 6 ++++- paper/src/chapters/03-methodology.tex | 33 ++++++++++++++++++++++++--- paper/src/chapters/04-results.tex | 8 +++++-- paper/src/main.tex | 13 ----------- 4 files changed, 41 insertions(+), 19 deletions(-) diff --git a/paper/src/auto/main.el b/paper/src/auto/main.el index a64eef1..31e5a9a 100644 --- a/paper/src/auto/main.el +++ b/paper/src/auto/main.el @@ -17,6 +17,10 @@ "chapters/05-discussion" "chapters/06-conclusion" "article" - "art12")) + "art12") + (LaTeX-add-labels + "app:compute_budget" + "tab:compute_derivation" + "app:whoclicked_card")) :latex) diff --git a/paper/src/chapters/03-methodology.tex b/paper/src/chapters/03-methodology.tex index e07dcac..a8acbeb 100644 --- a/paper/src/chapters/03-methodology.tex +++ b/paper/src/chapters/03-methodology.tex @@ -94,7 +94,8 @@ where $\mathbb{E}[P]$ is the expected price charged by the policy and $\underlin We now formally demonstrate that standard dynamic pricing mechanisms are not incentive-compatible with high-frequency agentic traffic. As the number of independent competitive agents $N$ querying the system grows, the platform's ability to sustain a COI vanishes. - A fundamental assumption for our claim lies in the alignment of the AI agent through its prompt which has been demonstrated by \cite{fish_algorithmic_2025} to cause strong collusive behavior under linguistic nudges. This assumption can be generalized to the human user asking the agent to research products with a minimizing objective. +\paragraph{Assumption Scope} +The theorem and core experiments in this thesis assume a non-collusive independent-session setting: each agent queries prices independently and does not share sampled quotes across agents. Collusive coordination is outside the current proof scope and is treated as an extension scenario. \begin{theorem}[COI Erosion in the Limit] Let $N$ be the number of independent, utility-maximizing agents querying the platform. Let $p_{(1)}$ be the first order statistic (minimum) of the prices offered to these agents. As $N \to \infty$, the Cost of Information converges to 0. @@ -331,7 +332,7 @@ where $\mathcal{S}_e$ denotes the set of destination events that follow $e$ in t To obtain this statistic, we aggregate transitions by triggering event $e$ and treat normalized outgoing probabilities as categorical distributions $P_e$ (human) and $Q_e$ (agent). We intersect shared event labels, then accumulate log-ratio contributions over shared destinations. Large contributions, including near-zero $Q_e(k)$ cases, identify transitions where one actor class is difficult to mimic. -With these divergence features we train a contrastive model to estimate a weak agent probability $f(\tau)\in[0,1]$, which we later use as a weighting and control signal. +With these divergence features we compute a weak agent probability $f(\tau')\in[0,1]$ directly from divergence gaps, which we later use as a weighting and control signal. \subsubsection{Transition Probability Estimation} @@ -375,10 +376,36 @@ Because contamination level $\alpha$ and demand shift are non-stationary online, \Delta_A &= D_{KL}(\hat{\mathcal{T}}^\prime \parallel \bar{\mathcal{T}}_A) \end{align} -This yields two centroid-like heuristics that act as a session-level agent score in the engine. On a per-customer or use-case basis a similar study should be done in order to obtain ground truth behavior models for humans and agents and their specific interaction with a given products website. +From these two divergences we define the gap score: +\begin{equation} +g(\tau') := \Delta_H(\tau') - \Delta_A(\tau'). +\end{equation} +Positive values indicate trajectories farther from the human centroid and closer to the agent centroid. + +We map this gap to a weak agent probability using a temperature-controlled logistic map: +\begin{equation} +f(\tau') := P(Y=A\mid\tau') = \operatorname{softmax}(-\Delta_A,-\Delta_H)_A = \sigma\left(\frac{\Delta_H-\Delta_A}{T}\right), \quad T>0. +\end{equation} +The session-level control signal injected into pricing is then +\begin{equation} +\hat{\alpha}(\tau') := f(\tau'). +\end{equation} + +This turns distinguishability into an operational control input in the engine. On a per-customer or use-case basis, a similar data collection and fitting process should be repeated to obtain domain-specific behavior kernels. In implementation, we maintain an alternating game-history stack (our \textit{Limbo} stack) and execute it explicitly every epoch with exactly two transitions: first the platform publishes a price vector (leader move), then the market responds with trajectory-derived demand (follower move). +To avoid notation drift, we separate two COI objects used for different purposes: +\begin{align} +\text{COI}_{\text{level}}(\pi) &:= \mathbb{E}[P]-\underline{p} \quad \text{(global reporting KPI)} \\ +\text{COI}_{\text{leak}}(p,\tau') &:= f(\tau')\cdot \text{InfoValue}(p,\tau') \quad \text{(local control penalty)} +\end{align} +where $\text{COI}_{\text{level}}$ is evaluated at policy level and $\text{COI}_{\text{leak}}$ is evaluated per observed quote during training. We connect local leakage to expected global erosion with the operational assumption +\begin{equation} +\mathbb{E}[\Delta\text{COI}_{\text{level},t} \mid \tau_t'] \approx -\kappa\,\text{COI}_{\text{leak}}(p_t,\tau_t') + \xi_t, +\end{equation} +where $\kappa>0$ and $\xi_t$ is residual noise. This keeps theorem-level COI erosion (global, asymptotic) distinct from training-time leakage control (local surrogate). + % Mention discretized action space and the clipping and over shotting in continuous action spaces % Also talk about catastrophic economics, we add termination on bankrupcy or zero demand so market collaps diff --git a/paper/src/chapters/04-results.tex b/paper/src/chapters/04-results.tex index f1e4f56..45208ae 100644 --- a/paper/src/chapters/04-results.tex +++ b/paper/src/chapters/04-results.tex @@ -40,7 +40,12 @@ We report two preliminary stages before the full factorial interpretation. First \subsubsection{The Impact of Contamination on Revenue} -A linear fit test on run-level data ($n=95$) shows a strong negative association between contamination and mean revenue. The fitted model mapping $\alpha \to \text{revenue}$ result in $t(93)=-8.2148$, $p=1.20\times 10^{-12}$, $R^2=0.4205$, and a 95\% confidence interval for the slope of $[-75{,}288.76,\,-45{,}975.13]$. In practical terms, a $+0.1$ increase in $\alpha$ corresponds to an average decrease of about $6{,}063$ revenue units within our environment. +The contamination--revenue slope is estimated on a controlled cohort (single sweep, baseline policy, $n_{\text{products}}=100$, $n=95$). In this setting, contamination $\alpha$ is set exogenously by the experiment, so the slope identifies the within-sweep causal effect of contamination on revenue under fixed policy and environment settings. The fitted linear model is + +\[ +\widehat{y}=348{,}823.41-90{,}140.53\,\alpha, +\] +with $t(93)=-61.45$, $p=4.27\times10^{-77}$, $R^2=0.976$, and a 95\% confidence interval for the slope of $[-93{,}053.38,\,-87{,}227.68]$. Interpreted on the contamination grid, a $+0.1$ increase in $\alpha$ corresponds to an average revenue decrease of about $9{,}014$ units. A heteroskedasticity-robust check (HC1) preserves the same direction and significance ($t=-41.25$, $p=1.42\times10^{-61}$), supporting a large and statistically stable impact in this controlled regime. \subsubsection{Large Scale Factorial Training} @@ -58,7 +63,6 @@ In our complete training runs we logged $\approx 180$ days of net compute time. \caption{Revenue curves by contamination for the final cohort. The baseline remains above the defended curve in most cells, but the gap narrows in the high-contamination region.} \label{fig:final_focus_revenue_by_alpha} \end{figure} -% TODO: we need a similar plot which shows the COI preserved (what we gain across teh multiple conatmination leves, showing that the robust method has better COI optimization.) \begin{figure}[ht] \centering diff --git a/paper/src/main.tex b/paper/src/main.tex index f31edd9..555fbc3 100644 --- a/paper/src/main.tex +++ b/paper/src/main.tex @@ -110,19 +110,6 @@ v4 & 64 & 275 & $64 \times 275 = 17{,}600$ \\ Converting to petaFLOPS: $160{,}320\;\text{TFLOPS} = 160.32\;\text{PFLOPS} \approx 160\;\text{PFLOPS}$. This is the theoretical peak under sustained BF16 arithmetic; realized throughput depends on memory bandwidth utilization and inter-chip communication overhead, but the figure serves as a useful upper bound for provisioning decisions. -\section{Slope-Test Verification: Revenue vs. Contamination} -\label{app:alpha_revenue_slope} - -This appendix provides a compact verification of the slope result reported in the main results section. Using the same run-level pairs $x_i=\texttt{study/alpha}_i$ and $y_i=\texttt{eval/revenue\_mean}_i$ ($n=95$), we re-checked the ordinary least squares slope test in Python with standard test routines (SciPy two-sided $t$ test for the slope). - -\[ -\widehat{y}=326{,}878.57-60{,}631.95\,x, -\] -\[ -t(93)=-8.2148,\qquad p=1.2038\times 10^{-12},\qquad R^2=0.4205,\qquad 95\%\,\text{CI}_{\beta_1}=[-75{,}288.76,\,-45{,}975.13]. -\] - -The Python verification reproduces the reported coefficients and inference values, confirming that the slope-test results are correct under standard methods. \section{whoclickedit Dataset Card} \label{app:whoclicked_card} From 18b41ff8025fa343997e459836e01b4f2784f38b Mon Sep 17 00:00:00 2001 From: Daniel Rosel Date: Fri, 27 Mar 2026 16:58:41 +0100 Subject: [PATCH 04/44] banner plot and mehtodlogy updates --- docs/static/images/banner.svg | 4 +- paper/src/chapters/03-methodology.tex | 11 +- paper/src/chapters/04-results.tex | 14 + .../final/final_focus_headline_summary.json | 4 +- .../plots/final_focus_revenue_by_alpha.pdf | Bin 17518 -> 17518 bytes .../final/plots/final_focus_revenue_delta.pdf | Bin 19801 -> 19801 bytes .../final/plots/final_focus_risk_deltas.pdf | Bin 19550 -> 19550 bytes .../figures/results/process_final_sweeps.py | 270 ++++++++++++++++++ 8 files changed, 295 insertions(+), 8 deletions(-) diff --git a/docs/static/images/banner.svg b/docs/static/images/banner.svg index 38d501e..517491c 100644 --- a/docs/static/images/banner.svg +++ b/docs/static/images/banner.svg @@ -41,7 +41,7 @@ - p + p E[P] @@ -49,7 +49,7 @@ average information rent - COI := E[P] - p + COI := E[P] - p diff --git a/paper/src/chapters/03-methodology.tex b/paper/src/chapters/03-methodology.tex index a8acbeb..8c58717 100644 --- a/paper/src/chapters/03-methodology.tex +++ b/paper/src/chapters/03-methodology.tex @@ -23,7 +23,7 @@ where: The platform does not directly observe the true underlying demand function $d(p)$. Instead, it observes a behavioral proxy $\hat{q}_t$, which is a composite signal derived from the mixture of actor types. We define the demand proxy for product $i$ at epoch $t$ as a weighted aggregation of events: \begin{equation} \label{eq:qhat} -\hat{q}_{t,i} = \sum_{s \in \mathcal{S}_t} \sum_{k=1}^{L_s} \omega(a_{s,k}) \cdot \mathbb{1}[i_{s,k} = i] +\hat{q}_{t,i} = \sum_{s \in \mathcal{S}_t} \sum_{k=1}^{L_s} \omega(a_{s,k}) \cdot \mathds{1}[i_{s,k} = i] \end{equation} where $\omega: \mathcal{A} \to \mathbb{R}_+$ assigns weights to actions based on their signal strength regarding willingness to pay. @@ -318,7 +318,7 @@ To train a robust pricing learner, we need a simulator that can generate realist \subsubsection{Ground-Truth Distinguishability} Because sessions are collected under controlled experimental conditions where each actor is assigned a known type at the start of the trial, labels $\theta_s \in \{H, A\}$ are available as ground truth rather than as the output of a heuristic classifier. We therefore estimate separate transition kernels directly from each labeled partition $\mathcal{D}_H$ and $\mathcal{D}_A$, treating the resulting $\hat{\mathcal{T}}_H$ and $\hat{\mathcal{T}}_A$ as the ground-truth behavioral profiles for each class. We then ask a direct methodological question: are the kernels distinguishable enough to justify downstream pricing control that depends on that distinguishability? -To answer this, we compute per-session KL divergence scores against both class-level centroids. For each session $s$ in either partition, we fit a session-level event transition kernel $\hat{\mathcal{T}}_s$ from that session's trajectory alone, then compute its average KL divergence to the human centroid ($\Delta_{H,s}$) and to the agent centroid ($\Delta_{A,s}$). The per-session distinguishability score is the gap $\Delta_{H,s} - \Delta_{A,s}$: a negative value indicates proximity to human behavior, a positive value indicates proximity to agent behavior. +To answer this, we compute per-session KL divergence scores against both class-level centroids. For each session $s$ in either partition, we fit a session-level event transition kernel $\hat{\mathcal{T}}_s$ from that session's trajectory alone, then compute its average KL divergence to the human centroid ($\Delta_{H,s}$) and to the agent centroid ($\Delta_{A,s}$). The per-session distinguishability score is the gap $\Delta_{H,s} - \Delta_{A,s}$: a negative value indicates proximity to human behavior, a positive value indicates proximity to agent behavior. The reason behind KL divergence for profile analysis is grounded in its nature and tailored characteristics for probability distributions. The normality assumption cannot be made for KL divergence distributions, which are right-skewed and bounded below by zero, so we do not use a Student's $t$-test. Instead we apply a Mann-Whitney $U$ test \parencite{mann_test_1947} on the per-session gap scores between the two groups. The Mann-Whitney test is a rank-based nonparametric test that compares the stochastic ordering of two independent samples without distributional assumptions, making it appropriate for small samples drawn from skewed populations. We report $U$, the exact two-sided $p$-value, and group-level descriptive statistics for the gap scores. @@ -471,7 +471,8 @@ The robust policy $\pi^*$ is obtained by solving the maximin problem: \label{eq:robust_policy} \pi^* = \arg \max_{\pi} \min_{Q \in \mathcal{U}_\epsilon} \mathbb{E}_{d \sim Q} \left[ R(p, d) - \lambda \cdot \text{COI}_{\text{leak}}(p,\tau') \right] \end{equation} -where $R(p, d)$ is the revenue function and $\lambda$ weighs the information-leakage penalty. +where $R(p, d)$ is the revenue function and $\lambda$ weighs the information-leakage penalty. We note that $p$ is directly dependent on $\pi$ which is the one deicing that as its action. + In practice, we parameterize this with a session-level leakage term: \begin{equation} @@ -479,6 +480,8 @@ In practice, we parameterize this with a session-level leakage term: \end{equation} where $f(\tau')$ is the weak agent probability and $\text{InfoValue}$ is implemented either as a constant query-tax surrogate or as a revelation surrogate $-\log\pi(p\mid\tau')$. +To make the intuition of our $\max \min$ easier in connection to the COI term which we are subtracting, we introduce the strongest possible penalization and try to maximize only for the worst case scenario in which the leakage is extremely high and that negation sends a signal to pick the candidate of the hardest problem. + For the baseline engine reported here, we intentionally use the constant query-tax surrogate to keep the mechanism minimal: \begin{equation} r_t = R(p_t,\tilde q_t) - \lambda\,f(\tau_t')\,c_{\text{info}} @@ -501,7 +504,7 @@ As part of reward engineering, we keep a UX factor ($UX\in[0,1]$) as an auxiliar \resizebox{0.5\columnwidth}{!}{% \input{chapters/balance_figure.tex} } - \caption{Introducing the UX index allows us to better distinguish the kind of impact different methods have and allows us to compare them on this Pareto-like scale.} + \caption{Introducing the UX index allows us to better distinguish the kind of impact different methods have and allows us to compare them on this Pareto-efficiency-like scale.} \end{figure} We also consider taxation-like overlays for agent traffic under strategy-proof mechanism design (e.g., Vickrey-Clarke-Groves style rules). This remains an extension path and is not part of the main implementation in this thesis. diff --git a/paper/src/chapters/04-results.tex b/paper/src/chapters/04-results.tex index 45208ae..fe22c49 100644 --- a/paper/src/chapters/04-results.tex +++ b/paper/src/chapters/04-results.tex @@ -64,6 +64,20 @@ In our complete training runs we logged $\approx 180$ days of net compute time. \label{fig:final_focus_revenue_by_alpha} \end{figure} +\begin{figure}[ht] + \centering + \input{chapters/figures/results/includes/final/final_focus_coi_by_alpha.tex} + \caption{COI level curves by contamination for the final cohort. The shaded band marks the per-$\alpha$ gap between defended and baseline policies.} + \label{fig:final_focus_coi_by_alpha} +\end{figure} + +\begin{figure}[ht] + \centering + \input{chapters/figures/results/includes/final/final_focus_coi_preservation_grid.tex} + \caption{COI preservation by product count at the contamination endpoints ($\alpha=0.0$ and $\alpha=1.0$). Bars report defended-minus-baseline mean COI level, with the zero line separating preservation from erosion.} + \label{fig:final_focus_coi_preservation_grid} +\end{figure} + \begin{figure}[ht] \centering \input{chapters/figures/results/includes/final/final_focus_revenue_delta.tex} diff --git a/paper/src/chapters/figures/results/generated/final/final_focus_headline_summary.json b/paper/src/chapters/figures/results/generated/final/final_focus_headline_summary.json index 3ab4253..a7b1fd1 100644 --- a/paper/src/chapters/figures/results/generated/final/final_focus_headline_summary.json +++ b/paper/src/chapters/figures/results/generated/final/final_focus_headline_summary.json @@ -1,9 +1,9 @@ { - "bundle": "engine/studies/results/wandb_sweep_bundles/bundle_20260317_122818", + "bundle": "/home/velocitatem/Documents/Projects/PHANTOM/engine/studies/results/wandb_sweep_bundles/bundle_20260317_122818", "focus_cohort": "max_alpha_coverage", "focus_sweep_id": "i88nw811", "focus_run_count": 768, - "git_commit": "e62e842faad79b143f5555d187075e85c8926363", + "git_commit": "105b01497600fd31ec07ae49271e680517321cba", "alpha_cells": 11, "alpha_min": 0.0, "alpha_max": 1.0, diff --git a/paper/src/chapters/figures/results/generated/final/plots/final_focus_revenue_by_alpha.pdf b/paper/src/chapters/figures/results/generated/final/plots/final_focus_revenue_by_alpha.pdf index 0ec609b669ef8a4a4c7e4ae917a01866e3e1a25a..61c028f71ee52fe466718cdd5e444df883a7bd98 100644 GIT binary patch delta 22 ecmaFY!T7F&al;aOc5_2h6H8OW&8zJXvj6~S;s{*; delta 22 ecmaFY!T7F&al;aOc4I?JBXc9m&8zJXvj6~S(+FMw diff --git a/paper/src/chapters/figures/results/generated/final/plots/final_focus_revenue_delta.pdf b/paper/src/chapters/figures/results/generated/final/plots/final_focus_revenue_delta.pdf index 5bbe5614d06e1f1d10f47c254233b211a5010354..b5b945d25198027cc7a1498417a047befccfd3c3 100644 GIT binary patch delta 22 ecmcaPi}B_x#trY?+06}2O)O1~Hh*(ZW(5FkqX?e> delta 22 ecmcaPi}B_x#trY?*^Lb?jm(WKH-B?aW(5FkjtHLs diff --git a/paper/src/chapters/figures/results/generated/final/plots/final_focus_risk_deltas.pdf b/paper/src/chapters/figures/results/generated/final/plots/final_focus_risk_deltas.pdf index 422a81680a4d0c92e8927ae324574f3ef157864c..9e5a5431d6e41b0fab80e603058393c3e10dde0a 100644 GIT binary patch delta 22 dcmcaNgYn)B#tj_q?B<51CYGi~oB7@SSpi(t2EzaV delta 22 dcmcaNgYn)B#tj_q?8b(cM&?GAoB7@SSpi(Y2EzaV diff --git a/paper/src/chapters/figures/results/process_final_sweeps.py b/paper/src/chapters/figures/results/process_final_sweeps.py index 63270d1..3dc5c0a 100644 --- a/paper/src/chapters/figures/results/process_final_sweeps.py +++ b/paper/src/chapters/figures/results/process_final_sweeps.py @@ -210,6 +210,48 @@ def _zone_summary(alpha_deltas: pd.DataFrame) -> pd.DataFrame: ) +def _alpha_product_coi_preservation(runs: pd.DataFrame) -> pd.DataFrame: + grouped = ( + runs.groupby(["alpha", "n_products", "mode"], as_index=False) + .agg( + runs=("run_id", "size"), + coi_level_mean=("eval_coi_level_mean", "mean"), + ) + .sort_values(["alpha", "n_products", "mode"]) + .reset_index(drop=True) + ) + + rows: list[dict[str, float | int]] = [] + for (alpha, n_products), group in grouped.groupby( + ["alpha", "n_products"], sort=True + ): + defended = group[group["mode"] == "defended"] + baseline = group[group["mode"] == "baseline"] + if defended.empty or baseline.empty: + continue + + d_coi = float(defended["coi_level_mean"].iloc[0]) + b_coi = float(baseline["coi_level_mean"].iloc[0]) + rows.append( + { + "alpha": float(alpha), + "n_products": float(n_products), + "baseline_runs": int(baseline["runs"].iloc[0]), + "defended_runs": int(defended["runs"].iloc[0]), + "baseline_coi_level_mean": b_coi, + "defended_coi_level_mean": d_coi, + "coi_preserved": d_coi - b_coi, + "coi_preserved_pct": 0.0 + if b_coi == 0.0 + else 100.0 * (d_coi - b_coi) / b_coi, + } + ) + + return ( + pd.DataFrame(rows).sort_values(["alpha", "n_products"]).reset_index(drop=True) + ) + + def _save_plot(fig: plt.Figure, path: Path) -> Path: path.parent.mkdir(parents=True, exist_ok=True) fig.savefig(path, bbox_inches="tight") @@ -217,6 +259,61 @@ def _save_plot(fig: plt.Figure, path: Path) -> Path: return path +def _smoothed_curve( + x: np.ndarray, + y: np.ndarray, + *, + window: int = 5, + points: int = 320, +) -> tuple[np.ndarray, np.ndarray]: + x_values = np.asarray(x, dtype=float) + y_values = np.asarray(y, dtype=float) + mask = np.isfinite(x_values) & np.isfinite(y_values) + x_values = x_values[mask] + y_values = y_values[mask] + if x_values.size == 0: + return x_values, y_values + + order = np.argsort(x_values) + x_values = x_values[order] + y_values = y_values[order] + + unique_x = np.unique(x_values) + if unique_x.size != x_values.size: + dedup = ( + pd.DataFrame({"x": x_values, "y": y_values}) + .groupby("x", as_index=False) + .agg(y=("y", "mean")) + .sort_values("x") + ) + x_values = dedup["x"].to_numpy(dtype=float) + y_values = dedup["y"].to_numpy(dtype=float) + + if x_values.size < 3: + return x_values, y_values + + win = int(max(3, window)) + if win % 2 == 0: + win += 1 + if win > x_values.size: + win = x_values.size if x_values.size % 2 == 1 else x_values.size - 1 + if win < 3: + return x_values, y_values + + half = win // 2 + offsets = np.arange(-half, half + 1, dtype=float) + sigma = max(win / 3.0, 1.0) + kernel = np.exp(-0.5 * (offsets / sigma) ** 2) + kernel = kernel / np.sum(kernel) + y_padded = np.pad(y_values, (half, half), mode="edge") + y_smooth = np.convolve(y_padded, kernel, mode="valid") + + n_points = max(int(points), x_values.size) + x_dense = np.linspace(float(np.min(x_values)), float(np.max(x_values)), n_points) + y_dense = np.interp(x_dense, x_values, y_smooth) + return x_dense, y_dense + + def _plot_focus_revenue_by_alpha(alpha_mode: pd.DataFrame, out_path: Path) -> Path: fig, ax = plt.subplots(figsize=(7.8, 4.8), constrained_layout=True) for mode, color, label in ( @@ -243,6 +340,148 @@ def _plot_focus_revenue_by_alpha(alpha_mode: pd.DataFrame, out_path: Path) -> Pa return _save_plot(fig, out_path) +def _plot_focus_coi_by_alpha(alpha_mode: pd.DataFrame, out_path: Path) -> Path: + fig, ax = plt.subplots(figsize=(7.8, 4.8), constrained_layout=True) + for mode, color, label in ( + ("baseline", "#4C72B0", "Baseline"), + ("defended", "#C44E52", "Defended"), + ): + sub = alpha_mode[alpha_mode["mode"] == mode].sort_values("alpha") + if sub.empty: + continue + x_raw = sub["alpha"].to_numpy(dtype=float) + y_raw = sub["coi_level_mean"].to_numpy(dtype=float) + x_smooth, y_smooth = _smoothed_curve(x_raw, y_raw) + ax.plot( + x_smooth, + y_smooth, + linewidth=1.9, + color=color, + label=label, + ) + ax.scatter( + x_raw, + y_raw, + s=18, + color=color, + edgecolor="#FFFFFF", + linewidth=0.45, + zorder=3, + ) + + paired = alpha_mode.pivot_table( + index="alpha", + columns="mode", + values="coi_level_mean", + aggfunc="mean", + ).sort_index() + if {"baseline", "defended"}.issubset(set(paired.columns)): + paired = paired.dropna(subset=["baseline", "defended"], how="any") + if not paired.empty: + x = paired.index.to_numpy(dtype=float) + y_baseline = paired["baseline"].to_numpy(dtype=float) + y_defended = paired["defended"].to_numpy(dtype=float) + x_fill, y_baseline_smooth = _smoothed_curve(x, y_baseline) + _, y_defended_smooth = _smoothed_curve(x, y_defended) + ax.fill_between( + x_fill, + y_baseline_smooth, + y_defended_smooth, + color="#55A868", + alpha=0.12, + label="Gap", + ) + + ax.axvline(0.7, color="#666666", linewidth=1.0, linestyle="--") + ax.set_xlabel(r"Contamination $\alpha$") + ax.set_ylabel("Mean COI level") + ax.set_title("Final Cohort COI Curves") + ax.legend(loc="lower left") + return _save_plot(fig, out_path) + + +def _plot_focus_coi_preservation_grid( + coi_preservation: pd.DataFrame, out_path: Path +) -> Path: + if coi_preservation.empty: + raise ValueError("COI preservation grid requires at least one paired cell") + + alpha_levels = sorted(coi_preservation["alpha"].dropna().unique().tolist()) + endpoint_targets = (0.0, 1.0) + endpoint_levels = [ + alpha + for target in endpoint_targets + for alpha in alpha_levels + if np.isclose(alpha, target, atol=1e-9) + ] + if len(endpoint_levels) < 2 and alpha_levels: + endpoint_levels = [alpha_levels[0], alpha_levels[-1]] + endpoint_levels = sorted(set(endpoint_levels)) + + data = coi_preservation[coi_preservation["alpha"].isin(endpoint_levels)].copy() + if data.empty: + raise ValueError( + "COI preservation grid has no rows for selected alpha endpoints" + ) + + alpha_levels = sorted(data["alpha"].dropna().unique().tolist()) + product_levels = sorted(data["n_products"].dropna().unique().tolist()) + + bars = data.pivot_table( + index="n_products", + columns="alpha", + values="coi_preserved", + aggfunc="mean", + ).reindex(index=product_levels, columns=alpha_levels) + + x = np.arange(len(product_levels), dtype=float) + n_alpha = max(len(alpha_levels), 1) + bar_width = min(0.78 / n_alpha, 0.35) + offsets = (np.arange(n_alpha, dtype=float) - (n_alpha - 1) / 2.0) * bar_width + palette = ["#4C72B0", "#C44E52", "#55A868", "#8172B3"] + + fig, ax = plt.subplots(figsize=(7.8, 5.0), constrained_layout=True) + for idx, alpha in enumerate(alpha_levels): + values = bars[alpha].to_numpy(dtype=float) + mask = np.isfinite(values) + if not np.any(mask): + continue + xpos = x[mask] + offsets[idx] + v = values[mask] + ax.bar( + xpos, + v, + width=bar_width * 0.96, + color=palette[idx % len(palette)], + label=rf"$\alpha={alpha:.1f}$", + ) + for x_i, y_i in zip(xpos, v): + ax.text( + float(x_i), + float(y_i) + (0.035 if y_i >= 0 else -0.035), + f"{y_i:+.2f}", + ha="center", + va="bottom" if y_i >= 0 else "top", + fontsize=7, + ) + + valid = bars.to_numpy(dtype=float) + valid = valid[np.isfinite(valid)] + max_abs = float(np.max(np.abs(valid))) if valid.size else 1.0 + max_abs = max(max_abs * 1.22, 0.4) + ax.set_ylim(-max_abs, max_abs) + + ax.axhline(0.0, color="#444444", linewidth=1.0, linestyle="--") + ax.set_xticks(x) + ax.set_xticklabels([f"{int(v)}" for v in product_levels]) + ax.set_xlabel("Product count") + ax.set_ylabel("COI preserved (defended minus baseline)") + ax.set_title("COI Preservation by Product Count at $\\alpha=0.0$ vs $\\alpha=1.0$") + ax.legend(loc="upper right") + ax.grid(axis="y", alpha=0.22) + return _save_plot(fig, out_path) + + def _plot_focus_revenue_delta(alpha_deltas: pd.DataFrame, out_path: Path) -> Path: fig, ax = plt.subplots(figsize=(7.8, 4.8), constrained_layout=True) x = alpha_deltas["alpha"].to_numpy(dtype=float) @@ -326,6 +565,7 @@ def run( alpha_mode = _alpha_mode_summary(focus_runs) deltas = _alpha_deltas(alpha_mode) zones = _zone_summary(deltas) + coi_preservation = _alpha_product_coi_preservation(focus_runs) output_dir.mkdir(parents=True, exist_ok=True) plot_dir.mkdir(parents=True, exist_ok=True) @@ -343,6 +583,10 @@ def run( zones.to_csv(zone_path, index=False) written.append(zone_path) + coi_grid_path = output_dir / "final_focus_coi_preservation_grid.csv" + coi_preservation.to_csv(coi_grid_path, index=False) + written.append(coi_grid_path) + headline = { "bundle": str(bundle_dir), "focus_cohort": "max_alpha_coverage", @@ -370,6 +614,18 @@ def run( plot_dir / "final_focus_revenue_by_alpha.pdf", ) ) + written.append( + _plot_focus_coi_by_alpha( + alpha_mode, + plot_dir / "final_focus_coi_by_alpha.pdf", + ) + ) + written.append( + _plot_focus_coi_preservation_grid( + coi_preservation, + plot_dir / "final_focus_coi_preservation_grid.pdf", + ) + ) written.append( _plot_focus_revenue_delta( deltas, @@ -391,6 +647,20 @@ def run( "0.98\\linewidth", ) ) + written.append( + _write_include( + include_dir / "final_focus_coi_by_alpha.tex", + "chapters/figures/results/generated/final/plots/final_focus_coi_by_alpha.pdf", + "0.98\\linewidth", + ) + ) + written.append( + _write_include( + include_dir / "final_focus_coi_preservation_grid.tex", + "chapters/figures/results/generated/final/plots/final_focus_coi_preservation_grid.pdf", + "0.98\\linewidth", + ) + ) written.append( _write_include( include_dir / "final_focus_revenue_delta.tex", From 58042ba4f24fdf983a4616cc5743d430701710a9 Mon Sep 17 00:00:00 2001 From: Daniel Rosel Date: Fri, 27 Mar 2026 17:19:27 +0100 Subject: [PATCH 05/44] updating node positinoing --- paper/src/chapters/mdp_agent.pdf | Bin 10743 -> 11308 bytes paper/src/chapters/mdp_human.pdf | Bin 12194 -> 12049 bytes sim/rl/behavior_loader/models.py | 95 ++++++++++++++++++++++++------- 3 files changed, 76 insertions(+), 19 deletions(-) diff --git a/paper/src/chapters/mdp_agent.pdf b/paper/src/chapters/mdp_agent.pdf index 0566be9e926934d4b553b90623800f40acac21ea..6845eb5e38a36c814ccf729d04281671097dd95c 100644 GIT binary patch delta 9665 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 Dqd%DZ delta 9154 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 C?lHaq delta 10510 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 QE*=UwG&Kq(B}Gq03MMgmB>(^b diff --git a/sim/rl/behavior_loader/models.py b/sim/rl/behavior_loader/models.py index cb67cbf..0b1a285 100644 --- a/sim/rl/behavior_loader/models.py +++ b/sim/rl/behavior_loader/models.py @@ -3,7 +3,7 @@ try: except ImportError: from sim.rl.behavior_loader.loader import Loader, AgentLoader, JointLoader from collections import defaultdict -from typing import Dict, List, Tuple, Set +from typing import Dict, List, Optional, Set, Tuple import numpy as np import graphviz import sys @@ -195,6 +195,35 @@ def aggregate_event_transitions(mdp: Dict) -> Dict[str, Dict[str, float]]: return dict(evt_trans) +def _resolve_event_order( + evt_trans: Dict[str, Dict[str, float]], + event_order: Optional[List[str]] = None, +) -> List[str]: + observed = set(evt_trans.keys()) | { + dst for transitions in evt_trans.values() for dst in transitions + } + if event_order: + ordered = list(dict.fromkeys(event_order)) + missing = sorted(observed - set(ordered)) + return ordered + missing + return sorted(observed) + + +def _fixed_circle_positions( + events: List[str], radius: float +) -> Dict[str, Tuple[float, float]]: + if not events: + return {} + step = (2 * np.pi) / len(events) + return { + evt: ( + float(radius * np.cos(idx * step)), + float(radius * np.sin(idx * step)), + ) + for idx, evt in enumerate(events) + } + + def visualize_mdp( model: BehaviorModel, threshold: float = 0.05, @@ -202,20 +231,31 @@ def visualize_mdp( fmt: str = "svg", view: bool = False, export_dot: bool = False, + event_order: Optional[List[str]] = None, + layout_radius: float = 6.0, + node_diameter: float = 2.4, ): if not model.mdp: raise ValueError("build MDP first") evt_trans = aggregate_event_transitions(model.mdp) - g = graphviz.Digraph(format=fmt) - g.attr(rankdir="LR", size="30") - g.attr("node", shape="circle", width="1", height="1") + ordered_events = _resolve_event_order(evt_trans, event_order=event_order) + positions = _fixed_circle_positions(ordered_events, radius=layout_radius) - events = set(evt_trans.keys()) | { - e for trans in evt_trans.values() for e in trans.keys() - } - for evt in events: - g.node(evt) + g = graphviz.Digraph(format=fmt, engine="neato") + g.attr(overlap="false", splines="true", outputorder="edgesfirst") + g.attr( + "node", + shape="circle", + width=f"{node_diameter:.2f}", + height=f"{node_diameter:.2f}", + fixedsize="true", + fontsize="10", + ) + + for evt in ordered_events: + x_pos, y_pos = positions[evt] + g.node(evt, pos=f"{x_pos:.3f},{y_pos:.3f}!", pin="true") for src, dsts in evt_trans.items(): for dst, prob in dsts.items(): @@ -342,11 +382,6 @@ if __name__ == "__main__": f"Built MDP: {human_mdp['num_states']} states, " f"{sum(len(t) for t in human_mdp['transitions'].values())} transitions" ) - if not human_mdp["states"]: - exit("No states found") - visualize_mdp( - human_model, threshold=0.05, output="human_mdp_viz", fmt="pdf", export_dot=True - ) agent_model = AgentBehaviorModel(agent_dir) agent_mdp = agent_model.build_MDP() @@ -355,14 +390,35 @@ if __name__ == "__main__": f"AGENT... Built MDP: {agent_mdp['num_states']} states, " f"{sum(len(t) for t in agent_mdp['transitions'].values())} transitions" ) - if not agent_mdp["states"]: - exit("No states found") - visualize_mdp( - agent_model, threshold=0.05, output="agent_mdp_viz", fmt="pdf", export_dot=True - ) human_evt = aggregate_event_transitions(human_mdp) agent_evt = aggregate_event_transitions(agent_mdp) + canonical_events = sorted( + (set(human_evt.keys()) | {e for tr in human_evt.values() for e in tr.keys()}) + | (set(agent_evt.keys()) | {e for tr in agent_evt.values() for e in tr.keys()}) + ) + + if not human_mdp["states"]: + exit("No states found") + visualize_mdp( + human_model, + threshold=0.05, + output="human_mdp_viz", + fmt="pdf", + export_dot=True, + event_order=canonical_events, + ) + + if not agent_mdp["states"]: + exit("No states found") + visualize_mdp( + agent_model, + threshold=0.05, + output="agent_mdp_viz", + fmt="pdf", + export_dot=True, + event_order=canonical_events, + ) common = set(human_evt.keys()) & set(agent_evt.keys()) @@ -394,6 +450,7 @@ if __name__ == "__main__": output="joint_mdp_viz", fmt="pdf", export_dot=True, + event_order=canonical_events, ) inter_class_avg = float(np.mean([kl for _, kl in kl_divs])) From 9c464eaf3b8ce70c0771316f3a131ce273d56033 Mon Sep 17 00:00:00 2001 From: Daniel Rosel Date: Fri, 27 Mar 2026 21:14:54 +0100 Subject: [PATCH 06/44] chore: forgot the fiures --- .../final_focus_coi_preservation_grid.csv | 45 ++++++++++++++++++ .../final/plots/final_focus_coi_by_alpha.pdf | Bin 0 -> 24919 bytes .../final_focus_coi_preservation_grid.pdf | Bin 0 -> 21304 bytes .../final/final_focus_coi_by_alpha.tex | 1 + .../final_focus_coi_preservation_grid.tex | 1 + 5 files changed, 47 insertions(+) create mode 100644 paper/src/chapters/figures/results/generated/final/final_focus_coi_preservation_grid.csv create mode 100644 paper/src/chapters/figures/results/generated/final/plots/final_focus_coi_by_alpha.pdf create mode 100644 paper/src/chapters/figures/results/generated/final/plots/final_focus_coi_preservation_grid.pdf create mode 100644 paper/src/chapters/figures/results/includes/final/final_focus_coi_by_alpha.tex create mode 100644 paper/src/chapters/figures/results/includes/final/final_focus_coi_preservation_grid.tex diff --git a/paper/src/chapters/figures/results/generated/final/final_focus_coi_preservation_grid.csv b/paper/src/chapters/figures/results/generated/final/final_focus_coi_preservation_grid.csv new file mode 100644 index 0000000..a2e5115 --- /dev/null +++ b/paper/src/chapters/figures/results/generated/final/final_focus_coi_preservation_grid.csv @@ -0,0 +1,45 @@ +alpha,n_products,baseline_runs,defended_runs,baseline_coi_level_mean,defended_coi_level_mean,coi_preserved,coi_preserved_pct +0.0,5.0,9,10,137.060822623968,136.18680853180368,-0.874014092164316,-0.6376833842316922 +0.0,25.0,9,2,137.114858903596,136.13793579187393,-0.9769231117220727,-0.7124852255501622 +0.0,50.0,9,11,137.16224858153575,136.92415566181484,-0.23809291972091273,-0.17358487643878118 +0.0,100.0,9,12,135.86629045322655,137.3609873086303,1.4946968554037596,1.1001234010420895 +0.1,5.0,3,6,136.59581715538818,135.6308466787041,-0.9649704766840728,-0.7064421859904723 +0.1,25.0,11,8,135.9860669350444,136.43616365263273,0.45009671758833747,0.33098737814318313 +0.1,50.0,10,11,136.28362874897243,136.92880179422633,0.6451730452538982,0.4734046570203046 +0.1,100.0,8,8,137.35578496752095,137.53394777402949,0.17816280650853855,0.12970899372797937 +0.2,5.0,8,9,135.55116314329388,137.30311388107864,1.7519507377847674,1.2924645551973204 +0.2,25.0,10,9,137.01587649612287,137.22137163685403,0.20549514073115915,0.1499790724887083 +0.2,50.0,4,8,137.45096138958434,137.1307018163465,-0.32025957323784837,-0.2329991511155169 +0.2,100.0,9,9,137.50780776750915,137.43195025898902,-0.07585750852013007,-0.0551659645744523 +0.3,5.0,6,6,134.95569459599133,134.21855668602896,-0.7371379099623709,-0.5462073402453271 +0.3,25.0,9,16,136.38346021911525,136.32131251342705,-0.06214770568820427,-0.04556835967378819 +0.3,50.0,8,6,136.97414077213367,136.88041560990786,-0.09372516222580884,-0.06842544271310845 +0.3,100.0,7,16,137.19706520314455,137.31020460277784,0.11313939963329744,0.08246488324351146 +0.4,5.0,8,11,135.6494813257779,136.5487738152141,0.899292489436192,0.6629531352769695 +0.4,25.0,7,9,136.38451372914378,136.10614648175604,-0.27836724738773455,-0.20410473284420322 +0.4,50.0,7,10,137.12976275807247,136.98838321468799,-0.14137954338448822,-0.10309909427460566 +0.4,100.0,11,8,137.4158065068933,137.4849148270489,0.06910832015560686,0.050291390715769026 +0.5,5.0,7,19,135.91101413475477,136.145621134976,0.2346070002212457,0.1726180925915501 +0.5,25.0,8,7,137.0972914279529,137.35620682163616,0.2589153936832531,0.18885522170896996 +0.5,50.0,8,1,137.0714841014652,135.66696334266234,-1.404520758802846,-1.0246629837050352 +0.5,100.0,10,8,137.4717672869487,137.35366167964338,-0.11810560730532416,-0.08591262746975456 +0.6,5.0,8,13,133.13626070539635,136.09936023073067,2.9630995253343144,2.225614201296411 +0.6,25.0,5,10,136.0741624588533,136.26219778039936,0.18803532154606728,0.13818591137970535 +0.6,50.0,8,10,135.09036188289087,136.05846380616936,0.968101923278482,0.7166328595060871 +0.6,100.0,7,8,137.29304001584052,137.07512338179083,-0.2179166340496863,-0.15872372993164377 +0.7,5.0,7,7,136.0533783988379,135.14350016006424,-0.9098782387736719,-0.6687656341075052 +0.7,25.0,8,11,137.12781750399415,136.8176582131797,-0.3101592908144539,-0.2261826203172962 +0.7,50.0,14,11,137.06965735909125,136.7028634119364,-0.3667939471548607,-0.26759674914335285 +0.7,100.0,11,11,137.48279078937205,137.09121810549402,-0.39157268387802446,-0.28481578067317975 +0.8,5.0,4,7,135.3095773096514,136.59715728802078,1.2875799783693935,0.9515808148766959 +0.8,25.0,12,13,136.93488398652164,135.73319876476054,-1.201685221761096,-0.8775596011600497 +0.8,50.0,6,8,136.4704324290659,136.86568018140107,0.39524775233516607,0.289621528487943 +0.8,100.0,4,11,137.519864039095,137.4763376137669,-0.04352642532811046,-0.03165100957032396 +0.9,5.0,5,5,134.77024204025943,136.6651608019597,1.8949187617002679,1.4060364758669837 +0.9,25.0,9,13,136.7554042236364,136.06108143100832,-0.6943227926280713,-0.507711411164888 +0.9,50.0,10,12,136.08715955450202,137.07569864767092,0.988539093168896,0.7264014447836223 +0.9,100.0,11,9,137.57053132642514,137.30115968842037,-0.2693716380047704,-0.19580620602940735 +1.0,5.0,5,7,136.43177888041947,135.92674388998284,-0.5050349904366271,-0.37017401266847305 +1.0,25.0,11,9,136.7037183889911,136.22617845471228,-0.47753993427880914,-0.34932475861407586 +1.0,50.0,11,5,136.93074105866745,137.05826644845806,0.12752538979060546,0.09313130769953819 +1.0,100.0,8,9,136.4880191421812,137.41913068956546,0.9311115473842619,0.682192879079234 diff --git a/paper/src/chapters/figures/results/generated/final/plots/final_focus_coi_by_alpha.pdf b/paper/src/chapters/figures/results/generated/final/plots/final_focus_coi_by_alpha.pdf new file mode 100644 index 0000000000000000000000000000000000000000..b47a4fd8a175372b94fc20f6e8e179d5444e99f5 GIT binary patch literal 24919 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 Y-ow+}-IFp!qew)sA`lT#)>47}Urh=UiU0rr literal 0 HcmV?d00001 diff --git a/paper/src/chapters/figures/results/includes/final/final_focus_coi_by_alpha.tex b/paper/src/chapters/figures/results/includes/final/final_focus_coi_by_alpha.tex new file mode 100644 index 0000000..6eafa3f --- /dev/null +++ b/paper/src/chapters/figures/results/includes/final/final_focus_coi_by_alpha.tex @@ -0,0 +1 @@ +\includegraphics[width=0.98\linewidth]{chapters/figures/results/generated/final/plots/final_focus_coi_by_alpha.pdf} diff --git a/paper/src/chapters/figures/results/includes/final/final_focus_coi_preservation_grid.tex b/paper/src/chapters/figures/results/includes/final/final_focus_coi_preservation_grid.tex new file mode 100644 index 0000000..1ca04c8 --- /dev/null +++ b/paper/src/chapters/figures/results/includes/final/final_focus_coi_preservation_grid.tex @@ -0,0 +1 @@ +\includegraphics[width=0.98\linewidth]{chapters/figures/results/generated/final/plots/final_focus_coi_preservation_grid.pdf} From e77f037d626ccd8c58de900c3c7392e45349342c Mon Sep 17 00:00:00 2001 From: Daniel Rosel Date: Sat, 28 Mar 2026 11:56:37 +0100 Subject: [PATCH 07/44] initial progress --- paper/src/chapters/03-methodology.tex | 2 +- paper/src/chapters/mdp_agent.pdf | Bin 11308 -> 11236 bytes paper/src/chapters/mdp_human.pdf | Bin 12049 -> 11953 bytes scripts/nx_paper.sh | 21 ++++ sim/rl/behavior_loader/models.py | 163 ++++++++++++++++++++++---- 5 files changed, 163 insertions(+), 23 deletions(-) diff --git a/paper/src/chapters/03-methodology.tex b/paper/src/chapters/03-methodology.tex index 8c58717..799486e 100644 --- a/paper/src/chapters/03-methodology.tex +++ b/paper/src/chapters/03-methodology.tex @@ -23,7 +23,7 @@ where: The platform does not directly observe the true underlying demand function $d(p)$. Instead, it observes a behavioral proxy $\hat{q}_t$, which is a composite signal derived from the mixture of actor types. We define the demand proxy for product $i$ at epoch $t$ as a weighted aggregation of events: \begin{equation} \label{eq:qhat} -\hat{q}_{t,i} = \sum_{s \in \mathcal{S}_t} \sum_{k=1}^{L_s} \omega(a_{s,k}) \cdot \mathds{1}[i_{s,k} = i] +\hat{q}_{t,i} = \sum_{s \in \mathcal{S}_t} \sum_{k=1}^{L_s} \omega(a_{s,k}) \cdot \mathbf{1}[i_{s,k} = i] \end{equation} where $\omega: \mathcal{A} \to \mathbb{R}_+$ assigns weights to actions based on their signal strength regarding willingness to pay. diff --git a/paper/src/chapters/mdp_agent.pdf b/paper/src/chapters/mdp_agent.pdf index 6845eb5e38a36c814ccf729d04281671097dd95c..aeab1b7ff171d39eb33df300f16001377afec8b7 100644 GIT binary patch delta 9869 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 USXd(e+cY;9G7XJ{k|gr~0L^FmbN~PV delta 9894 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 diff --git a/paper/src/chapters/mdp_human.pdf b/paper/src/chapters/mdp_human.pdf index 69bc8d3cde21065fc132b2dbbab5b534201752f5..b753b4ea8fce0f6dfcb47961aafd33ed07beeed4 100644 GIT binary patch delta 10531 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 RK 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 str: + ports = ("e", "ne", "n", "nw", "w", "sw", "s", "se") + normalized = (angle_rad + (2 * np.pi)) % (2 * np.pi) + step = np.pi / 4 + idx = int(np.round(normalized / step)) % len(ports) + return ports[idx] + + +def _edge_ports( + src: str, + dst: str, + positions: Dict[str, Tuple[float, float]], + has_reverse: bool, +) -> Tuple[str, str]: + src_x, src_y = positions[src] + dst_x, dst_y = positions[dst] + angle = float(np.arctan2(dst_y - src_y, dst_x - src_x)) + + if has_reverse: + bend = np.pi / 10 + angle += bend if src < dst else -bend + + tail_port = _compass_from_angle(angle) + head_port = _compass_from_angle(angle + np.pi) + return tail_port, head_port + + +def _edge_style(prob: float) -> Dict[str, str]: + if prob >= 0.75: + edge_color = "#111827" + elif prob >= 0.50: + edge_color = "#374151" + elif prob >= 0.25: + edge_color = "#6b7280" + else: + edge_color = "#9ca3af" + return { + "color": edge_color, + "fontcolor": "#111827", + "fontsize": "10", + "penwidth": f"{0.9 + 3.6 * prob:.2f}", + "arrowsize": f"{0.55 + 0.55 * prob:.2f}", + } + + +def _format_node_label(evt: str) -> str: + max_line_len = 16 + tokens = evt.split("_") + if len(tokens) == 1: + return evt + + lines: List[str] = [] + curr = "" + for token in tokens: + piece = token if not curr else f"_{token}" + if curr and len(curr) + len(piece) > max_line_len: + lines.append(curr) + curr = token + else: + curr = f"{curr}{piece}" if curr else token + if curr: + lines.append(curr) + return "\n".join(lines) + + +def _compute_flow_positions( + events: List[str], + layout_radius: float, ) -> Dict[str, Tuple[float, float]]: + """Balanced grid layout for paper-friendly diagrams.""" if not events: return {} - step = (2 * np.pi) / len(events) - return { - evt: ( - float(radius * np.cos(idx * step)), - float(radius * np.sin(idx * step)), - ) - for idx, evt in enumerate(events) - } + + num_events = len(events) + cols = int(np.ceil(np.sqrt(num_events))) + rows = int(np.ceil(num_events / cols)) + x_step = max(layout_radius * 1.10, 3.6) + y_step = max(layout_radius * 0.95, 3.2) + + positions: Dict[str, Tuple[float, float]] = {} + for idx, evt in enumerate(events): + row = idx // cols + col = idx % cols + x = (col - (cols - 1) / 2.0) * x_step + y = ((rows - 1) / 2.0 - row) * y_step + positions[evt] = (float(x), float(y)) + + return positions def visualize_mdp( @@ -232,35 +307,79 @@ def visualize_mdp( view: bool = False, export_dot: bool = False, event_order: Optional[List[str]] = None, - layout_radius: float = 6.0, - node_diameter: float = 2.4, + layout_radius: float = 10.0, + node_diameter: float = 1.8, + label_threshold: float = 0.08, ): if not model.mdp: raise ValueError("build MDP first") evt_trans = aggregate_event_transitions(model.mdp) ordered_events = _resolve_event_order(evt_trans, event_order=event_order) - positions = _fixed_circle_positions(ordered_events, radius=layout_radius) + positions = _compute_flow_positions(ordered_events, layout_radius=layout_radius) g = graphviz.Digraph(format=fmt, engine="neato") - g.attr(overlap="false", splines="true", outputorder="edgesfirst") + g.attr( + overlap="false", + splines="true", + outputorder="edgesfirst", + pad="0.5", + sep="+9", + esep="+4", + bgcolor="white", + dpi="180", + ) g.attr( "node", shape="circle", + fixedsize="true", width=f"{node_diameter:.2f}", height=f"{node_diameter:.2f}", - fixedsize="true", - fontsize="10", + fontsize="11", + fontname="Helvetica", + style="filled", + fillcolor="white", + color="#374151", + fontcolor="#111827", + penwidth="1.8", + peripheries="1", + ) + g.attr( + "edge", + fontname="Helvetica", ) for evt in ordered_events: - x_pos, y_pos = positions[evt] - g.node(evt, pos=f"{x_pos:.3f},{y_pos:.3f}!", pin="true") + x, y = positions[evt] + g.node(evt, label=_format_node_label(evt), pos=f"{x:.2f},{y:.2f}!", pin="true") - for src, dsts in evt_trans.items(): - for dst, prob in dsts.items(): - if prob > threshold: - g.edge(src, dst, label=f"{prob:.2f}") + edges = [ + (src, dst, prob) + for src, dsts in evt_trans.items() + for dst, prob in dsts.items() + if prob > threshold + ] + edge_set = {(src, dst) for src, dst, _ in edges} + + for src, dst, prob in sorted(edges, key=lambda row: row[2]): + edge_attrs: Dict[str, str] = _edge_style(prob) + + if src == dst: + # pick a loop port away from the main flow + sx, sy = positions[src] + loop_port = "n" if sy <= 0 else "s" + edge_attrs.update({"tailport": loop_port, "headport": loop_port}) + else: + has_reverse = (dst, src) in edge_set + tail_port, head_port = _edge_ports(src, dst, positions, has_reverse) + edge_attrs.update({"tailport": tail_port, "headport": head_port}) + if has_reverse: + edge_attrs["constraint"] = "false" + + if prob >= label_threshold or src == dst: + edge_attrs["label"] = f" {prob:.2f} " + + g.edge(src, dst, **edge_attrs) g.render(output, view=view, cleanup=True) print(f"Saved MDP graph to {output}.{fmt}") From 59b2b46f6e9381bc5b0ef68fb89e61018c481544 Mon Sep 17 00:00:00 2001 From: Daniel Rosel Date: Sat, 28 Mar 2026 13:18:08 +0100 Subject: [PATCH 08/44] updating bins --- paper/src/chapters/mdp_agent.pdf | Bin 11236 -> 10932 bytes paper/src/chapters/mdp_human.pdf | Bin 11953 -> 11953 bytes sim/rl/behavior_loader/models.py | 26 +++++++++++++++++++------- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/paper/src/chapters/mdp_agent.pdf b/paper/src/chapters/mdp_agent.pdf index aeab1b7ff171d39eb33df300f16001377afec8b7..24d141ea8f5e3b3d4d986e0cda0860d91e6d895b 100644 GIT binary patch delta 8951 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 Ryel3GHaId0B_%~qMhbOwShoNG delta 9251 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

^adF-M+@&wfYM2;X^Bn1&kVlP2uTBopd1jIM_= zlvF+IB(3s9F&Xd2C&;T&YeE)7I$4NEMdT1^AOoP1RN#{W`bvY@U+~T<@l({fg1#X9 zg|xyLl0@>w8)$1xonyf&Li)Q+hXv1syu;q9v)mVE#|-HIlcC!ryMGUp%)^EiZhuct z<}A_9W*=sb!{AX4i=Mbo|JBJ)2lStmIm>;xd+eAyPaT^x2Ir>F!b5e0(>OnN%zz-C z2!!P36uEvGGW-V;tpwupcw>h@uz&lp&Af~|wUgyVI8^T95ANkf+)0SG3D1{lC;tO7 z#bEU13mXYx;iMGzAAjM#8v9!8RoL^ee~P^ldjf@3?xedY^h;9ZMV4+D~G!<&ov2jZUE# zg#O63Z@?UAMxMA&Y!cg8EGuU>B(GF1H6txwE+19$G=e5mbAMFxd#zDBN4GqnSqymy zF?BSes({$$#bYAc*aEda_=Ppzp4%Kct?)Mw{52JBRl;A4@B@dN9RAY--y7k(O6c&w zx4-@w`w(0QsXUXjIPWUVt zKIQNy4xgmLe}7ov<4U;7;Uf$D`)c^`1pJZ1AMo-&tcEL>A7ocn!{rCz((mo;5{KX0 z;omrXz~Ofs-sjL-2^U*j>>`I27rd7W?{YY|-ptN9;GHmN=5Ut7+Z@htIL+ZL4yQPr zD#hU^+Fi=@Br^3R5SBvZ)+0b?|czQywz0DI6X$!DJ4T@XRC*6DOG1 z#D7qj;54uaCKzvk2RV$Zgt3({hQkBG0QNv9j6MNBn*?4Cqd1HlVPPY!Fk*NJ8)1Rr z!whV=wAWM-3cyK!IcM2131uv4i5HCh#C!%IEdsBkq6;usc;TqcvDy;*f>}@ zgmSRp1r`ow)YUv1OsgRz4~!fP`Y>kTpvSBAVW8unHGzhMf|n^AWGhH{AmT|8BPPHG zI13!1-Nb|eKuth6P(RIE{{rpze_teM$Nn!kwfc(wW9v|SKIN@_ZJKMJbjBGZ8;^=JMEA22-(l`s2u~jB8yN1yD zb)FBjak9G_69ZS8^F7+jojLw7?;M!OR)qOg_vTuAqqUiq&PU+cc`i~r1|bL&=H*@b+Ywca#Obv zzaqF^kJB!0?e(`&`gGVP7*i*H(%$w?X^+L^d5%IA?%{pB!@q(%8RVA^Z65vN4IcI4 z(l6~IZ#t8aDIWqiIg_#}D*`t;lduRGllUoBJ{mJHFf#B12~HW17zdE0HV4FA03^7S zfP|(8h-D5Wl>0!$G9aOv3?ix^rZ9pCW=5?mKryY?3=9C!iV3=tj4B=qIXE{8B_%~q FMhd%l9cTam diff --git a/paper/src/chapters/mdp_human.pdf b/paper/src/chapters/mdp_human.pdf index b753b4ea8fce0f6dfcb47961aafd33ed07beeed4..6ae3aa3b7cb7218b4f1994305782059333b98451 100644 GIT binary patch delta 280 zcmV+z0q6d)U9nxThAe+N6l_V39XFU9l0Yb>5R%?X4?-4>pjbwdOY`@YoYpjbwdOY`@YoH(WEw2vLl z49h?XE09yd!E#2}8q0M-stVcrWl+eDqEn6l$X<2p@Bt2r2A>n!o3Zy`WV@XdM{C+q zDvN~_+8VHwmFWtLv-gABWZ6VJD~7X{E;Y7GlIu5C39n6G3$K5$s5hMQf-ZT<*osp6 zMA;*y$!zi$3lG6M>W{*zm?I{ez?*8d*qNUYmxjlmixRxZ*Z0`H4+4kPU2(8x98gtp zi1XEDqBA-LYDMpc=q4{Seu1~6nx}o(s@1oasyXZ;45=e;lry81%3*o;JO!cj*YGaf e;a|bM@Z!sKw|)!Z2Df@)=@<3eZ$-0@Eg}Id5sV)I diff --git a/sim/rl/behavior_loader/models.py b/sim/rl/behavior_loader/models.py index 25c8c15..0b1d95e 100644 --- a/sim/rl/behavior_loader/models.py +++ b/sim/rl/behavior_loader/models.py @@ -310,12 +310,24 @@ def visualize_mdp( layout_radius: float = 10.0, node_diameter: float = 1.8, label_threshold: float = 0.08, + drop_isolated_nodes: bool = False, ): if not model.mdp: raise ValueError("build MDP first") evt_trans = aggregate_event_transitions(model.mdp) ordered_events = _resolve_event_order(evt_trans, event_order=event_order) + + edges = [ + (src, dst, prob) + for src, dsts in evt_trans.items() + for dst, prob in dsts.items() + if prob > threshold + ] + if drop_isolated_nodes: + connected = {src for src, _, _ in edges} | {dst for _, dst, _ in edges} + ordered_events = [evt for evt in ordered_events if evt in connected] + positions = _compute_flow_positions(ordered_events, layout_radius=layout_radius) g = graphviz.Digraph(format=fmt, engine="neato") @@ -353,15 +365,14 @@ def visualize_mdp( x, y = positions[evt] g.node(evt, label=_format_node_label(evt), pos=f"{x:.2f},{y:.2f}!", pin="true") - edges = [ - (src, dst, prob) - for src, dsts in evt_trans.items() - for dst, prob in dsts.items() - if prob > threshold - ] - edge_set = {(src, dst) for src, dst, _ in edges} + edge_set = { + (src, dst) for src, dst, _ in edges if src in positions and dst in positions + } for src, dst, prob in sorted(edges, key=lambda row: row[2]): + if src not in positions or dst not in positions: + continue + edge_attrs: Dict[str, str] = _edge_style(prob) if src == dst: @@ -537,6 +548,7 @@ if __name__ == "__main__": fmt="pdf", export_dot=True, event_order=canonical_events, + drop_isolated_nodes=True, ) common = set(human_evt.keys()) & set(agent_evt.keys()) From eab9203111dba8824bbf15272cd51f55ea3be2e0 Mon Sep 17 00:00:00 2001 From: Daniel Rosel Date: Sat, 28 Mar 2026 15:43:37 +0100 Subject: [PATCH 09/44] fixing the radme --- README.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index e920e3b..a21d899 100644 --- a/README.md +++ b/README.md @@ -6,10 +6,12 @@ Agent-aware dynamic pricing research platform for studying how automated transaction orchestration changes pricing power, and for testing defenses that recover margin while protecting legitimate user experience. -[![Build PDF](https://github.com/velocitatem/PHANTOM/actions/workflows/latex.yml/badge.svg)](https://github.com/velocitatem/PHANTOM/actions/workflows/latex.yml) -[![Paper](https://img.shields.io/badge/Paper-PDF-red?logo=adobe-acrobat-reader)](https://pub-d5b94a3c29fd40c6b3881946e463fdb7.r2.dev/thesis-latest.pdf) -[![Dataset on HF](https://huggingface.co/datasets/huggingface/badges/resolve/main/dataset-on-hf-sm.svg)](https://huggingface.co/datasets/velocitatem/whoclickedit) -[![TPU Research Cloud](https://img.shields.io/badge/TPU%20Research%20Cloud-TRC%20supported-4285F4?logo=googlecloud&logoColor=white)](https://sites.research.google/trc/faq/) +

+ Build PDF + Paper PDF + Dataset on Hugging Face + TPU Research Cloud +

**Live demos:** [Hotel](https://phantom-hotel.vercel.app) | [Airline](https://phantom-airline.vercel.app) | [Academic page](https://velocitatem.github.io/PHANTOM/) From 291472295b4b2be82a81288412d902a2b15493d5 Mon Sep 17 00:00:00 2001 From: Daniel Rosel Date: Tue, 31 Mar 2026 09:06:15 +0200 Subject: [PATCH 10/44] chore: updating comments fro mfeedback --- paper/src/chapters/02-literature-review.tex | 2 +- paper/src/chapters/03-methodology.tex | 48 +++++++++++-------- paper/src/chapters/04-results.tex | 24 ++++++++-- paper/src/chapters/05-discussion.tex | 14 ++++++ .../figures/experiment_design_tree.tex | 17 +++++++ paper/src/main.tex | 4 +- 6 files changed, 80 insertions(+), 29 deletions(-) create mode 100644 paper/src/chapters/figures/experiment_design_tree.tex diff --git a/paper/src/chapters/02-literature-review.tex b/paper/src/chapters/02-literature-review.tex index 272ea4c..5aad893 100644 --- a/paper/src/chapters/02-literature-review.tex +++ b/paper/src/chapters/02-literature-review.tex @@ -49,7 +49,7 @@ Our effort to combat contamination stems from research by \textcite{hardt_strate To bridge the gap between detection and robust pricing, we look at work in Distributionally Robust Optimization (DRO). As defined by \textcite{kuhn_wasserstein_2024}, DRO provides a framework for decision-making under ambiguity, where the true data distribution is unknown but lies within a ``Wasserstein ball'' of a target distribution. In our context, the ``ambiguity set'' represents the uncertainty introduced by agentic reconnaissance. By optimizing for the worst-case distribution within this set, pricing mechanisms can become resilient to the distributional shifts such as the ones caused by non-human actors, effectively robustifying the revenue function against the contamination described in our problem statement. -In order to create an environment in which prices can be tested against a demand estimate generated by some behavioral model, we take inspiration from the architecture proposed by \textcite{ie_recsim_2019} in the RecSim platform built for recommendation systems. By modeling the distinct user behavior as POMDPs we can generate faithful interactions which allow us to generalize, past the constraint which is also present in recommendation systems, of rarely having enough experience with individual actor's interactions for good recommendations without generalization. The key inspiration comes from the user choice modeling which we translate to a user transition model for each distinct actor type (agent or human). We further consider the possibility of modeling our quantitative research platform using dynamic Bayesian networks for the sake of tractability within the system. The contribution or RecSim enables researchers to better understand learning algorithms in fixed environments, a gap we identify as needing to be bridged within the space of dynamic pricing. +In order to create an environment in which prices can be tested against a demand estimate generated by some behavioral model, we take inspiration from the architecture proposed by \textcite{ie_recsim_2019} in the RecSim platform built for recommendation systems. By modeling the distinct user behavior as partially observable Markov decision processes, we can generate faithful interactions which allow us to generalize, past the constraint which is also present in recommendation systems, of rarely having enough experience with individual actor's interactions for good recommendations without generalization. The key inspiration comes from the user choice modeling which we translate to a user transition model for each distinct actor type (agent or human). We further consider the possibility of modeling our quantitative research platform using dynamic Bayesian networks for the sake of tractability within the system. The contribution or RecSim enables researchers to better understand learning algorithms in fixed environments, a gap we identify as needing to be bridged within the space of dynamic pricing. % TODO: mention https://github.com/meta-pytorch/OpenEnv/tree/main/envs/browsergym_env We also acknowledge the difficulty in similarly affected fields such as authorship, where \textcite{ganie_uncertainty_2025} demonstrate the theoretical limits of the distributional divergence between text authored by a human or large language model. Their approach of computing the divergence between two distributions demonstrates purely theoretically that no classifier can outperform random guessing on their particular task. This is yet another factor to take into consideration when exploring the potential mitigation strategies. diff --git a/paper/src/chapters/03-methodology.tex b/paper/src/chapters/03-methodology.tex index 799486e..4fec8a0 100644 --- a/paper/src/chapters/03-methodology.tex +++ b/paper/src/chapters/03-methodology.tex @@ -7,7 +7,7 @@ This section details the theoretical and practical framework developed to addres \subsection{Problem Formalization} -We define a commercial environment where the platform interacts with a stream of sessions. Let $\mathcal{S}$ denote the set of all sessions. Each session $s \in \mathcal{S}$ is generated by an actor belonging to a latent class $\theta_s \in \{H, A\}$, where $H$ denotes Human and $A$ denotes Agent. +We define a commercial environment where the platform interacts with a stream of sessions. Let $\mathcal{S}$ denote the set of all sessions. Each session $s \in \mathcal{S}$ is generated by an actor belonging to a latent class $Y_s \in \{H, A\}$, where $H$ denotes Human and $A$ denotes Agent. Each session produces a trajectory of observable events $\tau_s = (e_{s,1}, \ldots, e_{s,L_s})$. An event $e_{s,k}$ is a tuple defined as: \begin{equation} @@ -20,7 +20,7 @@ where: \item $t_{s,k} \in \mathbb{R}_+$ is the continuous timestamp. \end{itemize} -The platform does not directly observe the true underlying demand function $d(p)$. Instead, it observes a behavioral proxy $\hat{q}_t$, which is a composite signal derived from the mixture of actor types. We define the demand proxy for product $i$ at epoch $t$ as a weighted aggregation of events: +The platform does not directly observe the true underlying demand function $d(p)$ where $d \in \mathbb{R}^{+}$ and our proxy $\hat{q} \in \mathbb{R}^{+}$. Instead, it observes a behavioral proxy $\hat{q}_t$, which is a composite signal derived from the mixture of actor types. We define the demand proxy for product $i$ at epoch $t$ as a weighted aggregation of events: \begin{equation} \label{eq:qhat} \hat{q}_{t,i} = \sum_{s \in \mathcal{S}_t} \sum_{k=1}^{L_s} \omega(a_{s,k}) \cdot \mathbf{1}[i_{s,k} = i] @@ -34,19 +34,20 @@ In the current engine implementation, we use the normalized variant of this prox with fixed category-level weights (cart, dwell, nav, filter) following the same rank order from Table~\ref{tab:action_space}. This keeps the signal dense and directly usable in the simulator. \subsubsection{Actor Types and Demand Curves} -We formalize the heterogeneity of actors by introducing a type space $\Theta$. An actor of class $Y_s$ is further parameterized by a type $\theta \sim \mathcal{D}_{Y}$. This type determines the actor's demand response function $d(p; \theta)$, sampled from a distribution of possible demand curves. The total observed demand is a stochastic process governed by the naively defined mixture: +We formalize the heterogeneity of actors by introducing a type space $\Theta$. An actor of class $Y_s$ is further parameterized by a type $\theta \sim \mathcal{D}_{Y_s}$. This type determines the actor's demand response function $d\!\left(p \mid Y_s,\theta\right)$, sampled from a distribution of possible demand curves. In compact form, demand remains price-dependent as $d(p\mid Y=y)$. The total observed demand is a stochastic process governed by the naively defined mixture: \begin{equation} \label{eq:mixture_demand} -Q(p) = (1-\alpha) \cdot \mathbb{E}_{\theta \sim \mathcal{D}_H}[d(p; \theta)] + \alpha \cdot \mathbb{E}_{\theta \sim \mathcal{D}_A}[d(p; \theta)] + \epsilon_t +Q(p) = (1-\alpha) \cdot \mathbb{E}_{\theta \sim \mathcal{D}_H}[d(p\mid Y=H,\theta)] + \alpha \cdot \mathbb{E}_{\theta \sim \mathcal{D}_A}[d(p\mid Y=A,\theta)] + \epsilon_t \end{equation} where $\alpha \in [0, 1]$ represents the contamination parameter (proportion of agents) and $\epsilon_t$ is non-stationary market noise. +We address that the composition of two non-stationary variables can cause difficulty distinguishing the sources of possible dynamic composition in online environments, whether from market noise or agents specifically. Accounting for behavioral and market variation, we also treat $\epsilon_t$ as absorbing serving-path variability from LLM infrastructure (e.g., batch-size-dependent inference behavior under changing load), which appears stochastic at the request level even under greedy decoding \parencite{horace_he_and_thinking_machines_lab_defeating_2025}. \subsection{Cost of Information (COI) Framework} -The platform's pricing power comes from information asymmetry: users who express strong interest signals pay more than the base price. We quantify this markup as the \textit{Cost of Information} (COI), which represents the average premium extracted above marginal cost. COI measures the revenue at risk when information asymmetry collapses. +The platform's pricing power comes from information asymmetry: users who express strong interest signals pay more than the base price. We quantify this markup as the \textit{Cost of Information} (COI), which represents the average premium extracted above marginal cost. The intuition behind this being a cost comes from the perspective of the user who is interacting with the platform, where the user is the one incurring that ``cost.'' COI measures the revenue at risk when information asymmetry collapses. A top-level view in the current AI discourse is that sufficiently large productivity gains can induce vertical deflation through cost compression and supply expansion \parencite{rachitsky_marc_2026}. Our contribution is narrower and mechanism-level: even under long-run deflation, platform revenue still depends on short-run information costs to the user. We formalize that rent as the Cost of Information (COI) and study how agentic reconnaissance accelerates its erosion. \begin{definition}[Cost of Information] @@ -88,7 +89,7 @@ where $\mathbb{E}[P]$ is the expected price charged by the policy and $\underlin \draw[<->, thick, red] (\pmin, 2.0) -- (\mean, 2.0) node[midway, above] {COI}; \end{tikzpicture} - \caption{Illustration of the Cost of Information (COI). The COI is defined as the difference between the expected price $\mathbb{E}[p]$ realized by the policy and the minimum viable price $\underline{p}$.} + \caption{Illustration of the Cost of Information (COI). The COI is defined as the difference between the expected price $\mathbb{E}[p]$ realized by the policy and the minimum viable price $\underline{p}$. The abstraction we assume is that the reservation price $\underline{p}$ already has some innate margin and would always result in at least a break-even transaction.} \label{fig:coi_illustration} \end{figure} @@ -159,14 +160,14 @@ The transformation that governs this dynamic pricing is a very simple surge-base \begin{equation} \hat{p}_i = \begin{cases} -p_{0,i} \cdot \lambda_{\text{surge}} & \text{if } \hat{q}_i \geq \theta_{\text{high}} \\ -p_{0,i} \cdot \lambda_{\text{disc}} & \text{if } \hat{q}_i \leq \theta_{\text{low}} \\ -p_{0,i} & \text{otherwise} + p_{0,i} \cdot \lambda_{\text{surge}} & \text{if } \hat{q}_i \geq \varrho_{\text{high}} \\ + p_{0,i} \cdot \lambda_{\text{disc}} & \text{if } \hat{q}_i \leq \varrho_{\text{low}} \\ + p_{0,i} & \text{otherwise} \end{cases} \quad \forall i \in \{1, \ldots, N\} \end{equation} -where $p_0 \in \mathbb{R}^N$ is the base price vector (which is seeded into our database distinctly for each mode of the commerce platform), $\theta_{\text{high}}, \theta_{\text{low}} \in \mathbb{R}$ are demand thresholds defining surge and discount regions, and $\lambda_{\text{surge}}, \lambda_{\text{disc}} \in \mathbb{R}^+$ are multiplicative factors with typical values $\lambda_{\text{surge}} = 1.2$ and $\lambda_{\text{disc}} = 0.9$. This piecewise function enables rapid price adjustment in response to observed demand without requiring complex elasticity estimation or historical calibration, allowing us to expose actors within our experiments to a system with a dynamic component of pricing. +where $p_0 \in \mathbb{R}^N$ is the base price vector (which is seeded into our database distinctly for each mode of the commerce platform), $\varrho_{\text{high}}, \varrho_{\text{low}} \in \mathbb{R}$ are demand thresholds defining surge and discount regions, and $\lambda_{\text{surge}}, \lambda_{\text{disc}} \in \mathbb{R}^+$ are multiplicative factors with typical values $\lambda_{\text{surge}} = 1.2$ and $\lambda_{\text{disc}} = 0.9$. This piecewise function enables rapid price adjustment in response to observed demand without requiring complex elasticity estimation or historical calibration, allowing us to expose actors within our experiments to a system with a dynamic component of pricing. % For our offline experimental setting, we generalize a master value function that can encompass different demand estimation and pricing strategies. % @@ -183,6 +184,7 @@ We start from a practical constraint: we do not have access to proprietary produ The interface is organized as a product catalog where each product belongs to a time-bounded price vector (for example, a daily pricing period). During each period we collect interaction data by instrumenting UI components and predefined action templates that are still customizable. This gives us control without losing realism. Since users act with motivations, we define a pool of tasks (jobs to be done) and assign tasks randomly to participants. +We discuss limitations and choices made in this experimental design in Section~\ref{sec:limitations_risks}. The task pool is stored as a structured table with fields \texttt{id}, \texttt{created\_at}, \texttt{task\_name}, \texttt{task\_description}, and \texttt{task\_def\_of\_done}. We formulate the tasks as compact jobs-to-be-done rather than as strict click scripts, because the target is to elicit realistic browsing and comparison behavior which can capture nuance of different people. In hotel mode the assigned tasks include \textit{Cheapest Room}, \textit{Cheapest Room w/ View}, \textit{MultiStep Cheapest Room}, \textit{The Digital Nomad (Executive)}, and \textit{The 3-Way Tradeoff (Desk + Quiet + Flexible)}. These prompts deliberately require critical thought in search, inspection of room details, comparison of amenities or images, return visits to the listing page, and a final booking decision which create a degree of cognitive load. In airline mode we use \textit{Last-Minute One-Way Flight}, where the actor must urgently travel to LAX from either SEA or JFK within the next 1--3 days, inspect at least a small set of candidate itineraries, and then book a reasonable earliest departure. A representative task is to find the cheapest feasible catalog item under explicit constraints while removing strict financial limits so we avoid trivial optimization behavior. Participants are also randomly assigned to one experimental platform mode (hotel or airline). Once assigned, they are dropped into the experiment with an actor ID. Under each experiment ID, we can observe multiple sessions across time and gather long interaction traces for the same actor. @@ -190,7 +192,7 @@ The human data collection involved 13 participants, all of whom provided explici To evaluate quality and realism of the setup, we store both structured event logs and full interaction transcripts. This lets us combine quantitative analysis with transcript-level qualitative findings. The result is an isolated system where we can control the interaction process while preserving realistic behavior. -Operationally, goals and experiment runs are tracked in PostgreSQL (goal table, run table, and assignment mapping). This data-acquisition phase is the first half of the methodology and is intentionally a disconnected component that feeds the later contributions. The second half uses collected behavioral traces to distinguish classes $\theta \in \{A,H\}$ with session-conditioned probability estimates, then injects those estimates into the pricing learner. +Operationally, goals and experiment runs are tracked in PostgreSQL (goal table, run table, and assignment mapping). This data-acquisition phase is the first half of the methodology and is intentionally a disconnected component that feeds the later contributions. The second half uses collected behavioral traces to distinguish classes $Y \in \{A,H\}$ with session-conditioned probability estimates, then injects those estimates into the pricing learner. Our process follows three stages: (1) observe and \textit{vectorize} behavioral interactions, (2) learn distinguishability to characterize human versus agent patterns, and (3) use the learned signal to train a defensive policy in a controlled dynamic-pricing simulator. @@ -263,7 +265,7 @@ v4 & 64 (32 + 32) & us-central2-b & 32 Spot + 32 On-demand \\ For connections from Madrid, we prioritize the europe-west4 allocation for latency-sensitive runs with the benefit of having the most grouped chips within a single region. This regional grouping is important for the deployment of our Kubernetes cluster which cannot span multiple regions. All sweep metadata, model checkpoints, and reward traces are logged in Weights \& Biases. % TODO: cite this (from bib) Hardware specifications are from the official Google Cloud TPU documentation \parencite{noauthor_tpu_2026,noauthor_tpu_2025-1,noauthor_tpu_2025}. -Design of training processes: we build docker image with the fact in mind of different caching over layers in order to most speed up docker re-building and such we place the most volatile steps towards the end of the image building. What is means in practice is that any dependency installations are isolated so edits to source code do no trigger rebuilds. Only if we update our entry point of training a sweep, Docker will also rebuild the source-code copy stage. +Design of training processes: we build docker image with the fact in mind of different caching over layers in order to most speed up docker re-building and such we place the most volatile steps towards the end of the image building. What is means in practice is that any dependency installations are isolated so edits to source code do no trigger rebuilds. Only if we update our entry point of training a sweep, Docker will also rebuild the source-code copy stage. % TODO: cite Docker best practices on cache-efficient Dockerfile layering. Due to the preemptive nature of the current demand of TPU chips we sttle for running our on demeaned as the primary source of compute. The on demand TPU pod of 32 chips spread across 4 virtual hosts creates a relatively unique parallelization setup. Despite our desire to use a traditional approach of clustering and perhaps deploying SLURM jobs of our sweep agent, the lack of predictability in provisioning each instance of a compute resource makes this an high friction layer we do not want to add. @@ -300,8 +302,9 @@ $\mathcal{A}_{\text{filter}}$ & \texttt{search}, \texttt{filter\_date}, \texttt{ \end{table} This partition enables the weight function $\omega$ from Eq.~\ref{eq:qhat} to assign category-specific signal strengths, with $\omega(\mathcal{A}_{\text{cart}}) > \omega(\mathcal{A}_{\text{dwell}}) > \omega(\mathcal{A}_{\text{nav}}) > \omega(\mathcal{A}_{\text{filter}})$ reflecting decreasing commitment. -Its important to acknowledge that this creates a very blatant assumption in the weighting, we do motivate the scale of each weight by the per-category observed divergence between each behavioral profile. +It's important to acknowledge that this creates a very blatant assumption in the weighting, and we motivate the scale of each weight by the per-category observed divergence between each behavioral profile. In the simulator baseline this order is encoded with a compact fixed scale: cart $=4.0$, dwell $=2.0$, nav $=1.0$, filter $=0.5$. Unknown actions are mapped by prefix heuristics to the nearest category. +We back this up by saying that each weight was assigned by observing an initial small dataset and computing KL divergence between each interaction type; the ones with the highest divergence receive a proportionately high weight in our demand estimation. The metadata record $\mu$ varies by action type. For product views, $\mu$ contains the observed price $p_{\text{obs}}$ and product attributes. For dwell events, $\mu$ includes the element text and accumulated hover duration. This heterogeneous structure is captured via a schema-on-read approach in our Kafka ingestion pipeline, where events are validated against type-specific schemas before storage. @@ -316,7 +319,7 @@ To train a robust pricing learner, we need a simulator that can generate realist \subsubsection{Ground-Truth Distinguishability} -Because sessions are collected under controlled experimental conditions where each actor is assigned a known type at the start of the trial, labels $\theta_s \in \{H, A\}$ are available as ground truth rather than as the output of a heuristic classifier. We therefore estimate separate transition kernels directly from each labeled partition $\mathcal{D}_H$ and $\mathcal{D}_A$, treating the resulting $\hat{\mathcal{T}}_H$ and $\hat{\mathcal{T}}_A$ as the ground-truth behavioral profiles for each class. We then ask a direct methodological question: are the kernels distinguishable enough to justify downstream pricing control that depends on that distinguishability? +Because sessions are collected under controlled experimental conditions where each actor is assigned a known type at the start of the trial, labels $Y_s \in \{H, A\}$ are available as ground truth rather than as the output of a heuristic classifier. We therefore estimate separate transition kernels directly from each labeled partition $\mathcal{D}_H$ and $\mathcal{D}_A$, treating the resulting $\hat{\mathcal{T}}_H$ and $\hat{\mathcal{T}}_A$ as the ground-truth behavioral profiles for each class. We then ask a direct methodological question: are the kernels distinguishable enough to justify downstream pricing control that depends on that distinguishability? To answer this, we compute per-session KL divergence scores against both class-level centroids. For each session $s$ in either partition, we fit a session-level event transition kernel $\hat{\mathcal{T}}_s$ from that session's trajectory alone, then compute its average KL divergence to the human centroid ($\Delta_{H,s}$) and to the agent centroid ($\Delta_{A,s}$). The per-session distinguishability score is the gap $\Delta_{H,s} - \Delta_{A,s}$: a negative value indicates proximity to human behavior, a positive value indicates proximity to agent behavior. The reason behind KL divergence for profile analysis is grounded in its nature and tailored characteristics for probability distributions. @@ -329,6 +332,7 @@ Let $P_e$ and $Q_e$ be categorical distributions over destination states followi \end{equation} where $\mathcal{S}_e$ denotes the set of destination events that follow $e$ in the human trajectories. \end{definition} +The asymmetry of KL divergence is a point we leverage to natively create divergence from human behavior, to gather signal of the dissimilarity from human-like interactions. To obtain this statistic, we aggregate transitions by triggering event $e$ and treat normalized outgoing probabilities as categorical distributions $P_e$ (human) and $Q_e$ (agent). We intersect shared event labels, then accumulate log-ratio contributions over shared destinations. Large contributions, including near-zero $Q_e(k)$ cases, identify transitions where one actor class is difficult to mimic. @@ -366,6 +370,7 @@ To scale this to catalog-level pricing, we expand the base event transition matr \subsection{Distributionally Robust Reinforcement Learning (DR-RL)} We formulate pricing as a Stackelberg game: the platform (leader) sets prices $p_t$, and the population (follower) responds through trajectories and demand. A useful intuition is that the platform behaves like a distorted mirror at a 45-degree angle: what it mirrors is population demand into an estimated demand proxy, and that proxy drives revenue. +% TODO: add canonical Stackelberg citation. Because contamination level $\alpha$ and demand shift are non-stationary online, a simple error term is not enough. We therefore use a Distributionally Robust Optimization objective. Let $\tau'$ be a newly observed trajectory generated by an unknown actor profile (sampled from the behavioral models in Section~\ref{sec:tpe}). We need a demand mapping conditioned on price and trajectory, $\hat{Q}(p,\tau')$. For each $\tau'$, we compute $\hat{\mathcal{T}}'$ and compare it with controlled baselines $\bar{\mathcal{T}}_H$ and $\bar{\mathcal{T}}_A$: @@ -425,7 +430,7 @@ and we evaluate a small fixed grid in $\mathcal{A}_{\epsilon_\alpha}(\alpha_0)$ \subsubsection{Environment Setup for Dynamic Pricing} -The complete pricing-demand-trajectory loop is illustrated in Figure~\ref{fig:oracle_flow}. The Oracle maps historical price and demand state to a new price vector, which is exposed to a distribution of demand curves. Each product generates trajectories weighted by behavioral kernels $\tau_\theta$, producing a full transition matrix $\tau'$ over sessions. Sampled trajectories $\{\tau_k\}$ are aggregated through the demand proxy function $Q(\cdot)$ to yield the next demand vector, which feeds back into the Oracle. +The complete pricing-demand-trajectory loop is illustrated in Figure~\ref{fig:oracle_flow}. The Oracle maps historical price and demand state to a new price vector, which is exposed to a distribution of demand curves. Each product generates trajectories weighted by behavioral kernels $\tau_Y$, producing a full transition matrix $\tau'$ over sessions. Sampled trajectories $\{\tau_k\}$ are aggregated through the demand proxy function $Q(\cdot)$ to yield the next demand vector, which feeds back into the Oracle. \begin{figure}[ht] \centering @@ -441,7 +446,7 @@ p_N \end{pmatrix} \underrightarrow{d_i \sim \mathcal{N}_{\vec{p}}} \begin{pmatrix}d_0\\ d_1\\ \cdots \\ d_N\end{pmatrix} -\underrightarrow{\vec{d}\otimes \tau_\theta} +\underrightarrow{\vec{d}\otimes \tau_Y} \begin{bmatrix} 0.01 & 0.02 & \cdots & 0.3 \\ 0.41 & 0.24 & \cdots & 0.0 \\ @@ -461,7 +466,7 @@ p_N \end{aligned} $}% } -\caption{Oracle-based pricing loop: historical price and demand state map to a new price vector; each product samples demand curves from $\mathcal{N}_{\vec{p}}$; trajectories are generated via the Kronecker product $\vec{d}\otimes\tau_\theta$ into transition matrix $\tau'$; sampled trajectories $\{\tau_k\}$ aggregate through proxy $Q(\cdot)$ to yield updated demand $\vec{\hat{q}}$, closing the feedback loop.} +\caption{Oracle-based pricing loop: historical price and demand state map to a new price vector; each product samples demand curves from $\mathcal{N}_{\vec{p}}$; trajectories are generated via the Kronecker product $\vec{d}\otimes\tau_Y$ into transition matrix $\tau'$; sampled trajectories $\{\tau_k\}$ aggregate through proxy $Q(\cdot)$ to yield updated demand $\vec{\hat{q}}$, closing the feedback loop.} \label{fig:oracle_flow} \end{figure} @@ -471,7 +476,8 @@ The robust policy $\pi^*$ is obtained by solving the maximin problem: \label{eq:robust_policy} \pi^* = \arg \max_{\pi} \min_{Q \in \mathcal{U}_\epsilon} \mathbb{E}_{d \sim Q} \left[ R(p, d) - \lambda \cdot \text{COI}_{\text{leak}}(p,\tau') \right] \end{equation} -where $R(p, d)$ is the revenue function and $\lambda$ weighs the information-leakage penalty. We note that $p$ is directly dependent on $\pi$ which is the one deicing that as its action. +where $R(p, d)$ is the revenue function and $\lambda$ weighs the information-leakage penalty. We note that $p$ is directly dependent on $\pi$, which is the one deciding this as its action. +Looking at the reward structure, note that we are not subtracting COI but rather the leakage of COI, which is as defined below. In practice, we parameterize this with a session-level leakage term: @@ -492,12 +498,12 @@ with fixed $c_{\text{info}}>0$. Another possible extension is to adapt the ambiguity radius online, e.g., $\epsilon(\Delta_H)$, so the Wasserstein ball changes with live divergence. We keep this as future work and retain a fixed-radius setup because Wasserstein ambiguity already handles heavy-tail and ``black swan'' behavior without absolute continuity assumptions \parencite{kuhn_wasserstein_2024}. \subsubsection{Actor Implementation} -In our simulation, the ``follower'' is implemented as a set of Actors. Each Actor is initialized with a type $\theta$ which samples a specific demand curve $d(p; \theta)$ from the latent distribution. This formalization ensures that our DR-RL agent does not overfit to a single deterministic demand function but learns a policy robust to the distributional uncertainty defined by $\mathcal{U}_\epsilon$. +In our simulation, the ``follower'' is implemented as a set of Actors. Each Actor is initialized with a class $Y$ and a latent type $\theta \sim \mathcal{D}_Y$, which samples a specific demand curve $d\!\left(p\mid Y,\theta\right)$ from the latent distribution. This formalization ensures that our DR-RL agent does not overfit to a single deterministic demand function but learns a policy robust to the distributional uncertainty defined by $\mathcal{U}_\epsilon$. Practical implementation of browser agents is a strongly evolving field with near-weekly releases of SOTA architectures. In this thesis implementation we abstract that layer into trajectory generators learned from observed human/agent transition kernels. -As part of reward engineering, we keep a UX factor ($UX\in[0,1]$) as an auxiliary evaluation axis. In the current baseline it is not injected into the core reward; it is tracked separately to compare policy trade-offs. +As part of reward engineering, we keep a UX factor ($UX\in[0,1]$) as an auxiliary evaluation axis. In code, the UX index is implemented as a volatility penalty on relative price changes, with an extra upward-volatility component weighted by $0.5$ and scaled by $\eta_{\text{ux}}$ and an information-budget term. We also keep a separate supra-competitive penalty tied to persistent price excess above a competitive anchor, which punishes high-price behavior even when volatility is low. \begin{figure}[ht] \centering @@ -541,7 +547,7 @@ We now present the complete pricing mechanism that integrates the behavioral dis \end{algorithm} -The algorithm operates in discrete epochs indexed by $t$. At each epoch, the platform applies one discrete multiplicative price action, the environment samples a batch of sessions, and demand is recomputed from weighted events. Robustness is implemented as an inner minimization over a small local grid of contamination candidates around nominal $\alpha_0$, matching the current engine implementation. The history buffer $\mathcal{L}$ (``Limbo'' in our implementation) enforces the alternating Stackelberg structure by preserving the temporal sequence of price publications and demand observations. +The algorithm operates in discrete epochs indexed by $t$. At each epoch, the platform applies one discrete multiplicative price action, the environment samples a batch of sessions, and demand is recomputed from weighted events. Robustness is implemented as an inner minimization over a small local grid of contamination candidates around nominal $\alpha_0$, matching the current engine implementation. The history buffer $\mathcal{L}$ (what we are calling the ``Limbo'' stack in our implementation) enforces the alternating Stackelberg structure by preserving the temporal sequence of price publications and demand observations. %The defensive price update in Line 24 implements contamination-aware margin shrinkage: as estimated contamination $\hat{\alpha}_t$ rises, the margin $(p^{\mathrm{ref}} - c)$ is reduced by factor $\kappa\in[0,1]$, with projection $\Pi_{\mathcal{P}}$ ensuring feasibility. In subsequent experiments this heuristic rule is replaced by DR-RL policy $\pi^*$ from Eq.~\ref{eq:robust_policy}. diff --git a/paper/src/chapters/04-results.tex b/paper/src/chapters/04-results.tex index fe22c49..a9c9ae2 100644 --- a/paper/src/chapters/04-results.tex +++ b/paper/src/chapters/04-results.tex @@ -40,12 +40,26 @@ We report two preliminary stages before the full factorial interpretation. First \subsubsection{The Impact of Contamination on Revenue} -The contamination--revenue slope is estimated on a controlled cohort (single sweep, baseline policy, $n_{\text{products}}=100$, $n=95$). In this setting, contamination $\alpha$ is set exogenously by the experiment, so the slope identifies the within-sweep causal effect of contamination on revenue under fixed policy and environment settings. The fitted linear model is +The contamination--revenue slope is estimated on a controlled cohort (single sweep, baseline policy, $n_{\text{products}}=100$, $n=95$). In this setting, contamination $\alpha$ is set exogenously by the experiment, so the slope identifies the within-sweep causal effect of contamination on revenue under fixed policy and environment settings. -\[ -\widehat{y}=348{,}823.41-90{,}140.53\,\alpha, -\] -with $t(93)=-61.45$, $p=4.27\times10^{-77}$, $R^2=0.976$, and a 95\% confidence interval for the slope of $[-93{,}053.38,\,-87{,}227.68]$. Interpreted on the contamination grid, a $+0.1$ increase in $\alpha$ corresponds to an average revenue decrease of about $9{,}014$ units. A heteroskedasticity-robust check (HC1) preserves the same direction and significance ($t=-41.25$, $p=1.42\times10^{-61}$), supporting a large and statistically stable impact in this controlled regime. +\begin{table}[ht] +\centering +\caption{Slope verification table for contamination versus revenue (OLS-style report).} +\label{tab:contamination_slope_table} +\begin{tabular}{@{}lrrrrr@{}} +\toprule +Term & Coef. & Std. Err. & $t$ & $p>|t|$ & 95\% CI \\ +\midrule +Intercept & 348,823.41 & 784.29 & 444.77 & $<10^{-99}$ & $[347,264.96,\,350,381.86]$ \\ +$\alpha$ & $-90,140.53$ & 1,466.90 & $-61.45$ & $4.27\times10^{-77}$ & $[-93,053.38,\,-87,227.68]$ \\ +\midrule +HC1 robust check ($\alpha$) & $-90,140.53$ & 2,185.22 & $-41.25$ & $1.42\times10^{-61}$ & -- \\ +\bottomrule +\end{tabular} +\end{table} + +Interpreted on the contamination grid, a $+0.1$ increase in $\alpha$ corresponds to an average revenue decrease of about $9{,}014$ units, and the robust check preserves both direction and significance. +% TODO: add a compact proposal note for re-running tests with statsmodels in the appendix methodology notes. \subsubsection{Large Scale Factorial Training} diff --git a/paper/src/chapters/05-discussion.tex b/paper/src/chapters/05-discussion.tex index 5b2512f..c926ad8 100644 --- a/paper/src/chapters/05-discussion.tex +++ b/paper/src/chapters/05-discussion.tex @@ -11,9 +11,23 @@ However, ecommerce commodities differ fundamentally from financial securities: t \subsection{Risk Assessment and Limitations} +\label{sec:limitations_risks} This technology does not come without a more bitter side, ethical concerns do arise from the idea of deploying black-box like solutions to set prices based on a behavioral attributes. Approaches like universal behavioral profile modeling (UBPM) used in recommendation systems is very broadly utilized. +In our experimental setup we randomly assign each user to a platform and, within that platform, assign them to a task. Figure~\ref{fig:exp_design_tree} summarizes this design decision tree. + +\begin{figure}[ht] + \centering + \resizebox{0.92\columnwidth}{!}{% + \input{chapters/figures/experiment_design_tree.tex} + } + \caption{Experimental design decision tree for participant assignment.} + \label{fig:exp_design_tree} +\end{figure} + +Although our participant sample size is somewhat low for humans, we do a one-to-one balance of human-to-agent experimental sessions. This way we are observing a uniform distribution of participation from each participating side. Our sample size of participants might look scarce, but each participant generates a rich amount of data, with a totality of 3,874 rows of data. + With a system like this there is potential for strong drift given the rapid advance of agentic systems and user preference. Our intent behind adding the UX term into the reward shaping process was to further address the risk of degraded user experience. Looking deeper at the underlying methodology, reinforcement learning does not come without it's complications such as reward hacking and often the lack of intepretability which is quite critical in systems that have a strong impact on the revenue of a company. % \subsection{Implications of Findings} Interpretation of results and altenrative scenarios with broader market implications. diff --git a/paper/src/chapters/figures/experiment_design_tree.tex b/paper/src/chapters/figures/experiment_design_tree.tex new file mode 100644 index 0000000..d37379d --- /dev/null +++ b/paper/src/chapters/figures/experiment_design_tree.tex @@ -0,0 +1,17 @@ +\begin{tikzpicture}[ + level distance=14mm, + sibling distance=36mm, + decision/.style={rectangle, draw, rounded corners=2pt, align=center, minimum width=26mm, minimum height=8mm, font=\small}, + leaf/.style={rectangle, draw, align=center, minimum width=30mm, minimum height=8mm, font=\small}, + edge from parent/.style={draw, -{Latex[length=2mm]}} +] +\node[decision] {Participant} + child { + node[decision] {Platform: Hotel} + child {node[leaf] {Task sampled\\from hotel pool}} + } + child { + node[decision] {Platform: Airline} + child {node[leaf] {Task sampled\\from airline pool}} + }; +\end{tikzpicture} diff --git a/paper/src/main.tex b/paper/src/main.tex index 555fbc3..2046342 100644 --- a/paper/src/main.tex +++ b/paper/src/main.tex @@ -48,14 +48,14 @@ These behavioral signals serve as inputs for a Distributionally Robust Reinforce \begin{description} \item[Agent $A$] A non-human actor, typically an LLM-driven system that executes web actions toward a goal. \item[Human $H$] A human participant interacting with the platform to complete a task. -\item[Actor Type $\theta$] A latent class parameter describing whether a session is generated by a human or an agent profile. +\item[Actor Class $Y$] A latent class parameter describing whether a session is generated by a human or an agent profile. \item[Platform] A web interface exposing purchasable items and their offered prices. \item[Session $s$] A bounded interaction record tied to one actor and one session identifier. \item[Event $e_{s,k}$] A single interaction tuple in a session, including action, item target, and timestamp. \item[Trajectory $\tau_s$] The ordered sequence of events generated within a session. \item[Demand Proxy $\hat{q}_{t,i}$] A weighted aggregate of observed actions used as an operational substitute for latent demand. \item[Action Weight Function $\omega(a)$] A mapping from action type to signal strength in the demand proxy. -\item[True Demand $d(p;\theta)$] The latent purchase response as a function of price and actor type. +\item[True Demand $d(p\mid Y,\theta)$] The latent purchase response as a function of price, actor class, and latent type. \item[Contamination $\alpha$] The proportion of agent-generated traffic in the session mixture. \item[Non-stationary Noise $\epsilon_t$] Time-varying residual variation not explained by the actor mixture. \item[Pricing Policy $\pi(\tau)$] A function mapping observed interaction history to an offered price. From e18a3e73634bf031f96f92469d47d56e091443bf Mon Sep 17 00:00:00 2001 From: Daniel Rosel Date: Wed, 8 Apr 2026 11:58:20 +0200 Subject: [PATCH 11/44] scary --- Makefile | 20 +- paper/defense/manim/requirements.txt | 2 - paper/src/chapters/03-methodology.tex | 2 +- paper/src/chapters/04-results.tex | 10 +- paper/src/chapters/06-conclusion.tex | 4 +- .../legacy/first_sweep_headline_summary.json | 10 - .../legacy/first_sweep_tier_alpha_deltas.csv | 31 -- .../first_sweep_tier_alpha_mode_summary.csv | 61 ---- .../legacy/first_sweep_tier_mode_summary.csv | 11 - .../legacy/first_sweep_top_configs.csv | 26 -- .../legacy/plots/first_sweep_tier_revenue.pdf | Bin 17510 -> 0 bytes .../legacy/plots/ppo_alpha_curves.pdf | Bin 25398 -> 0 bytes .../legacy/plots/ppo_delta_curves.pdf | Bin 21999 -> 0 bytes .../legacy/plots/ppo_tradeoff_scatter.pdf | Bin 26824 -> 0 bytes .../generated/legacy/ppo_alpha_deltas.csv | 7 - .../legacy/ppo_alpha_mode_summary.csv | 13 - .../legacy/ppo_headline_summary.json | 7 - .../legacy/ppo_overall_mode_summary.csv | 3 - .../legacy/ppo_pairwise_win_rates.csv | 25 -- .../{final => }/final_focus_coi_by_alpha.tex | 0 .../final_focus_coi_preservation_grid.tex | 0 .../final_focus_revenue_by_alpha.tex | 0 .../{final => }/final_focus_revenue_delta.tex | 0 .../{final => }/final_focus_risk_deltas.tex | 0 .../legacy/first_sweep_tier_revenue.tex | 1 - .../includes/legacy/ppo_alpha_curves.tex | 1 - .../includes/legacy/ppo_delta_curves.tex | 1 - .../includes/legacy/ppo_tradeoff_scatter.tex | 1 - .../chapters/figures/results/plot_results.py | 313 ------------------ .../figures/results/process_all_results.py | 51 --- .../figures/results/process_final_sweeps.py | 2 +- .../figures/results/process_first_sweep.py | 272 --------------- .../figures/results/process_ppo_benchmark.py | 277 ---------------- .../figures/results/revenue_alpha_classic.py | 63 ---- paper/src/chapters/mdp_agent.pdf | Bin 10932 -> 10932 bytes paper/src/chapters/mdp_human.pdf | Bin 11953 -> 11953 bytes 36 files changed, 28 insertions(+), 1186 deletions(-) delete mode 100644 paper/defense/manim/requirements.txt delete mode 100644 paper/src/chapters/figures/results/generated/legacy/first_sweep_headline_summary.json delete mode 100644 paper/src/chapters/figures/results/generated/legacy/first_sweep_tier_alpha_deltas.csv delete mode 100644 paper/src/chapters/figures/results/generated/legacy/first_sweep_tier_alpha_mode_summary.csv delete mode 100644 paper/src/chapters/figures/results/generated/legacy/first_sweep_tier_mode_summary.csv delete mode 100644 paper/src/chapters/figures/results/generated/legacy/first_sweep_top_configs.csv delete mode 100644 paper/src/chapters/figures/results/generated/legacy/plots/first_sweep_tier_revenue.pdf delete mode 100644 paper/src/chapters/figures/results/generated/legacy/plots/ppo_alpha_curves.pdf delete mode 100644 paper/src/chapters/figures/results/generated/legacy/plots/ppo_delta_curves.pdf delete mode 100644 paper/src/chapters/figures/results/generated/legacy/plots/ppo_tradeoff_scatter.pdf delete mode 100644 paper/src/chapters/figures/results/generated/legacy/ppo_alpha_deltas.csv delete mode 100644 paper/src/chapters/figures/results/generated/legacy/ppo_alpha_mode_summary.csv delete mode 100644 paper/src/chapters/figures/results/generated/legacy/ppo_headline_summary.json delete mode 100644 paper/src/chapters/figures/results/generated/legacy/ppo_overall_mode_summary.csv delete mode 100644 paper/src/chapters/figures/results/generated/legacy/ppo_pairwise_win_rates.csv rename paper/src/chapters/figures/results/includes/{final => }/final_focus_coi_by_alpha.tex (100%) rename paper/src/chapters/figures/results/includes/{final => }/final_focus_coi_preservation_grid.tex (100%) rename paper/src/chapters/figures/results/includes/{final => }/final_focus_revenue_by_alpha.tex (100%) rename paper/src/chapters/figures/results/includes/{final => }/final_focus_revenue_delta.tex (100%) rename paper/src/chapters/figures/results/includes/{final => }/final_focus_risk_deltas.tex (100%) delete mode 100644 paper/src/chapters/figures/results/includes/legacy/first_sweep_tier_revenue.tex delete mode 100644 paper/src/chapters/figures/results/includes/legacy/ppo_alpha_curves.tex delete mode 100644 paper/src/chapters/figures/results/includes/legacy/ppo_delta_curves.tex delete mode 100644 paper/src/chapters/figures/results/includes/legacy/ppo_tradeoff_scatter.tex delete mode 100644 paper/src/chapters/figures/results/plot_results.py delete mode 100644 paper/src/chapters/figures/results/process_all_results.py delete mode 100644 paper/src/chapters/figures/results/process_first_sweep.py delete mode 100644 paper/src/chapters/figures/results/process_ppo_benchmark.py delete mode 100644 paper/src/chapters/figures/results/revenue_alpha_classic.py diff --git a/Makefile b/Makefile index 9e2d5d2..754751c 100644 --- a/Makefile +++ b/Makefile @@ -44,7 +44,7 @@ SWEEP_ENV_LOAD = set -a; [ -f "$(SWEEP_ENV_FILE)" ] && . "$(SWEEP_ENV_FILE)" || .PHONY: help help: - @echo "pdf.build pdf.watch pdf.clean pdf.genpop pdf.genpop.watch pdf.arxiv | test.backend test.e2e test.all | web.dev | install | train | benchmark | benchmark.simple | benchmark.agent | train.agent | train.bootstrap | stats.lines | manim.render manim.render.all" + @echo "pdf.build pdf.watch pdf.clean pdf.genpop pdf.genpop.watch pdf.arxiv | test.backend test.e2e test.all | web.dev | install | train | benchmark | benchmark.simple | benchmark.agent | train.agent | train.bootstrap | stats.lines | manim.defense manim.defense.hq manim.render manim.render.full manim.render.poster manim.render.appendix manim.render.all" @echo "backend.server backend.provider backend.worker | platform.up platform.down platform.logs | docker.train.publish" @echo "data.pull data.push data.whoclicked.publish | study.margin-erosion study.margin-erosion.quick study.margin-erosion.plot" @echo "tpu.ray.bootstrap tpu.ray.deps tpu.ray.verify tpu.ray.teardown" @@ -235,9 +235,25 @@ count-lines: all: @$(NX) run paper:build -.PHONY: manim.render manim.render.all +.PHONY: manim.defense manim.defense.hq manim.render manim.render.full manim.render.poster manim.render.appendix manim.render.all +# Main defense reel (paper/defense/manim/render_defense); uses paper/defense/.venv when present +manim.defense: + @cd paper/defense/manim && ./render_defense full + +manim.defense.hq: + @cd paper/defense/manim && ./render_defense full --quality qh + manim.render: @$(NX) run manim:render +manim.render.full: + @$(NX) run manim:render-full + +manim.render.poster: + @$(NX) run manim:render-poster + +manim.render.appendix: + @$(NX) run manim:render-appendix + manim.render.all: @$(NX) run manim:render-all diff --git a/paper/defense/manim/requirements.txt b/paper/defense/manim/requirements.txt deleted file mode 100644 index 4da4506..0000000 --- a/paper/defense/manim/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -manim>=0.18,<1 -numpy>=1.24 diff --git a/paper/src/chapters/03-methodology.tex b/paper/src/chapters/03-methodology.tex index 4fec8a0..d6144d7 100644 --- a/paper/src/chapters/03-methodology.tex +++ b/paper/src/chapters/03-methodology.tex @@ -304,7 +304,7 @@ $\mathcal{A}_{\text{filter}}$ & \texttt{search}, \texttt{filter\_date}, \texttt{ This partition enables the weight function $\omega$ from Eq.~\ref{eq:qhat} to assign category-specific signal strengths, with $\omega(\mathcal{A}_{\text{cart}}) > \omega(\mathcal{A}_{\text{dwell}}) > \omega(\mathcal{A}_{\text{nav}}) > \omega(\mathcal{A}_{\text{filter}})$ reflecting decreasing commitment. It's important to acknowledge that this creates a very blatant assumption in the weighting, and we motivate the scale of each weight by the per-category observed divergence between each behavioral profile. In the simulator baseline this order is encoded with a compact fixed scale: cart $=4.0$, dwell $=2.0$, nav $=1.0$, filter $=0.5$. Unknown actions are mapped by prefix heuristics to the nearest category. -We back this up by saying that each weight was assigned by observing an initial small dataset and computing KL divergence between each interaction type; the ones with the highest divergence receive a proportionately high weight in our demand estimation. +We back this up by saying that each weight was assigned by observing an initial small dataset and computing KL divergence between each interaction type; the ones with the highest divergence receive a proportionately high weight in our demand estimation. From the order which we observe in divergences, we assign a multiple of 2 increase in weight ascending form the lowest weight of $0.5$ in rare filtering operations. The metadata record $\mu$ varies by action type. For product views, $\mu$ contains the observed price $p_{\text{obs}}$ and product attributes. For dwell events, $\mu$ includes the element text and accumulated hover duration. This heterogeneous structure is captured via a schema-on-read approach in our Kafka ingestion pipeline, where events are validated against type-specific schemas before storage. diff --git a/paper/src/chapters/04-results.tex b/paper/src/chapters/04-results.tex index a9c9ae2..f661bd3 100644 --- a/paper/src/chapters/04-results.tex +++ b/paper/src/chapters/04-results.tex @@ -73,35 +73,35 @@ In our complete training runs we logged $\approx 180$ days of net compute time. \begin{figure}[ht] \centering - \input{chapters/figures/results/includes/final/final_focus_revenue_by_alpha.tex} + \input{chapters/figures/results/includes/final_focus_revenue_by_alpha.tex} \caption{Revenue curves by contamination for the final cohort. The baseline remains above the defended curve in most cells, but the gap narrows in the high-contamination region.} \label{fig:final_focus_revenue_by_alpha} \end{figure} \begin{figure}[ht] \centering - \input{chapters/figures/results/includes/final/final_focus_coi_by_alpha.tex} + \input{chapters/figures/results/includes/final_focus_coi_by_alpha.tex} \caption{COI level curves by contamination for the final cohort. The shaded band marks the per-$\alpha$ gap between defended and baseline policies.} \label{fig:final_focus_coi_by_alpha} \end{figure} \begin{figure}[ht] \centering - \input{chapters/figures/results/includes/final/final_focus_coi_preservation_grid.tex} + \input{chapters/figures/results/includes/final_focus_coi_preservation_grid.tex} \caption{COI preservation by product count at the contamination endpoints ($\alpha=0.0$ and $\alpha=1.0$). Bars report defended-minus-baseline mean COI level, with the zero line separating preservation from erosion.} \label{fig:final_focus_coi_preservation_grid} \end{figure} \begin{figure}[ht] \centering - \input{chapters/figures/results/includes/final/final_focus_revenue_delta.tex} + \input{chapters/figures/results/includes/final_focus_revenue_delta.tex} \caption{Defended-minus-baseline revenue delta over contamination for the final cohort. The strongest high-contamination deviation begins at $\alpha=0.7$, followed by recovery toward near parity by $\alpha=1.0$.} \label{fig:final_focus_revenue_delta} \end{figure} \begin{figure}[ht] \centering - \input{chapters/figures/results/includes/final/final_focus_risk_deltas.tex} + \input{chapters/figures/results/includes/final_focus_risk_deltas.tex} \caption{Defended-minus-baseline leakage and volatility deltas for the final cohort. Leakage remains lower for the defended policy across the full contamination range.} \label{fig:final_focus_risk_deltas} \end{figure} diff --git a/paper/src/chapters/06-conclusion.tex b/paper/src/chapters/06-conclusion.tex index a905bbb..34b01c7 100644 --- a/paper/src/chapters/06-conclusion.tex +++ b/paper/src/chapters/06-conclusion.tex @@ -21,4 +21,6 @@ Now we very explicitly mention what we contribute in this paper: \subsection{Future Works and Next Steps} -During the eights months of research dedicated to this work, a plethora of opportunities and industry gaps was identified, sadly a majority of which could not be addressed directly. +In our effort to tackle this work we initiated a set of constraints which we hope to relax in future iterations and hope that some of these will be addressed in industry. First of these constraints is the weighting of different actions within the demand estimation, which we would ideally find through learned methodology. Next, assumption of perfect alternating turns between the platform and the market calls for a fixed length non-strictly alternating state definition with a history of actions to possibly allow for the development of multi agentic or multi platform simulation. In our simulation we also make assumptions of non-perishable supply of items, which creates the biggest sim-to-real gap in our system. We also would like to further remove intra-session stationary nature of the contamination parameter to further create high-fidelity non-stationarity within a single evaluation window. + +For deployment of this it is advised to collect a higher sample size of human baselines and to complement this with the simulated agentic sessions and to mind the matrix scaling for very large catalog sizes. diff --git a/paper/src/chapters/figures/results/generated/legacy/first_sweep_headline_summary.json b/paper/src/chapters/figures/results/generated/legacy/first_sweep_headline_summary.json deleted file mode 100644 index caf3d15..0000000 --- a/paper/src/chapters/figures/results/generated/legacy/first_sweep_headline_summary.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "runs": 340, - "tiers": 5, - "alphas": 6, - "status": "ok", - "mean_tier_revenue_robust": 190714.62212212436, - "mean_tier_revenue_no_robust": 197371.17216609977, - "mean_tier_revenue_delta": -6656.5500439754105, - "mean_tier_revenue_delta_pct": -3.3726050116242514 -} \ No newline at end of file diff --git a/paper/src/chapters/figures/results/generated/legacy/first_sweep_tier_alpha_deltas.csv b/paper/src/chapters/figures/results/generated/legacy/first_sweep_tier_alpha_deltas.csv deleted file mode 100644 index fcddcd6..0000000 --- a/paper/src/chapters/figures/results/generated/legacy/first_sweep_tier_alpha_deltas.csv +++ /dev/null @@ -1,31 +0,0 @@ -tier,alpha,runs_robust,runs_no_robust,eval_revenue_mean_delta,eval_revenue_mean_delta_pct,eval_reward_mean_delta,eval_reward_mean_delta_pct,eval_coi_level_mean_delta,eval_coi_level_mean_delta_pct,eval_margin_mean_delta,eval_margin_mean_delta_pct,objective_score_delta,objective_score_delta_pct,train_alpha_adv_delta,train_alpha_adv_delta_pct -dqn,0.0,5.0,2.0,-31308.987414117495,-8.73651226889534,-1909.7427407095092,-0.5742991901121623,-2.8982436567700063,-2.1108702433020436,-0.001972064237093285,-0.2116777198290971,-1909.7427407095092,-0.5742991901121623,, -dqn,0.1,8.0,4.0,-7723.542755668925,-2.2789188721535494,-74239.37371836061,-21.063854618469847,1.7435833801418141,1.2859365583872486,0.0011891962142838164,0.1278074871971924,-74239.37371836061,-21.063854618469847,0.17619791666666657,176.19791666666694 -dqn,0.25,7.0,3.0,-12344.82818986749,-3.7035466052614323,93154.03627578515,36.06691230407512,0.03214544949867104,0.023426184113378143,1.763733457238459e-05,0.001893256490383175,93154.03627578515,36.06691230407512,0.14530952380952394,58.12380952380958 -dqn,0.4,5.0,10.0,-7816.300706216833,-2.4694340725162824,-42362.74668471434,-13.411888482380219,0.6251272343707797,0.4579446603861758,0.0002750615520492605,0.02953644634355915,-42362.74668471434,-13.411888482380219,0.09856666666666747,24.64166666666691 -dqn,0.6,5.0,4.0,-16150.011887742497,-5.347485987139731,-28508.74710866122,-10.151356300001888,-0.63306323164079,-0.46056970247177387,-0.00034537433455417155,-0.0370668515552649,-28508.74710866122,-10.151356300001888,0.1361999999999981,22.699999999999644 -dqn,0.8,7.0,6.0,-18191.8826663699,-6.440527544692988,-55296.94441124235,-20.19273590083627,-0.796733634735034,-0.579832425016392,-0.0006423984775592029,-0.0689476165584585,-55296.94441124235,-20.19273590083627,0.1532857142857158,19.160714285714512 -linear,0.0,9.0,8.0,-14967.67388588126,-4.273413942959129,-20107.23171681742,-6.60039931288617,-0.06127790826209889,-0.04564810574240612,-7.607744079518586e-05,-0.008177885913528719,-20107.23171681742,-6.60039931288617,, -linear,0.1,3.0,5.0,-24531.399901538738,-7.171831328305365,-96669.7835552101,-26.44920711447249,-0.3680976907859872,-0.2733723058172187,-0.0002515287835096469,-0.02702956778346356,-96669.7835552101,-26.44920711447249,, -linear,0.25,6.0,9.0,-14840.859479571285,-4.520682292638562,-26510.179456423968,-8.033117756667396,-0.13734776448131925,-0.10212641096230607,-9.41162442338328e-05,-0.010115001392981545,-26510.179456423968,-8.033117756667396,, -linear,0.4,4.0,11.0,-17196.7642560167,-5.486915251242723,-74520.10209817477,-25.042311510043184,0.12217076984330788,0.09098828726103136,0.00010713887099822461,0.011516865671259795,-74520.10209817477,-25.042311510043184,, -linear,0.6,5.0,3.0,-14284.06615788641,-4.854766876637072,38417.71856593515,14.088596762512362,0.24251461234271687,0.1806530855220358,0.0002606811969937395,0.028024824619509187,38417.71856593515,14.088596762512362,, -linear,0.8,4.0,11.0,-10840.488575784548,-3.933600919557566,15749.581078662042,6.447651726824251,0.028051260535562506,0.020876236575910773,5.361882659971062e-05,0.005763158099097226,15749.581078662042,6.447651726824251,, -qtable,0.0,9.0,8.0,-18644.457288398524,-8.15323701554329,32993.42568058451,20.675688115613053,10.369779227648095,10.682768960780463,0.018566897519637582,2.0803084179092814,32993.42568058451,20.675688115613053,0.11839814814814797, -qtable,0.1,6.0,5.0,-12549.400855549495,-4.616991193742389,-37207.79701261924,-15.336047254435487,0.0884057957559321,0.07703761042583206,-0.01127789819771663,-1.2272540823820444,-37207.79701261924,-15.336047254435487,0.07577777777777787,75.77777777777803 -qtable,0.25,6.0,5.0,-1534.3527429780224,-0.5456640130847226,18433.43663451099,7.304472653867784,-0.5776125938941306,-0.45734160960552755,-0.003316338490628068,-0.3584028328803385,18433.43663451099,7.304472653867784,0.1181458333333334,47.258333333333354 -qtable,0.4,8.0,6.0,-15146.258176090778,-5.274860187729517,-37364.22587794208,-13.005651205148677,0.4611471727478005,0.3629050099230144,0.0071046453227539,0.7751478467862876,-37364.22587794208,-13.005651205148677,0.11010416666666772,27.52604166666698 -qtable,0.6,6.0,6.0,-9577.578548656049,-3.9322693501816666,-19088.152339068736,-9.571307395166029,0.9081750157567683,0.7495917946306662,0.0015520804425310786,0.16838348372043557,-19088.152339068736,-9.571307395166029,0.16983333333333228,28.305555555555333 -qtable,0.8,5.0,2.0,-52751.680936846446,-19.699089872409548,-16508.209313987172,-7.589601869470744,-15.022454081083623,-11.215398490282094,-0.007791824761087751,-0.8384414846099099,-16508.209313987172,-7.589601869470744,0.11120000000000174,13.900000000000245 -static,0.0,5.0,6.0,-4782.871053113384,-5.233544525848519,14411.4689779756,25.538141347978577,1.307060701942973,1.8731997380823568,0.002537468952847566,0.2911381045328444,14411.4689779756,25.538141347978577,, -static,0.1,8.0,5.0,1629.4524528499896,1.880088900553112,-5347.078589385725,-8.14812684380662,0.3600324838305795,0.5019134064795009,-4.6492644957929485e-05,-0.005316014641356001,-5347.078589385725,-8.14812684380662,, -static,0.25,5.0,6.0,-9938.662276761897,-10.398087633377964,-23616.087243780566,-27.701108621456626,-3.0513860773271233,-4.099238223547561,-0.003519771479853273,-0.40113716461596144,-23616.087243780566,-27.701108621456626,, -static,0.4,3.0,4.0,1850.8400595222774,2.1912497828943436,15058.659457798465,23.67199439061036,3.669612467486587,5.430169778169349,0.006763447803564415,0.7804393835882188,15058.659457798465,23.67199439061036,, -static,0.6,6.0,5.0,1038.893948415236,1.2765037688226162,-6062.864079504681,-9.363144945348399,-1.712609061865976,-2.3996341009364213,-0.0042285583442709385,-0.48362088973179423,-6062.864079504681,-9.363144945348399,, -static,0.8,3.0,7.0,2696.6340631967323,3.6826150812750567,149.22406835677975,0.27280281303997084,0.8491716126507072,1.2427748744725668,0.0032786525965587954,0.3777595573932637,149.22406835677975,0.27280281303997084,, -surge,0.0,6.0,6.0,-606.73760243367,-5.066579306500225,-244.17585425326251,-5.525800641331023,0.014874931199557295,0.09186560988877175,0.0019308940532419272,0.4471794260021321,-244.17585425326251,-5.525800641331023,, -surge,0.1,2.0,5.0,169.78743573408792,1.446343107913299,-1012.7706974660168,-20.02053666691211,-0.14459518037699226,-0.864651254901582,-0.0018650458785858248,-0.4260349899970559,-1012.7706974660168,-20.02053666691211,, -surge,0.25,10.0,7.0,-128.20993816584632,-1.1276930411162496,-81.21373487263281,-1.7081453033360994,0.3008506477195141,1.839047728806548,0.0030750148302954305,0.7102446987902812,-81.21373487263281,-1.7081453033360994,, -surge,0.4,6.0,6.0,-473.03722764431404,-4.297928307550563,28.557452243338048,0.6755106104955642,-0.5027452173053764,-3.072002360121898,-0.005581380442163164,-1.288152985482699,28.557452243338048,0.6755106104955642,, -surge,0.6,2.0,5.0,307.79436325796996,3.0356727142643067,2060.57396030564,63.382050333909866,0.2339650444065704,1.438519400758399,0.001302270025389629,0.30077697380833807,2060.57396030564,63.382050333909866,, -surge,0.8,3.0,3.0,423.15386247993047,4.372210191290083,1117.0942083304312,34.86182570616373,0.8971464536957541,5.327339899805159,0.007068630716831503,1.6094191039618562,1117.0942083304312,34.86182570616373,, diff --git a/paper/src/chapters/figures/results/generated/legacy/first_sweep_tier_alpha_mode_summary.csv b/paper/src/chapters/figures/results/generated/legacy/first_sweep_tier_alpha_mode_summary.csv deleted file mode 100644 index dba8d81..0000000 --- a/paper/src/chapters/figures/results/generated/legacy/first_sweep_tier_alpha_mode_summary.csv +++ /dev/null @@ -1,61 +0,0 @@ -tier,alpha,mode,runs,eval_revenue_mean_mean,eval_revenue_mean_std,eval_reward_mean_mean,eval_reward_mean_std,eval_coi_level_mean_mean,eval_coi_level_mean_std,eval_margin_mean_mean,eval_margin_mean_std,objective_score_mean,objective_score_std,train_alpha_adv_mean,train_alpha_adv_std -dqn,0.0,no_robust,2,358369.40933039243,3531.782519351935,332534.46523867303,114183.5587841961,137.30089123035202,0.8184776440325546,0.9316352418598786,0.0006839003676302996,332534.46523867303,114183.5587841961,, -dqn,0.0,robust,5,327060.42191627494,24311.17412598574,330624.7224979635,62834.39223547943,134.40264757358202,6.160000643680792,0.9296631776227853,0.004262039730140749,330624.7224979635,62834.39223547943,0.17835000000000004,0.08829347371125472 -dqn,0.1,no_robust,4,338912.58043645386,19584.736810155388,352449.13650924934,34076.74819101191,135.58860029055563,3.4055508991301524,0.9304589585186211,0.0023438665484978773,352449.13650924934,34076.74819101191,0.0999999999999998,0.0 -dqn,0.1,robust,8,331189.03768078494,8060.912085646968,278209.7627908887,57861.69545853692,137.33218367069745,0.43113256118808096,0.931648154732905,0.000296560958972609,278209.7627908887,57861.69545853692,0.2761979166666664,0.09826648189130198 -dqn,0.25,no_robust,3,333324.4996115304,6101.717861804452,258281.15112936878,46772.05216097596,137.2201692904545,0.9866477887862672,0.9315871706751672,0.0006356053229300815,258281.15112936878,46772.05216097596,0.25,0.0 -dqn,0.25,robust,7,320979.6714216629,7345.8761269427705,351435.18740515393,40320.63699261721,137.25231473995316,0.3527287960309152,0.9316048080097395,0.0002575240668471541,351435.18740515393,40320.63699261721,0.39530952380952394,0.073021206240698 -dqn,0.4,no_robust,10,316521.94295076875,3631.1820920182718,315859.66987697606,59129.03566963754,136.50715652926755,0.5085743959240285,0.931261495881483,0.00031280530251053175,315859.66987697606,59129.03566963754,0.3999999999999993,0.0 -dqn,0.4,robust,5,308705.6422445519,10654.571556448245,273496.9231922617,68868.59270778317,137.13228376363833,0.9543108715306617,0.9315365574335323,0.0006302636717132419,273496.9231922617,68868.59270778317,0.49856666666666677,0.05745573175159429 -dqn,0.6,no_robust,4,302011.2988903938,2354.1141598720183,280836.828756133,58683.00124997926,137.4522093492651,0.4692723362517602,0.9317606434396914,0.0003317518021682495,280836.828756133,58683.00124997926,0.600000000000001,0.0 -dqn,0.6,robust,5,285861.2870026513,10386.571631344234,252328.08164747176,59388.56063758225,136.8191461176243,1.0629203361893034,0.9314152691051373,0.0005692783702932289,252328.08164747176,59388.56063758225,0.7361999999999991,0.07108625433623189 -dqn,0.8,no_robust,6,282459.51189759385,2625.018247527438,273845.72691287595,66378.16690732416,137.4075681801531,0.29728950101826707,0.9317196295169007,0.00022799290978965786,273845.72691287595,66378.16690732416,0.7999999999999985,0.0 -dqn,0.8,robust,7,264267.62923122395,6771.288971321149,218548.7825016336,50043.2009443344,136.61083454541807,1.2319662937254596,0.9310772310393415,0.0010118564779437284,218548.7825016336,50043.2009443344,0.9532857142857143,0.04709817507333055 -linear,0.0,no_robust,8,350250.9723061577,3156.286820918861,304636.59490360576,71682.88027353655,134.2397614654424,0.32611787466946035,0.9302824910938235,0.00024020749661685483,304636.59490360576,71682.88027353655,, -linear,0.0,robust,9,335283.29842027643,7707.594869976611,284529.36318678834,55524.58819004573,134.1784835571803,0.4477314164684001,0.9302064136530284,0.00034781034181738526,284529.36318678834,55524.58819004573,, -linear,0.1,no_robust,5,342052.1032713031,2576.546352056584,365492.17954557994,44890.93522299766,134.65068807375954,0.2181027640393531,0.930569018064469,0.00014058935916940913,365492.17954557994,44890.93522299766,, -linear,0.1,robust,3,317520.7033697644,4796.580459456527,268822.39599036984,39256.421140635124,134.28259038297355,0.24570499109363475,0.9303174892809594,0.00018817899183709092,268822.39599036984,39256.421140635124,, -linear,0.25,no_robust,9,328288.0441241802,2178.525494145428,330011.0898339667,38591.36053388808,134.48799697074742,0.2199303973026469,0.9304619997297959,0.00015341642413402035,330011.0898339667,38591.36053388808,, -linear,0.25,robust,6,313447.18464460893,11811.426711620714,303500.9103775427,63358.917144214036,134.3506492062661,0.2947034403278951,0.9303678834855621,0.00021446628431268986,303500.9103775427,63358.917144214036,, -linear,0.4,no_robust,11,313414.0672597746,1982.9537556159262,297576.7714904776,69396.90446617964,134.2708754290745,0.3062093691351849,0.9302780292522507,0.00023067974755288992,297576.7714904776,69396.90446617964,, -linear,0.4,robust,4,296217.3030037579,5109.898340355844,223056.66939230284,38293.73688466607,134.3930461989178,0.12347753686382154,0.9303851681232489,7.324605809708878e-05,223056.66939230284,38293.73688466607,, -linear,0.6,no_robust,3,294227.64307441004,2081.9176570448135,272686.62176604365,66672.50905805513,134.24327165069943,0.30764332256042104,0.9301795837547151,0.00020453921786790446,272686.62176604365,66672.50905805513,, -linear,0.6,robust,5,279943.5769165236,9866.031719660255,311104.3403319788,28363.930707781863,134.48578626304214,0.21280262186464388,0.9304402649517088,0.00020533894868120649,311104.3403319788,28363.930707781863,, -linear,0.8,no_robust,11,275586.89347174135,1618.038877505867,244268.4832547461,56201.44465269986,134.36933631960773,0.2845660213184439,0.9303723007028001,0.00017640716421186918,244268.4832547461,56201.44465269986,, -linear,0.8,robust,4,264746.4048959568,7976.6279174956235,260018.06433340814,57942.49882730146,134.3973875801433,0.31511916357643405,0.9304259195293998,0.00023606570471334208,260018.06433340814,57942.49882730146,, -qtable,0.0,no_robust,8,228675.52179404112,103199.70453252994,159575.94976328663,95848.81008103945,97.07014413321637,33.0637115678536,0.8925069648229078,0.04890522141482132,159575.94976328663,95848.81008103945,0.0,0.0 -qtable,0.0,robust,9,210031.0645056426,84361.3834579348,192569.37544387113,116824.7880426837,107.43992336086447,21.41128645838254,0.9110738623425454,0.019188350719133364,192569.37544387113,116824.7880426837,0.11839814814814797,0.061909456985161225 -qtable,0.1,no_robust,5,271809.0706466638,14898.209045050968,242616.60384397948,49181.45526408063,114.75666919996793,3.461383158930426,0.9189538140159812,0.002294693249439748,242616.60384397948,49181.45526408063,0.0999999999999998,0.0 -qtable,0.1,robust,6,259259.66979111428,102995.29934229614,205408.80683136024,94155.1845420674,114.84507499572386,36.206421837506966,0.9076759158182646,0.048591979839360346,205408.80683136024,94155.1845420674,0.17577777777777767,0.06720562696899951 -qtable,0.25,no_robust,5,281190.01916657295,70274.10208723843,252358.2126733039,129868.46825082717,126.29784427276161,15.368804047323954,0.9253103453385114,0.009044883517550522,252358.2126733039,129868.46825082717,0.25,0.0 -qtable,0.25,robust,6,279655.6664235949,93056.2549557545,270791.6493078149,116021.46257259768,125.72023167886748,26.760714047253796,0.9219940068478834,0.022785695882060884,270791.6493078149,116021.46257259768,0.3681458333333334,0.08845114686619042 -qtable,0.4,no_robust,6,287140.4669895195,32698.16434426399,287292.23388022534,83855.95000252876,127.07104066863859,9.200301166154173,0.9165535777734913,0.01306001923887748,287292.23388022534,83855.95000252876,0.3999999999999993,0.0 -qtable,0.4,robust,8,271994.2088134287,79259.3185780895,249928.00800228326,88265.30801790548,127.53218784138639,23.406428094683015,0.9236582230962452,0.020073747007871224,249928.00800228326,88265.30801790548,0.510104166666667,0.09294655989347765 -qtable,0.6,no_robust,6,243563.64469828535,67006.60707045678,199430.98211127534,79119.52886604435,121.15594411011905,17.91243944823949,0.9217533740470492,0.011558797825966702,199430.98211127534,79119.52886604435,0.600000000000001,0.0 -qtable,0.6,robust,6,233986.0661496293,43155.478617087436,180342.8297722066,48117.79957836251,122.06411912587582,12.160951090203252,0.9233054544895802,0.006840854872863436,180342.8297722066,48117.79957836251,0.7698333333333333,0.09107066853090896 -qtable,0.8,no_robust,2,267787.4017455507,1552.038101264713,217510.87340156303,45358.788584678456,133.9448981157492,0.47346860040111405,0.9293224278749692,0.0002998116010539045,217510.87340156303,45358.788584678456,0.7999999999999985,0.0 -qtable,0.8,robust,5,215035.72080870424,32869.73253165852,201002.66408757586,63247.67956376057,118.92244403466557,8.586916805142152,0.9215306031138815,0.004644709320891907,201002.66408757586,63247.67956376057,0.9112000000000002,0.07381653307732307 -static,0.0,no_robust,6,91388.75248869567,13415.65534300268,56431.15832748852,8525.098185703384,69.77689967440658,3.670744870085874,0.8715688236409825,0.005831496806767582,56431.15832748852,8525.098185703384,, -static,0.0,robust,5,86605.88143558228,7614.909395960895,70842.62730546412,8033.737230392738,71.08396037634955,3.6802889678420283,0.8741062925938301,0.005083911544334936,70842.62730546412,8033.737230392738,, -static,0.1,no_robust,5,86668.90445290186,8037.955688932984,65623.40881389238,19329.448262530004,71.73199185012882,4.199046495412734,0.874577067494122,0.006610505646022198,65623.40881389238,19329.448262530004,, -static,0.1,robust,8,88298.35690575185,9576.838833058617,60276.33022450666,13359.490452744656,72.0920243339594,6.7706096714767865,0.8745305748491641,0.010083585815241344,60276.33022450666,13359.490452744656,, -static,0.25,no_robust,6,95581.63603909909,8345.698435455577,85253.22060752509,13111.526873622026,74.43788116042678,2.1078820386097368,0.8774483618896327,0.0037254791853004897,85253.22060752509,13111.526873622026,, -static,0.25,robust,5,85642.97376233719,9472.880627242153,61637.13336374452,15937.429780623212,71.38649508309966,4.0264905454627264,0.8739285904097794,0.005323853359397925,61637.13336374452,15937.429780623212,, -static,0.4,no_robust,4,84465.04245981346,12101.831388745604,63613.81812329075,7778.361846092061,67.5782271530322,3.9088888968092,0.8666205147756862,0.007149121199217965,63613.81812329075,7778.361846092061,, -static,0.4,robust,3,86315.88251933573,8642.748496122398,78672.47758108922,17823.74997200773,71.24783962051879,2.790416943786253,0.8733839625792507,0.005990544453538607,78672.47758108922,17823.74997200773,, -static,0.6,no_robust,5,81385.88962988024,12343.523894997037,64752.43216774836,23486.779472906223,71.36959177224794,5.100226704959064,0.874353948320141,0.007787250295491337,64752.43216774836,23486.779472906223,, -static,0.6,robust,6,82424.78357829548,9831.886701625144,58689.56808824368,12672.506035553573,69.65698271038197,3.484982360048201,0.8701253899758701,0.005917711231889304,58689.56808824368,12672.506035553573,, -static,0.8,no_robust,7,73226.06364450825,4447.877985963851,54700.340767716196,14406.881298569717,68.32867561883204,3.68262917356943,0.8679204886788817,0.007467501164611224,54700.340767716196,14406.881298569717,, -static,0.8,robust,3,75922.69770770498,5046.089536162847,54849.564836072976,22780.98012221352,69.17784723148274,1.5268167784698885,0.8711991412754405,0.0033278715575433297,54849.564836072976,22780.98012221352,, -surge,0.0,no_robust,6,11975.290738176132,411.4052900076416,4418.832131346071,896.5828048394391,16.192056219479124,0.8040364003224534,0.4317940274006973,0.008271862690929055,4418.832131346071,896.5828048394391,, -surge,0.0,robust,6,11368.553135742462,623.8217438159004,4174.6562770928085,639.9963040241264,16.20693115067868,0.9853827520149101,0.4337249214539392,0.010371668289035135,4174.6562770928085,639.9963040241264,, -surge,0.1,no_robust,5,11739.084232858655,332.778792718381,5058.659087494994,1110.8409258976824,16.722948073839394,0.6578121995950104,0.4377682402562083,0.005683401047550787,5058.659087494994,1110.8409258976824,, -surge,0.1,robust,2,11908.871668592743,81.41250285550258,4045.8883900289775,784.7169500268457,16.5783528934624,0.4088194924856508,0.4359031943776225,0.004531137621699143,4045.8883900289775,784.7169500268457,, -surge,0.25,no_robust,7,11369.223138855004,236.1121240061105,4754.4980344481255,1038.0550037539617,16.359045119223275,0.3945156775653057,0.4329514652531622,0.0038762110261952457,4754.4980344481255,1038.0550037539617,, -surge,0.25,robust,10,11241.013200689158,684.503587066406,4673.284299575493,1187.78635131025,16.65989576694279,1.0515950311117155,0.4360264800834576,0.009701952962125513,4673.284299575493,1187.78635131025,, -surge,0.4,no_robust,6,11006.168409400554,364.6584583108646,4227.535704048808,1414.7964077877168,16.365391636138824,0.9138430058543858,0.4332855262584901,0.008024003783434592,4227.535704048808,1414.7964077877168,, -surge,0.4,robust,6,10533.13118175624,526.0758051960169,4256.093156292146,783.7965507386594,15.862646418833448,0.7732699435426456,0.42770414581632693,0.008967505611725135,4256.093156292146,783.7965507386594,, -surge,0.6,no_robust,5,10139.2472848498,97.448078425168,3251.037082975553,742.2100315641153,16.26429537781848,0.4432465691073604,0.4329686574409998,0.004121820888165019,3251.037082975553,742.2100315641153,, -surge,0.6,robust,2,10447.04164810777,524.0029334247373,5311.611043281193,1808.6200710093085,16.49826042222505,0.6088756908260344,0.43427092746638946,0.007817511630542989,5311.611043281193,1808.6200710093085,, -surge,0.8,no_robust,3,9678.259826640971,272.83530913170915,3204.3479815026553,556.8799617962688,16.840420745981802,0.4589959822922529,0.43920385308157944,0.004953937449529005,3204.3479815026553,556.8799617962688,, -surge,0.8,robust,3,10101.413689120902,526.8318040489241,4321.442189833087,1284.166148011517,17.737567199677557,0.6586775330563983,0.44627248379841095,0.004644261847052545,4321.442189833087,1284.166148011517,, diff --git a/paper/src/chapters/figures/results/generated/legacy/first_sweep_tier_mode_summary.csv b/paper/src/chapters/figures/results/generated/legacy/first_sweep_tier_mode_summary.csv deleted file mode 100644 index e296749..0000000 --- a/paper/src/chapters/figures/results/generated/legacy/first_sweep_tier_mode_summary.csv +++ /dev/null @@ -1,11 +0,0 @@ -tier,mode,runs,eval_revenue_mean_mean,eval_revenue_mean_std,eval_reward_mean_mean,eval_reward_mean_std,eval_coi_level_mean_mean,eval_coi_level_mean_std,eval_margin_mean_mean,eval_margin_mean_std,objective_score_mean,objective_score_std,train_alpha_adv_mean,train_alpha_adv_std -dqn,no_robust,29,315185.66674813855,23538.781000060844,302576.8036266896,62951.88633145167,136.82560356086017,1.3692652218935986,0.9313739013618878,0.0009314135057224836,302576.8036266896,62951.88633145167,0.45740740740740693,0.2368477698794438 -dqn,robust,37,306875.13950902375,27585.74444520695,283724.7169827867,69843.05611741856,136.68837571992978,2.3797541654948753,0.9312171495138941,0.0016512408492580111,283724.7169827867,69843.05611741856,0.5058198198198196,0.28324483129860284 -linear,no_robust,47,315501.15296155965,27105.014861872147,298149.1730416604,67664.7308344108,134.36884359609928,0.29743647613433244,0.9303607531364,0.0002152647006739543,298149.1730416604,67664.7308344108,, -linear,robust,31,306269.9232239004,26399.875293394463,279872.824370329,54401.104602086416,134.32737693008372,0.31909212993628877,0.9303375215162144,0.00025000448833182963,279872.824370329,54401.104602086416,, -qtable,no_robust,32,259818.72178238883,67188.58622318009,222088.83510765125,94450.12569617687,116.84641954166946,22.42810298937963,0.9140582213134033,0.02778864370791322,222088.83510765125,94450.12569617687,0.29218749999999993,0.2559326319498438 -qtable,robust,40,244470.50673219413,78666.30912808319,216920.53697298188,93983.50987622296,118.94013969887506,23.1428303249914,0.9178608956089163,0.023827311253270544,216920.53697298188,93983.50987622296,0.4396239583333334,0.29521865862482416 -static,no_robust,33,85228.452028227,12041.415672002751,64828.579890468536,17681.280330831738,70.58818912317687,4.204964531595236,0.8721419294578765,0.007107262779462876,64828.579890468536,17681.280330831738,, -static,robust,30,84963.18577955024,8926.291379160475,63243.76603076817,14880.924342692271,70.94358095957392,4.363134562111469,0.8730306888410219,0.006660289247744752,63243.76603076817,14880.924342692271,, -surge,no_robust,32,11121.867310184698,809.9895800277001,4260.038064073964,1160.4282377968032,16.416108827015794,0.641203520341943,0.43413855082681374,0.006214799767130059,4260.038064073964,1160.4282377968032,, -surge,robust,29,10994.355365953365,750.5115890942825,4448.160863178768,1000.7519971246122,16.495943148858906,0.9823026347466668,0.4347587896392907,0.009698591291108968,4448.160863178768,1000.7519971246122,, diff --git a/paper/src/chapters/figures/results/generated/legacy/first_sweep_top_configs.csv b/paper/src/chapters/figures/results/generated/legacy/first_sweep_top_configs.csv deleted file mode 100644 index e51fd74..0000000 --- a/paper/src/chapters/figures/results/generated/legacy/first_sweep_top_configs.csv +++ /dev/null @@ -1,26 +0,0 @@ -Name,tier,alpha,mode,objective/score,eval/revenue_mean,eval/reward_mean,eval/coi_level_mean,lambda_coi,robust_radius,learning_rate,batch_size,n_steps,total_timesteps -eager-sweep-244,dqn,0.0,no_robust,413274.4339549909,355872.06196128257,413274.4339549909,136.722140138007,0.2,0.1,0.0003,256,4096,15000 -efficient-sweep-319,linear,0.0,no_robust,410094.0151741567,353309.5198146561,410094.0151741567,134.55152038805429,0.4,0.1,0.001,128,4096,15000 -swept-sweep-422,linear,0.0,no_robust,403130.32747386186,347611.2815474988,403130.32747386186,133.8559785775022,0.4,0.3,0.0001,512,1024,15000 -decent-sweep-478,linear,0.1,no_robust,400452.36418713134,345284.5750647792,400452.36418713134,134.73082941975588,0.1,0.2,0.001,128,1024,50000 -eternal-sweep-339,linear,0.1,no_robust,399628.4231731644,344154.38525771734,399628.4231731644,134.89479277649667,0.4,0.1,0.0001,256,1024,50000 -ethereal-sweep-21,dqn,0.1,no_robust,398492.807245857,343580.6802427996,398492.807245857,136.67160732585188,0.1,0.2,0.001,512,2048,50000 -dark-sweep-418,linear,0.1,no_robust,394615.3720658343,339749.76272695075,394615.3720658343,134.39233246711,0.2,0.1,0.0003,256,1024,50000 -wandering-sweep-122,dqn,0.0,robust,394061.3617726404,339512.43434806296,394061.3617726404,137.6864755964331,0.1,0.3,0.0001,256,2048,30000 -laced-sweep-132,dqn,0.1,robust,389274.54998495104,335600.5979215904,389274.54998495104,137.36888574027677,0.4,0.2,0.001,256,2048,30000 -rich-sweep-53,qtable,0.0,robust,388601.2626147048,335630.6853337664,388601.2626147048,133.4414069888203,0.2,0.1,0.0001,512,1024,50000 -faithful-sweep-430,qtable,0.25,no_robust,387035.6970938766,333255.5771210341,387035.6970938766,137.4906091183188,0.1,0.2,0.0003,128,1024,15000 -dark-sweep-280,qtable,0.25,no_robust,386318.8845004527,332220.0316564078,386318.8845004527,137.26992450099925,0.4,0.1,0.0001,256,1024,50000 -chocolate-sweep-383,linear,0.25,no_robust,383989.49015403807,331071.7003244704,383989.49015403807,134.60590742050857,0.1,0.2,0.001,512,1024,30000 -dry-sweep-263,dqn,0.0,robust,383372.6880637367,330436.0312615148,383372.6880637367,137.40558130223476,0.1,0.3,0.001,128,1024,50000 -different-sweep-143,qtable,0.0,robust,383278.4198015018,330546.16800945485,383278.4198015018,135.9021538079678,0.1,0.3,0.001,256,2048,30000 -woven-sweep-139,dqn,0.25,robust,382788.1296637251,329427.735752473,382788.1296637251,136.8968339394894,0.1,0.1,0.001,512,1024,15000 -dark-sweep-215,dqn,0.25,robust,382358.2401374872,329330.0097603144,382358.2401374872,137.64528612332785,0.2,0.1,0.0001,512,4096,30000 -charmed-sweep-136,linear,0.25,no_robust,382249.5728044314,329646.2053260979,382249.5728044314,134.46825608007862,0.4,0.1,0.0001,256,2048,15000 -light-sweep-308,linear,0.0,robust,381939.1275250679,329628.9436641051,381939.1275250679,133.6209821974879,0.2,0.2,0.001,128,4096,30000 -treasured-sweep-325,linear,0.25,robust,381322.0104772589,328353.58675398555,381322.0104772589,134.8950293943581,0.1,0.1,0.0001,512,2048,15000 -fine-sweep-202,dqn,0.25,robust,378751.33572275366,326518.9068184018,378751.33572275366,137.2900973301052,0.1,0.2,0.0001,512,2048,30000 -treasured-sweep-380,linear,0.25,no_robust,377898.0979419424,325869.1953595453,377898.0979419424,134.54118723889738,0.4,0.3,0.001,128,1024,50000 -pretty-sweep-49,qtable,0.25,robust,377318.4766808995,325282.0152823859,377318.4766808995,137.19609012644068,0.4,0.1,0.0001,128,4096,50000 -desert-sweep-253,linear,0.25,robust,376808.6335063269,325146.3478714648,376808.6335063269,134.48396340732663,0.2,0.1,0.0003,256,1024,30000 -jolly-sweep-133,qtable,0.4,no_robust,376419.57394710975,323709.24588324485,376419.57394710975,137.8349363778071,0.1,0.3,0.0001,128,2048,50000 diff --git a/paper/src/chapters/figures/results/generated/legacy/plots/first_sweep_tier_revenue.pdf b/paper/src/chapters/figures/results/generated/legacy/plots/first_sweep_tier_revenue.pdf deleted file mode 100644 index a2787265ff4344842a889b0c132a1c1d8850dad5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17510 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 diff --git a/paper/src/chapters/figures/results/generated/legacy/ppo_alpha_deltas.csv b/paper/src/chapters/figures/results/generated/legacy/ppo_alpha_deltas.csv deleted file mode 100644 index 42cf5c9..0000000 --- a/paper/src/chapters/figures/results/generated/legacy/ppo_alpha_deltas.csv +++ /dev/null @@ -1,7 +0,0 @@ -alpha,runs_robust,runs_no_robust,eval_revenue_mean_robust,eval_revenue_mean_no_robust,eval_revenue_mean_delta,eval_revenue_mean_delta_pct,eval_reward_mean_robust,eval_reward_mean_no_robust,eval_reward_mean_delta,eval_reward_mean_delta_pct,eval_coi_level_mean_robust,eval_coi_level_mean_no_robust,eval_coi_level_mean_delta,eval_coi_level_mean_delta_pct,eval_coi_leakage_mean_robust,eval_coi_leakage_mean_no_robust,eval_coi_leakage_mean_delta,eval_coi_leakage_mean_delta_pct,eval_volatility_mean_robust,eval_volatility_mean_no_robust,eval_volatility_mean_delta,eval_volatility_mean_delta_pct,eval_margin_mean_robust,eval_margin_mean_no_robust,eval_margin_mean_delta,eval_margin_mean_delta_pct,train_alpha_adv_robust,train_alpha_adv_no_robust,train_alpha_adv_delta,train_alpha_adv_delta_pct,train_coi_penalty_robust,train_coi_penalty_no_robust,train_coi_penalty_delta,train_coi_penalty_delta_pct,train_ux_penalty_robust,train_ux_penalty_no_robust,train_ux_penalty_delta,train_ux_penalty_delta_pct,train_agent_prob_robust,train_agent_prob_no_robust,train_agent_prob_delta,train_agent_prob_delta_pct -0.0,4.0,4.0,3379.9042994670963,3565.2912010160844,-185.38690154898813,-5.199768857482219,313527.4707462,331300.229069,-17772.758322799986,-5.364547550342456,137.08358925982625,137.28764358955686,-0.2040543297306101,-0.14863269875959326,0.1146626165658294,0.11861133504329742,-0.003948718477468013,-3.3291240470622716,0.06687153537785637,0.06445662162531288,0.0024149137525434905,3.746572022625408,0.9315273502623671,0.9317078361627993,-0.00018048590043218127,-0.019371512552207898,0.18958333333333333,,,,5.553200113221484,,,,61.35134238638615,66.58479574844135,-5.233453362055201,-7.859832418540847,0.12778212146468534,0.11615891320235115,0.011623208262334192,10.00629907933654 -0.1,4.0,4.0,3307.028238366196,3458.002436284769,-150.97419791857283,-4.365936713473732,306772.49146475,321215.477968,-14442.986503249966,-4.4963544704059375,137.1182041122497,136.82757579763506,0.29062831461465066,0.21240478238427865,0.1128546052304944,0.11704917861668755,-0.004194573386193154,-3.5835991638433753,0.0685405649303561,0.06737596899527175,0.0011645959350843477,1.728503430007924,0.9315331673960889,0.9313276818191593,0.00020548557692967595,0.0220637248243606,0.2818749999999999,0.1,0.18187499999999987,181.87499999999986,5.079528726095333,,,,52.44772950699336,53.288869747139515,-0.841140240146153,-1.578453895039319,0.11644381911386253,0.11765277436070229,-0.0012089552468397546,-1.0275620387270383 -0.25,4.0,4.0,3134.3438215278165,3300.5539051855053,-166.21008365768876,-5.035823938416998,290691.4771835,306522.90003785,-15831.422854350007,-5.16484179563586,136.89990884669214,136.71752459667877,0.18238425001337077,0.1334022471160229,0.11113957413522965,0.1139905600539111,-0.0028509859186814507,-2.50107194607439,0.06427159998376095,0.06846858821082077,-0.004196988227059828,-6.12980103246314,0.9314501501825461,0.9313053225630614,0.0001448276194846443,0.015551035302371268,0.44833333333333336,0.25,0.19833333333333336,79.33333333333334,4.7183804755060255,,,,49.04307009982127,55.2030005738411,-6.159930474019831,-11.158687770568074,0.10998505830218755,0.11684259343269415,-0.0068575351305066035,-5.869037077182653 -0.4,4.0,4.0,2983.852437569374,3180.7872854626567,-196.9348478932825,-6.191386918369099,276545.26309355,295433.5405797,-18888.277486150037,-6.393409986248494,136.19210761854086,136.5783021470118,-0.38619452847095204,-0.2827641890402586,0.10875560547061063,0.11189234314151972,-0.0031367376709090927,-2.8033532794480807,0.07452230347799255,0.07104688223410768,0.003475421243884863,4.891729425132195,0.9307282962514367,0.9310542820602117,-0.0003259858087749645,-0.03501254599824534,0.5999999999999999,0.4000000000000001,0.1999999999999998,49.999999999999936,4.174996403604185,,,,47.99794119802058,50.794260008988424,-2.796318810967847,-5.505186630286606,0.10222958892923095,0.11161526349272373,-0.009385674563492777,-8.408952565976458 -0.6,4.0,4.0,2789.0434220430398,2982.2460998252786,-193.20267778223888,-6.4784283830083,258688.11700405,277051.95613675,-18363.8391327,-6.628301560749781,136.86774320500828,136.81931587629953,0.04842732870875466,0.035395096371142916,0.10501047827147733,0.10802266412956946,-0.0030121858580921257,-2.788475809557069,0.06914180963767007,0.06698591531512615,0.0021558943225439137,3.2184292957732996,0.9314130089130337,0.9313849217310588,2.8087181974889575e-05,0.003015636319588161,0.7733333333333334,0.5999999999999999,0.17333333333333356,28.888888888888935,4.178300996512875,,,,39.928062615509425,47.86860429278531,-7.940541677275881,-16.588203885594947,0.11297979438696983,0.1162670925925253,-0.0032872982055554695,-2.827367686122743 -0.8,4.0,4.0,2586.098242115281,2841.1305915063504,-255.03234939106915,-8.97643882169642,239765.24959855,264140.55002745,-24375.300428900024,-9.228155399224729,136.5038826686135,137.28163778418497,-0.7777551155714661,-0.5665397995864124,0.10253056902792507,0.1031498585902154,-0.0006192895622903344,-0.6003784888844036,0.07325665736408164,0.06592454978099352,0.007332107583088124,11.1219683827132,0.9311235469993302,0.9316596013994161,-0.0005360544000858614,-0.05753758124541101,1.0,0.8000000000000002,0.19999999999999984,24.99999999999998,3.5384100686094007,,,,37.14414699970415,37.43809775029793,-0.29395075059377973,-0.7851647606519765,0.09990322635678014,0.10432800196112454,-0.0044247756043444,-4.241215705437541 diff --git a/paper/src/chapters/figures/results/generated/legacy/ppo_alpha_mode_summary.csv b/paper/src/chapters/figures/results/generated/legacy/ppo_alpha_mode_summary.csv deleted file mode 100644 index 52cff7b..0000000 --- a/paper/src/chapters/figures/results/generated/legacy/ppo_alpha_mode_summary.csv +++ /dev/null @@ -1,13 +0,0 @@ -alpha,mode,runs,eval_revenue_mean_mean,eval_revenue_mean_std,eval_reward_mean_mean,eval_reward_mean_std,eval_coi_level_mean_mean,eval_coi_level_mean_std,eval_coi_leakage_mean_mean,eval_coi_leakage_mean_std,eval_volatility_mean_mean,eval_volatility_mean_std,eval_margin_mean_mean,eval_margin_mean_std,train_alpha_adv_mean,train_alpha_adv_std,train_coi_penalty_mean,train_coi_penalty_std,train_ux_penalty_mean,train_ux_penalty_std,train_agent_prob_mean,train_agent_prob_std -0.0,no_robust,4,3565.2912010160844,52.219179508209216,331300.229069,5038.96659004527,137.28764358955686,0.6434240315013728,0.11861133504329742,0.004019332768284657,0.06445662162531288,0.004080405219050139,0.9317078361627993,0.00038018051704976865,,,,,66.58479574844135,32.282270089830455,0.11615891320235115,0.016558627227281013 -0.0,robust,4,3379.9042994670963,54.727408939657735,313527.4707462,5408.058196552377,137.08358925982625,1.047386315387148,0.1146626165658294,0.0025627354157035497,0.06687153537785637,0.008577061675868377,0.9315273502623671,0.0007274203134899985,0.18958333333333333,0.02083333333333336,5.553200113221484,0.45981481828856186,61.35134238638615,30.27964905193963,0.12778212146468534,0.027929667978205217 -0.1,no_robust,4,3458.002436284769,60.75923217871363,321215.477968,6016.373193216596,136.82757579763506,1.1899102161551907,0.11704917861668755,0.0021220259908233973,0.06737596899527175,0.006801136773079149,0.9313276818191593,0.0008352263172197586,0.1,0.0,,,53.288869747139515,18.480340945815023,0.11765277436070229,0.017544197575138736 -0.1,robust,4,3307.028238366196,35.58495715224888,306772.49146475,3488.2690530060245,137.1182041122497,0.8582218376452346,0.1128546052304944,0.0005963155492967403,0.0685405649303561,0.0050673362512629015,0.9315331673960889,0.0005217376436765336,0.2818749999999999,0.03624999999999999,5.079528726095333,0.6109585102054891,52.44772950699336,29.0263361696475,0.11644381911386253,0.021152545180088765 -0.25,no_robust,4,3300.5539051855053,50.460978662647115,306522.90003785,4860.668937531515,136.71752459667877,0.7410676951244369,0.1139905600539111,0.003319948537321803,0.06846858821082077,0.008614994548315848,0.9313053225630614,0.0004919872662680591,0.25,0.0,,,55.2030005738411,26.88247558235345,0.11684259343269415,0.013462146346772591 -0.25,robust,4,3134.3438215278165,64.06834403659167,290691.4771835,6331.196493752059,136.89990884669214,1.3796663751798552,0.11113957413522965,0.0015044942041406348,0.06427159998376095,0.0042331619171274894,0.9314501501825461,0.0008939739741734515,0.44833333333333336,0.0033333333333333518,4.7183804755060255,0.4538389380858333,49.04307009982127,28.20484665432831,0.10998505830218755,0.010731404693185651 -0.4,no_robust,4,3180.7872854626567,71.87564776824694,295433.5405797,7035.374110540269,136.5783021470118,1.7095219574599192,0.11189234314151972,0.0013821115134030936,0.07104688223410768,0.005766138692685495,0.9310542820602117,0.0013989725050689828,0.4000000000000001,0.0,,,50.794260008988424,24.836708377642946,0.11161526349272373,0.005787749200301594 -0.4,robust,4,2983.852437569374,45.51290575912758,276545.26309355,4555.1725323898245,136.19210761854086,1.5546063667946701,0.10875560547061063,0.001118798290958954,0.07452230347799255,0.0040446395928049874,0.9307282962514367,0.0013558080014763189,0.5999999999999999,0.0,4.174996403604185,0.12189448324552496,47.99794119802058,33.51782503281748,0.10222958892923095,0.0031686467591609474 -0.6,no_robust,4,2982.2460998252786,39.93674476199945,277051.95613675,3931.02017169463,136.81931587629953,1.1995405806950865,0.10802266412956946,0.000405835985606262,0.06698591531512615,0.002805894772223563,0.9313849217310588,0.0008100530228792662,0.5999999999999999,0.0,,,47.86860429278531,23.830502772642472,0.1162670925925253,0.028676813474186293 -0.6,robust,4,2789.0434220430398,35.297482315631626,258688.11700405,3420.6735023624556,136.86774320500828,0.7097303238857778,0.10501047827147733,0.0008273121554488608,0.06914180963767007,0.009066158371268139,0.9314130089130337,0.0005024421703994162,0.7733333333333334,0.053333333333333385,4.178300996512875,0.5865970573865015,39.928062615509425,30.25078643153115,0.11297979438696983,0.0274101056520461 -0.8,no_robust,4,2841.1305915063504,21.84043179776092,264140.55002745,2073.353315114627,137.28163778418497,0.6288968799501957,0.1031498585902154,0.0012877581835795701,0.06592454978099352,0.00340700896766341,0.9316596013994161,0.00038430108058413553,0.8000000000000002,0.0,,,37.43809775029793,32.01740090550489,0.10432800196112454,0.018337841526911584 -0.8,robust,4,2586.098242115281,48.05539265296157,239765.24959855,4681.6472175597555,136.5038826686135,1.0611320896043694,0.10253056902792507,0.002587472569909977,0.07325665736408164,0.0015359324114246234,0.9311235469993302,0.0006145440308596868,1.0,0.0,3.5384100686094007,0.391972726035734,37.14414699970415,25.614063825315505,0.09990322635678014,0.010269342031085898 diff --git a/paper/src/chapters/figures/results/generated/legacy/ppo_headline_summary.json b/paper/src/chapters/figures/results/generated/legacy/ppo_headline_summary.json deleted file mode 100644 index 5b106f2..0000000 --- a/paper/src/chapters/figures/results/generated/legacy/ppo_headline_summary.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "status": "ok", - "revenue_delta": -191.29017636530716, - "revenue_delta_pct": -5.938226273545598, - "coi_leakage_delta": -0.002960415145605702, - "coi_leakage_delta_pct": -2.6404147469510946 -} \ No newline at end of file diff --git a/paper/src/chapters/figures/results/generated/legacy/ppo_overall_mode_summary.csv b/paper/src/chapters/figures/results/generated/legacy/ppo_overall_mode_summary.csv deleted file mode 100644 index c45b856..0000000 --- a/paper/src/chapters/figures/results/generated/legacy/ppo_overall_mode_summary.csv +++ /dev/null @@ -1,3 +0,0 @@ -mode,runs,eval_revenue_mean_mean,eval_revenue_mean_std,eval_reward_mean_mean,eval_reward_mean_std,eval_coi_level_mean_mean,eval_coi_level_mean_std,eval_coi_leakage_mean_mean,eval_coi_leakage_mean_std,eval_volatility_mean_mean,eval_volatility_mean_std,eval_margin_mean_mean,eval_margin_mean_std,train_alpha_adv_mean,train_alpha_adv_std,train_coi_penalty_mean,train_coi_penalty_std,train_ux_penalty_mean,train_ux_penalty_std,train_agent_prob_mean,train_agent_prob_std -no_robust,24,3221.335253213441,262.46595166337727,299277.442303125,24382.561944761477,136.9186666318945,1.0038463876967063,0.11211932326253345,0.005805494533542669,0.06737642102693879,0.005402738047823369,0.9314066076226178,0.0007436370959663933,0.43,0.2546411303445653,,,51.86293802024894,25.340287421525442,0.11381077317368686,0.016664235359362907 -robust,24,3030.0450768481337,288.262657026656,280998.34484843333,26820.020161880373,136.77757261848845,1.06224696086916,0.10915890811692774,0.004616462637659704,0.06943407846195294,0.006435789449278624,0.9312959200008004,0.0007858424519830652,0.5488541666666666,0.2860373751485706,4.540469463924883,0.7906156355346259,47.985382134405825,27.407657819442747,0.11155393475895271,0.01943348418653492 diff --git a/paper/src/chapters/figures/results/generated/legacy/ppo_pairwise_win_rates.csv b/paper/src/chapters/figures/results/generated/legacy/ppo_pairwise_win_rates.csv deleted file mode 100644 index 856cc8b..0000000 --- a/paper/src/chapters/figures/results/generated/legacy/ppo_pairwise_win_rates.csv +++ /dev/null @@ -1,25 +0,0 @@ -alpha,metric,direction,wins,ties,total_pairs,win_probability -0.0,eval/revenue_mean,higher,0,0,16,0.0 -0.0,eval/reward_mean,higher,0,0,16,0.0 -0.0,eval/coi_leakage_mean,lower,14,0,16,0.875 -0.0,eval/volatility_mean,lower,8,0,16,0.5 -0.1,eval/revenue_mean,higher,0,0,16,0.0 -0.1,eval/reward_mean,higher,0,0,16,0.0 -0.1,eval/coi_leakage_mean,lower,16,0,16,1.0 -0.1,eval/volatility_mean,lower,8,0,16,0.5 -0.25,eval/revenue_mean,higher,0,0,16,0.0 -0.25,eval/reward_mean,higher,0,0,16,0.0 -0.25,eval/coi_leakage_mean,lower,12,0,16,0.75 -0.25,eval/volatility_mean,lower,11,0,16,0.6875 -0.4,eval/revenue_mean,higher,0,0,16,0.0 -0.4,eval/reward_mean,higher,0,0,16,0.0 -0.4,eval/coi_leakage_mean,lower,16,0,16,1.0 -0.4,eval/volatility_mean,lower,6,0,16,0.375 -0.6,eval/revenue_mean,higher,0,0,16,0.0 -0.6,eval/reward_mean,higher,0,0,16,0.0 -0.6,eval/coi_leakage_mean,lower,16,0,16,1.0 -0.6,eval/volatility_mean,lower,7,0,16,0.4375 -0.8,eval/revenue_mean,higher,0,0,16,0.0 -0.8,eval/reward_mean,higher,0,0,16,0.0 -0.8,eval/coi_leakage_mean,lower,11,0,16,0.6875 -0.8,eval/volatility_mean,lower,0,0,16,0.0 diff --git a/paper/src/chapters/figures/results/includes/final/final_focus_coi_by_alpha.tex b/paper/src/chapters/figures/results/includes/final_focus_coi_by_alpha.tex similarity index 100% rename from paper/src/chapters/figures/results/includes/final/final_focus_coi_by_alpha.tex rename to paper/src/chapters/figures/results/includes/final_focus_coi_by_alpha.tex diff --git a/paper/src/chapters/figures/results/includes/final/final_focus_coi_preservation_grid.tex b/paper/src/chapters/figures/results/includes/final_focus_coi_preservation_grid.tex similarity index 100% rename from paper/src/chapters/figures/results/includes/final/final_focus_coi_preservation_grid.tex rename to paper/src/chapters/figures/results/includes/final_focus_coi_preservation_grid.tex diff --git a/paper/src/chapters/figures/results/includes/final/final_focus_revenue_by_alpha.tex b/paper/src/chapters/figures/results/includes/final_focus_revenue_by_alpha.tex similarity index 100% rename from paper/src/chapters/figures/results/includes/final/final_focus_revenue_by_alpha.tex rename to paper/src/chapters/figures/results/includes/final_focus_revenue_by_alpha.tex diff --git a/paper/src/chapters/figures/results/includes/final/final_focus_revenue_delta.tex b/paper/src/chapters/figures/results/includes/final_focus_revenue_delta.tex similarity index 100% rename from paper/src/chapters/figures/results/includes/final/final_focus_revenue_delta.tex rename to paper/src/chapters/figures/results/includes/final_focus_revenue_delta.tex diff --git a/paper/src/chapters/figures/results/includes/final/final_focus_risk_deltas.tex b/paper/src/chapters/figures/results/includes/final_focus_risk_deltas.tex similarity index 100% rename from paper/src/chapters/figures/results/includes/final/final_focus_risk_deltas.tex rename to paper/src/chapters/figures/results/includes/final_focus_risk_deltas.tex diff --git a/paper/src/chapters/figures/results/includes/legacy/first_sweep_tier_revenue.tex b/paper/src/chapters/figures/results/includes/legacy/first_sweep_tier_revenue.tex deleted file mode 100644 index 52a61b4..0000000 --- a/paper/src/chapters/figures/results/includes/legacy/first_sweep_tier_revenue.tex +++ /dev/null @@ -1 +0,0 @@ -\includegraphics[width=0.99\linewidth]{chapters/figures/results/generated/legacy/plots/first_sweep_tier_revenue.pdf} diff --git a/paper/src/chapters/figures/results/includes/legacy/ppo_alpha_curves.tex b/paper/src/chapters/figures/results/includes/legacy/ppo_alpha_curves.tex deleted file mode 100644 index b4f6618..0000000 --- a/paper/src/chapters/figures/results/includes/legacy/ppo_alpha_curves.tex +++ /dev/null @@ -1 +0,0 @@ -\includegraphics[width=0.98\linewidth]{chapters/figures/results/generated/legacy/plots/ppo_alpha_curves.pdf} diff --git a/paper/src/chapters/figures/results/includes/legacy/ppo_delta_curves.tex b/paper/src/chapters/figures/results/includes/legacy/ppo_delta_curves.tex deleted file mode 100644 index 2b37f92..0000000 --- a/paper/src/chapters/figures/results/includes/legacy/ppo_delta_curves.tex +++ /dev/null @@ -1 +0,0 @@ -\includegraphics[width=0.98\linewidth]{chapters/figures/results/generated/legacy/plots/ppo_delta_curves.pdf} diff --git a/paper/src/chapters/figures/results/includes/legacy/ppo_tradeoff_scatter.tex b/paper/src/chapters/figures/results/includes/legacy/ppo_tradeoff_scatter.tex deleted file mode 100644 index 7b795d1..0000000 --- a/paper/src/chapters/figures/results/includes/legacy/ppo_tradeoff_scatter.tex +++ /dev/null @@ -1 +0,0 @@ -\includegraphics[width=0.88\linewidth]{chapters/figures/results/generated/legacy/plots/ppo_tradeoff_scatter.pdf} diff --git a/paper/src/chapters/figures/results/plot_results.py b/paper/src/chapters/figures/results/plot_results.py deleted file mode 100644 index 0476948..0000000 --- a/paper/src/chapters/figures/results/plot_results.py +++ /dev/null @@ -1,313 +0,0 @@ -from __future__ import annotations - -import argparse -from pathlib import Path - -import matplotlib - -matplotlib.use("Agg") -import matplotlib.pyplot as plt -from matplotlib.ticker import FuncFormatter -import numpy as np -import pandas as pd - -from process_first_sweep import run as run_first_sweep -from process_ppo_benchmark import run as run_ppo_benchmark - - -def _output_dir() -> Path: - return Path(__file__).resolve().parent / "generated" / "legacy" - - -def _plot_dir() -> Path: - return _output_dir() / "plots" - - -def _configure_style() -> None: - plt.rcParams.update( - { - "font.family": "serif", - "font.size": 10, - "axes.titlesize": 10, - "axes.labelsize": 9, - "legend.fontsize": 8, - "xtick.labelsize": 8, - "ytick.labelsize": 8, - "figure.dpi": 220, - "savefig.dpi": 320, - "axes.spines.top": False, - "axes.spines.right": False, - "axes.grid": True, - "grid.alpha": 0.22, - } - ) - - -def _fmt_thousands(value: float, _: int) -> str: - return f"{int(value):,}" - - -def _load_csv(path: Path) -> pd.DataFrame: - if not path.exists(): - raise FileNotFoundError(f"Missing required input: {path}") - return pd.read_csv(path) - - -def _plot_ppo_alpha_curves(alpha_mode: pd.DataFrame, out_dir: Path) -> Path: - fig, axes = plt.subplots(2, 2, figsize=(9.3, 6.4), constrained_layout=True) - robust_color = "#C44E52" - baseline_color = "#4C72B0" - mode_colors = {"robust": robust_color, "no_robust": baseline_color} - mode_labels = {"robust": "Robust", "no_robust": "Non-robust"} - - panels = [ - ("eval_revenue_mean", "Mean Episode Revenue", "Revenue"), - ("eval_reward_mean", "Mean Episode Reward", "Reward"), - ("eval_coi_leakage_mean", "Mean COI Leakage", "COI Leakage"), - ("eval_volatility_mean", "Mean Price Volatility", "Volatility"), - ] - - for ax, (metric_prefix, title, ylabel) in zip(axes.flat, panels): - mean_col = f"{metric_prefix}_mean" - std_col = f"{metric_prefix}_std" - for mode in ("no_robust", "robust"): - sub = alpha_mode[alpha_mode["mode"] == mode].sort_values("alpha") - if sub.empty: - continue - x = sub["alpha"].to_numpy(dtype=float) - y = sub[mean_col].to_numpy(dtype=float) - ax.plot( - x, - y, - marker="o", - linewidth=1.8, - markersize=4, - color=mode_colors[mode], - label=mode_labels[mode], - ) - if std_col in sub.columns: - sigma = sub[std_col].fillna(0.0).to_numpy(dtype=float) - ax.fill_between( - x, - y - sigma, - y + sigma, - color=mode_colors[mode], - alpha=0.14, - linewidth=0, - ) - - ax.set_title(title) - ax.set_xlabel(r"Contamination $\alpha$") - ax.set_ylabel(ylabel) - ax.set_xticks(sorted(alpha_mode["alpha"].unique())) - if metric_prefix in {"eval_revenue_mean", "eval_reward_mean"}: - ax.yaxis.set_major_formatter(FuncFormatter(_fmt_thousands)) - - handles, labels = axes.flat[0].get_legend_handles_labels() - fig.legend(handles, labels, ncol=2, loc="upper center", bbox_to_anchor=(0.5, 1.02)) - - out_path = out_dir / "ppo_alpha_curves.pdf" - fig.savefig(out_path, bbox_inches="tight") - plt.close(fig) - return out_path - - -def _plot_ppo_delta_curves(deltas: pd.DataFrame, out_dir: Path) -> Path: - fig, axes = plt.subplots(2, 1, figsize=(8.6, 6.0), constrained_layout=True) - deltas = deltas.sort_values("alpha") - x = deltas["alpha"].to_numpy(dtype=float) - - top_metrics = [ - ("eval_revenue_mean_delta_pct", "Revenue", "#4C72B0"), - ("eval_reward_mean_delta_pct", "Reward", "#8172B3"), - ] - for col, label, color in top_metrics: - axes[0].plot( - x, - deltas[col].to_numpy(dtype=float), - marker="o", - linewidth=1.8, - markersize=4, - color=color, - label=label, - ) - axes[0].axhline(0.0, color="#444444", linewidth=1.0, linestyle="--") - axes[0].set_title("Robust Minus Non-robust Delta by Contamination") - axes[0].set_ylabel("Delta (%)") - axes[0].set_xlabel(r"Contamination $\alpha$") - axes[0].set_xticks(x) - axes[0].legend(loc="lower left") - - bottom_metrics = [ - ("eval_coi_leakage_mean_delta_pct", "COI Leakage", "#55A868"), - ("eval_volatility_mean_delta_pct", "Volatility", "#DD8452"), - ] - for col, label, color in bottom_metrics: - axes[1].plot( - x, - deltas[col].to_numpy(dtype=float), - marker="o", - linewidth=1.8, - markersize=4, - color=color, - label=label, - ) - axes[1].axhline(0.0, color="#444444", linewidth=1.0, linestyle="--") - axes[1].set_ylabel("Delta (%)") - axes[1].set_xlabel(r"Contamination $\alpha$") - axes[1].set_xticks(x) - axes[1].legend(loc="lower left") - - out_path = out_dir / "ppo_delta_curves.pdf" - fig.savefig(out_path, bbox_inches="tight") - plt.close(fig) - return out_path - - -def _plot_ppo_tradeoff_scatter(deltas: pd.DataFrame, out_dir: Path) -> Path: - fig, ax = plt.subplots(figsize=(6.4, 5.2), constrained_layout=True) - data = deltas.sort_values("alpha") - x = data["eval_coi_leakage_mean_delta_pct"].to_numpy(dtype=float) - y = data["eval_revenue_mean_delta_pct"].to_numpy(dtype=float) - alphas = data["alpha"].to_numpy(dtype=float) - - scatter = ax.scatter( - x, - y, - c=alphas, - cmap="viridis", - s=72, - edgecolor="#222222", - linewidth=0.5, - ) - for x_i, y_i, alpha in zip(x, y, alphas): - ax.annotate( - rf"$\alpha={alpha:.2f}$", - (x_i, y_i), - textcoords="offset points", - xytext=(5, 4), - fontsize=8, - ) - - ax.axhline(0.0, color="#555555", linewidth=1.0, linestyle="--") - ax.axvline(0.0, color="#555555", linewidth=1.0, linestyle="--") - ax.set_xlabel("COI Leakage Delta (%)") - ax.set_ylabel("Revenue Delta (%)") - ax.set_title("PPO Robust Tradeoff Frontier") - cbar = fig.colorbar(scatter, ax=ax) - cbar.set_label(r"Contamination $\alpha$") - - out_path = out_dir / "ppo_tradeoff_scatter.pdf" - fig.savefig(out_path, bbox_inches="tight") - plt.close(fig) - return out_path - - -def _plot_first_sweep_tier_revenue(tier_mode: pd.DataFrame, out_dir: Path) -> Path: - pivot = ( - tier_mode.pivot(index="tier", columns="mode", values="eval_revenue_mean_mean") - .dropna(subset=["robust", "no_robust"], how="any") - .copy() - ) - if pivot.empty: - raise ValueError("First sweep tier summary missing robust/non-robust pairs") - - order = sorted(pivot.index.tolist()) - pivot = pivot.loc[order] - delta_pct = 100.0 * (pivot["robust"] - pivot["no_robust"]) / pivot["no_robust"] - - fig, axes = plt.subplots(1, 2, figsize=(10.2, 4.3), constrained_layout=True) - x = np.arange(len(order)) - width = 0.36 - - axes[0].bar( - x - width / 2, - pivot["no_robust"].to_numpy(dtype=float), - width=width, - label="Non-robust", - color="#4C72B0", - ) - axes[0].bar( - x + width / 2, - pivot["robust"].to_numpy(dtype=float), - width=width, - label="Robust", - color="#C44E52", - ) - axes[0].set_xticks(x) - axes[0].set_xticklabels(order, rotation=20) - axes[0].set_ylabel("Mean Revenue") - axes[0].set_yscale("log") - axes[0].yaxis.set_major_formatter(FuncFormatter(_fmt_thousands)) - axes[0].set_title("First Sweep Tier Revenue (log scale)") - axes[0].legend() - - axes[1].bar(x, delta_pct.to_numpy(dtype=float), color="#55A868", width=0.55) - axes[1].axhline(0.0, color="#444444", linewidth=1.0, linestyle="--") - axes[1].set_xticks(x) - axes[1].set_xticklabels(order, rotation=20) - axes[1].set_ylabel("Revenue Delta (%)") - axes[1].set_title("Robust Minus Non-robust by Tier") - - out_path = out_dir / "first_sweep_tier_revenue.pdf" - fig.savefig(out_path, bbox_inches="tight") - plt.close(fig) - return out_path - - -def build_plots(data_dir: Path, out_dir: Path) -> list[Path]: - alpha_mode = _load_csv(data_dir / "ppo_alpha_mode_summary.csv") - deltas = _load_csv(data_dir / "ppo_alpha_deltas.csv") - tier_mode = _load_csv(data_dir / "first_sweep_tier_mode_summary.csv") - - out_dir.mkdir(parents=True, exist_ok=True) - paths = [ - _plot_ppo_alpha_curves(alpha_mode, out_dir), - _plot_ppo_delta_curves(deltas, out_dir), - _plot_ppo_tradeoff_scatter(deltas, out_dir), - _plot_first_sweep_tier_revenue(tier_mode, out_dir), - ] - return paths - - -def main() -> None: - parser = argparse.ArgumentParser( - description="Create paper-ready plots from result CSVs" - ) - parser.add_argument("--data-dir", type=Path, default=_output_dir()) - parser.add_argument("--plot-dir", type=Path, default=_plot_dir()) - parser.add_argument( - "--refresh-data", - action="store_true", - help="Regenerate processed CSVs before plotting", - ) - args = parser.parse_args() - - _configure_style() - - if bool(args.refresh_data): - run_ppo_benchmark( - input_path=Path(__file__).resolve().parents[5] - / "tpu_orchestration" - / "results" - / "ppo_benchmark.csv", - output_dir=args.data_dir, - include_non_finished=False, - ) - run_first_sweep( - input_path=Path(__file__).resolve().parents[5] - / "tpu_orchestration" - / "results" - / "first_sweep.csv", - output_dir=args.data_dir, - include_non_finished=False, - top_n=25, - ) - - outputs = build_plots(data_dir=args.data_dir, out_dir=args.plot_dir) - for path in outputs: - print(path) - - -if __name__ == "__main__": - main() diff --git a/paper/src/chapters/figures/results/process_all_results.py b/paper/src/chapters/figures/results/process_all_results.py deleted file mode 100644 index 78ca65f..0000000 --- a/paper/src/chapters/figures/results/process_all_results.py +++ /dev/null @@ -1,51 +0,0 @@ -from __future__ import annotations - -import argparse -from pathlib import Path - -from process_first_sweep import run as run_first_sweep -from process_ppo_benchmark import run as run_ppo_benchmark - - -def _default_output_dir() -> Path: - return Path(__file__).resolve().parent / "generated" / "legacy" - - -def main() -> None: - parser = argparse.ArgumentParser( - description="Process all result CSV exports for paper figures" - ) - parser.add_argument("--output-dir", type=Path, default=_default_output_dir()) - parser.add_argument("--include-non-finished", action="store_true") - parser.add_argument("--top-n", type=int, default=25) - args = parser.parse_args() - - written: list[Path] = [] - written.extend( - run_ppo_benchmark( - input_path=Path(__file__).resolve().parents[5] - / "tpu_orchestration" - / "results" - / "ppo_benchmark.csv", - output_dir=args.output_dir, - include_non_finished=bool(args.include_non_finished), - ) - ) - written.extend( - run_first_sweep( - input_path=Path(__file__).resolve().parents[5] - / "tpu_orchestration" - / "results" - / "first_sweep.csv", - output_dir=args.output_dir, - include_non_finished=bool(args.include_non_finished), - top_n=int(args.top_n), - ) - ) - - for path in written: - print(path) - - -if __name__ == "__main__": - main() diff --git a/paper/src/chapters/figures/results/process_final_sweeps.py b/paper/src/chapters/figures/results/process_final_sweeps.py index 3dc5c0a..550eb8a 100644 --- a/paper/src/chapters/figures/results/process_final_sweeps.py +++ b/paper/src/chapters/figures/results/process_final_sweeps.py @@ -639,7 +639,7 @@ def run( ) ) - include_dir = Path(__file__).resolve().parent / "includes" / "final" + include_dir = Path(__file__).resolve().parent / "includes" written.append( _write_include( include_dir / "final_focus_revenue_by_alpha.tex", diff --git a/paper/src/chapters/figures/results/process_first_sweep.py b/paper/src/chapters/figures/results/process_first_sweep.py deleted file mode 100644 index 0e62525..0000000 --- a/paper/src/chapters/figures/results/process_first_sweep.py +++ /dev/null @@ -1,272 +0,0 @@ -from __future__ import annotations - -import argparse -import json -from pathlib import Path -from typing import Iterable - -import numpy as np -import pandas as pd - - -def _project_root() -> Path: - return Path(__file__).resolve().parents[5] - - -def _default_input() -> Path: - return _project_root() / "tpu_orchestration" / "results" / "first_sweep.csv" - - -def _default_output_dir() -> Path: - return Path(__file__).resolve().parent / "generated" / "legacy" - - -def _sanitize(key: str) -> str: - return key.replace("/", "_").replace("-", "_") - - -def _coerce_numeric(frame: pd.DataFrame, columns: Iterable[str]) -> None: - for column in columns: - if column in frame.columns: - frame[column] = pd.to_numeric(frame[column], errors="coerce") - - -def _extract_alpha(frame: pd.DataFrame) -> pd.Series: - if "study/alpha" in frame.columns: - return pd.to_numeric(frame["study/alpha"], errors="coerce") - if "alpha" in frame.columns: - return pd.to_numeric(frame["alpha"], errors="coerce") - return pd.Series(np.nan, index=frame.index, dtype=float) - - -def _extract_mode(frame: pd.DataFrame) -> pd.Series: - if "study/mode" in frame.columns: - return frame["study/mode"].astype(str).str.strip().str.lower() - if "study/no_robust" in frame.columns: - no_robust = pd.to_numeric(frame["study/no_robust"], errors="coerce").fillna(0.0) - return pd.Series( - np.where(no_robust > 0.5, "no_robust", "robust"), - index=frame.index, - dtype="object", - ) - if "no_robust" in frame.columns: - no_robust = ( - frame["no_robust"].astype(str).str.lower().isin({"1", "true", "yes"}) - ) - return pd.Series( - np.where(no_robust, "no_robust", "robust"), - index=frame.index, - dtype="object", - ) - return pd.Series("", index=frame.index, dtype="object") - - -def _extract_tier(frame: pd.DataFrame) -> pd.Series: - for column in ("tiers", "runtime/backend", "algo", "run.backend", "run.algo"): - if column in frame.columns: - tier = frame[column].astype(str).str.strip().str.lower() - if tier.notna().any(): - return tier - return pd.Series("unknown", index=frame.index, dtype="object") - - -def _prepare_frame(frame: pd.DataFrame, include_non_finished: bool) -> pd.DataFrame: - data = frame.copy() - if not include_non_finished and "State" in data.columns: - data = data[data["State"].astype(str).str.lower() == "finished"].copy() - - data["alpha"] = _extract_alpha(data) - data["mode"] = _extract_mode(data) - data["tier"] = _extract_tier(data) - data = data[data["mode"].isin({"robust", "no_robust"})] - data = data[data["alpha"].notna()] - - _coerce_numeric( - data, - [ - "eval/revenue_mean", - "eval/reward_mean", - "eval/coi_level_mean", - "eval/coi_leakage_mean", - "eval/margin_mean", - "eval/volatility_mean", - "objective/score", - "train/alpha_adv", - "lambda_coi", - "robust_radius", - "learning_rate", - "batch_size", - "n_steps", - "total_timesteps", - ], - ) - return data.sort_values(["tier", "alpha", "mode"]).reset_index(drop=True) - - -def _group_summary( - frame: pd.DataFrame, by: list[str], metrics: list[str] -) -> pd.DataFrame: - agg_spec: dict[str, tuple[str, str]] = {"runs": ("mode", "size")} - for metric in metrics: - safe = _sanitize(metric) - agg_spec[f"{safe}_mean"] = (metric, "mean") - agg_spec[f"{safe}_std"] = (metric, "std") - return frame.groupby(by, as_index=False).agg(**agg_spec).sort_values(by) - - -def _tier_alpha_deltas(summary: pd.DataFrame, metrics: list[str]) -> pd.DataFrame: - rows: list[dict[str, float | str]] = [] - for (tier, alpha), group in summary.groupby(["tier", "alpha"], sort=True): - robust = group[group["mode"] == "robust"] - no_robust = group[group["mode"] == "no_robust"] - if robust.empty or no_robust.empty: - continue - - row: dict[str, float | str] = { - "tier": str(tier), - "alpha": float(alpha), - "runs_robust": float(robust["runs"].iloc[0]), - "runs_no_robust": float(no_robust["runs"].iloc[0]), - } - for metric in metrics: - safe = _sanitize(metric) - robust_value = float(robust[f"{safe}_mean"].iloc[0]) - no_robust_value = float(no_robust[f"{safe}_mean"].iloc[0]) - delta = robust_value - no_robust_value - row[f"{safe}_delta"] = delta - row[f"{safe}_delta_pct"] = ( - np.nan if no_robust_value == 0 else 100.0 * delta / no_robust_value - ) - rows.append(row) - - return pd.DataFrame(rows) - - -def _top_runs(frame: pd.DataFrame, n: int) -> pd.DataFrame: - rank_metric = "objective/score" - if rank_metric not in frame.columns or frame[rank_metric].notna().sum() == 0: - rank_metric = "eval/reward_mean" - - keep = [ - "Name", - "tier", - "alpha", - "mode", - rank_metric, - "eval/revenue_mean", - "eval/reward_mean", - "eval/coi_level_mean", - "eval/coi_leakage_mean", - "lambda_coi", - "robust_radius", - "learning_rate", - "batch_size", - "n_steps", - "total_timesteps", - ] - present = [column for column in keep if column in frame.columns] - ranked = frame[present].copy().sort_values(rank_metric, ascending=False) - return ranked.head(max(1, int(n))).reset_index(drop=True) - - -def _headline_json( - frame: pd.DataFrame, tier_mode: pd.DataFrame -) -> dict[str, float | str]: - out: dict[str, float | str] = { - "runs": int(len(frame)), - "tiers": int(frame["tier"].nunique()), - "alphas": int(frame["alpha"].nunique()), - } - - robust_rows = tier_mode[tier_mode["mode"] == "robust"] - no_robust_rows = tier_mode[tier_mode["mode"] == "no_robust"] - if robust_rows.empty or no_robust_rows.empty: - out["status"] = "incomplete_modes" - return out - - robust_mean = robust_rows["eval_revenue_mean_mean"].mean() - no_robust_mean = no_robust_rows["eval_revenue_mean_mean"].mean() - out.update( - { - "status": "ok", - "mean_tier_revenue_robust": float(robust_mean), - "mean_tier_revenue_no_robust": float(no_robust_mean), - "mean_tier_revenue_delta": float(robust_mean - no_robust_mean), - "mean_tier_revenue_delta_pct": float( - 100.0 * (robust_mean - no_robust_mean) / no_robust_mean - ) - if no_robust_mean - else np.nan, - } - ) - return out - - -def run( - input_path: Path, output_dir: Path, include_non_finished: bool, top_n: int -) -> list[Path]: - output_dir.mkdir(parents=True, exist_ok=True) - raw = pd.read_csv(input_path) - frame = _prepare_frame(raw, include_non_finished=include_non_finished) - - metrics = [ - metric - for metric in ( - "eval/revenue_mean", - "eval/reward_mean", - "eval/coi_level_mean", - "eval/coi_leakage_mean", - "eval/margin_mean", - "eval/volatility_mean", - "objective/score", - "train/alpha_adv", - ) - if metric in frame.columns - ] - - tier_mode = _group_summary(frame, ["tier", "mode"], metrics) - tier_alpha_mode = _group_summary(frame, ["tier", "alpha", "mode"], metrics) - deltas = _tier_alpha_deltas(tier_alpha_mode, metrics) - top_configs = _top_runs(frame, n=top_n) - headline = _headline_json(frame, tier_mode) - - outputs = { - "first_sweep_tier_mode_summary.csv": tier_mode, - "first_sweep_tier_alpha_mode_summary.csv": tier_alpha_mode, - "first_sweep_tier_alpha_deltas.csv": deltas, - "first_sweep_top_configs.csv": top_configs, - } - written_paths: list[Path] = [] - for filename, table in outputs.items(): - path = output_dir / filename - table.to_csv(path, index=False) - written_paths.append(path) - - headline_path = output_dir / "first_sweep_headline_summary.json" - headline_path.write_text(json.dumps(headline, indent=2)) - written_paths.append(headline_path) - return written_paths - - -def main() -> None: - parser = argparse.ArgumentParser( - description="Process first sweep CSV for paper tables" - ) - parser.add_argument("--input", type=Path, default=_default_input()) - parser.add_argument("--output-dir", type=Path, default=_default_output_dir()) - parser.add_argument("--include-non-finished", action="store_true") - parser.add_argument("--top-n", type=int, default=25) - args = parser.parse_args() - - written = run( - input_path=args.input, - output_dir=args.output_dir, - include_non_finished=bool(args.include_non_finished), - top_n=int(args.top_n), - ) - for path in written: - print(path) - - -if __name__ == "__main__": - main() diff --git a/paper/src/chapters/figures/results/process_ppo_benchmark.py b/paper/src/chapters/figures/results/process_ppo_benchmark.py deleted file mode 100644 index 85f48b2..0000000 --- a/paper/src/chapters/figures/results/process_ppo_benchmark.py +++ /dev/null @@ -1,277 +0,0 @@ -from __future__ import annotations - -import argparse -import json -from pathlib import Path -from typing import Iterable - -import numpy as np -import pandas as pd - - -def _project_root() -> Path: - return Path(__file__).resolve().parents[5] - - -def _default_input() -> Path: - return _project_root() / "tpu_orchestration" / "results" / "ppo_benchmark.csv" - - -def _default_output_dir() -> Path: - return Path(__file__).resolve().parent / "generated" / "legacy" - - -def _sanitize(key: str) -> str: - return key.replace("/", "_").replace("-", "_") - - -def _coerce_numeric(frame: pd.DataFrame, columns: Iterable[str]) -> None: - for column in columns: - if column in frame.columns: - frame[column] = pd.to_numeric(frame[column], errors="coerce") - - -def _extract_alpha(frame: pd.DataFrame) -> pd.Series: - if "study/alpha" in frame.columns: - return pd.to_numeric(frame["study/alpha"], errors="coerce") - if "alpha" in frame.columns: - return pd.to_numeric(frame["alpha"], errors="coerce") - return pd.Series(np.nan, index=frame.index, dtype=float) - - -def _extract_mode(frame: pd.DataFrame) -> pd.Series: - if "study/mode" in frame.columns: - return frame["study/mode"].astype(str).str.strip().str.lower() - if "study/no_robust" in frame.columns: - no_robust = pd.to_numeric(frame["study/no_robust"], errors="coerce").fillna(0.0) - return pd.Series( - np.where(no_robust > 0.5, "no_robust", "robust"), - index=frame.index, - dtype="object", - ) - if "no_robust" in frame.columns: - no_robust = ( - frame["no_robust"].astype(str).str.lower().isin({"1", "true", "yes"}) - ) - return pd.Series( - np.where(no_robust, "no_robust", "robust"), - index=frame.index, - dtype="object", - ) - return pd.Series("", index=frame.index, dtype="object") - - -def _prepare_frame(frame: pd.DataFrame, include_non_finished: bool) -> pd.DataFrame: - data = frame.copy() - if not include_non_finished and "State" in data.columns: - data = data[data["State"].astype(str).str.lower() == "finished"].copy() - - data["alpha"] = _extract_alpha(data) - data["mode"] = _extract_mode(data) - data = data[data["mode"].isin({"robust", "no_robust"})] - data = data[data["alpha"].notna()] - - numeric_cols = [ - "eval/revenue_mean", - "eval/reward_mean", - "eval/coi_level_mean", - "eval/coi_leakage_mean", - "eval/volatility_mean", - "eval/margin_mean", - "train/alpha_adv", - "train/coi_penalty", - "train/ux_penalty", - "train/agent_prob", - ] - _coerce_numeric(data, numeric_cols) - return data.sort_values(["alpha", "mode"]).reset_index(drop=True) - - -def _summary_by_alpha_mode(frame: pd.DataFrame, metrics: list[str]) -> pd.DataFrame: - agg_spec: dict[str, tuple[str, str]] = {"runs": ("mode", "size")} - for metric in metrics: - safe = _sanitize(metric) - agg_spec[f"{safe}_mean"] = (metric, "mean") - agg_spec[f"{safe}_std"] = (metric, "std") - - return ( - frame.groupby(["alpha", "mode"], as_index=False) - .agg(**agg_spec) - .sort_values(["alpha", "mode"]) - .reset_index(drop=True) - ) - - -def _delta_by_alpha(summary: pd.DataFrame, metrics: list[str]) -> pd.DataFrame: - rows: list[dict[str, float]] = [] - for alpha, alpha_group in summary.groupby("alpha", sort=True): - robust = alpha_group[alpha_group["mode"] == "robust"] - no_robust = alpha_group[alpha_group["mode"] == "no_robust"] - if robust.empty or no_robust.empty: - continue - - row: dict[str, float] = { - "alpha": float(alpha), - "runs_robust": float(robust["runs"].iloc[0]), - "runs_no_robust": float(no_robust["runs"].iloc[0]), - } - for metric in metrics: - safe = _sanitize(metric) - robust_value = float(robust[f"{safe}_mean"].iloc[0]) - no_robust_value = float(no_robust[f"{safe}_mean"].iloc[0]) - delta = robust_value - no_robust_value - row[f"{safe}_robust"] = robust_value - row[f"{safe}_no_robust"] = no_robust_value - row[f"{safe}_delta"] = delta - row[f"{safe}_delta_pct"] = ( - np.nan if no_robust_value == 0 else 100.0 * delta / no_robust_value - ) - rows.append(row) - - return pd.DataFrame(rows) - - -def _pairwise_win_rates(frame: pd.DataFrame) -> pd.DataFrame: - rules = { - "eval/revenue_mean": "higher", - "eval/reward_mean": "higher", - "eval/coi_leakage_mean": "lower", - "eval/volatility_mean": "lower", - } - rows: list[dict[str, float]] = [] - for alpha, alpha_group in frame.groupby("alpha", sort=True): - robust = alpha_group[alpha_group["mode"] == "robust"] - no_robust = alpha_group[alpha_group["mode"] == "no_robust"] - if robust.empty or no_robust.empty: - continue - - for metric, direction in rules.items(): - if metric not in frame.columns: - continue - robust_values = robust[metric].dropna().to_numpy(dtype=float) - no_robust_values = no_robust[metric].dropna().to_numpy(dtype=float) - if robust_values.size == 0 or no_robust_values.size == 0: - continue - - if direction == "higher": - wins = (robust_values[:, None] > no_robust_values[None, :]).sum() - else: - wins = (robust_values[:, None] < no_robust_values[None, :]).sum() - ties = (robust_values[:, None] == no_robust_values[None, :]).sum() - total = robust_values.size * no_robust_values.size - win_prob = (wins + 0.5 * ties) / total - rows.append( - { - "alpha": float(alpha), - "metric": metric, - "direction": direction, - "wins": int(wins), - "ties": int(ties), - "total_pairs": int(total), - "win_probability": float(win_prob), - } - ) - return pd.DataFrame(rows) - - -def _overall_mode_summary(frame: pd.DataFrame, metrics: list[str]) -> pd.DataFrame: - agg_spec: dict[str, tuple[str, str]] = {"runs": ("mode", "size")} - for metric in metrics: - safe = _sanitize(metric) - agg_spec[f"{safe}_mean"] = (metric, "mean") - agg_spec[f"{safe}_std"] = (metric, "std") - return frame.groupby("mode", as_index=False).agg(**agg_spec).sort_values("mode") - - -def _headline_json(overall: pd.DataFrame) -> dict[str, float | str]: - if {"robust", "no_robust"} - set(overall["mode"].tolist()): - return {"status": "incomplete_modes"} - - robust = overall[overall["mode"] == "robust"].iloc[0] - no_robust = overall[overall["mode"] == "no_robust"].iloc[0] - - revenue_delta = float( - robust["eval_revenue_mean_mean"] - no_robust["eval_revenue_mean_mean"] - ) - leakage_delta = float( - robust["eval_coi_leakage_mean_mean"] - no_robust["eval_coi_leakage_mean_mean"] - ) - return { - "status": "ok", - "revenue_delta": revenue_delta, - "revenue_delta_pct": float( - 100.0 * revenue_delta / no_robust["eval_revenue_mean_mean"] - ), - "coi_leakage_delta": leakage_delta, - "coi_leakage_delta_pct": float( - 100.0 * leakage_delta / no_robust["eval_coi_leakage_mean_mean"] - ), - } - - -def run(input_path: Path, output_dir: Path, include_non_finished: bool) -> list[Path]: - output_dir.mkdir(parents=True, exist_ok=True) - raw = pd.read_csv(input_path) - frame = _prepare_frame(raw, include_non_finished=include_non_finished) - - metrics = [ - metric - for metric in ( - "eval/revenue_mean", - "eval/reward_mean", - "eval/coi_level_mean", - "eval/coi_leakage_mean", - "eval/volatility_mean", - "eval/margin_mean", - "train/alpha_adv", - "train/coi_penalty", - "train/ux_penalty", - "train/agent_prob", - ) - if metric in frame.columns - ] - - alpha_mode = _summary_by_alpha_mode(frame, metrics) - deltas = _delta_by_alpha(alpha_mode, metrics) - win_rates = _pairwise_win_rates(frame) - overall = _overall_mode_summary(frame, metrics) - headline = _headline_json(overall) - - outputs = { - "ppo_alpha_mode_summary.csv": alpha_mode, - "ppo_alpha_deltas.csv": deltas, - "ppo_pairwise_win_rates.csv": win_rates, - "ppo_overall_mode_summary.csv": overall, - } - written_paths: list[Path] = [] - for filename, table in outputs.items(): - path = output_dir / filename - table.to_csv(path, index=False) - written_paths.append(path) - - headline_path = output_dir / "ppo_headline_summary.json" - headline_path.write_text(json.dumps(headline, indent=2)) - written_paths.append(headline_path) - return written_paths - - -def main() -> None: - parser = argparse.ArgumentParser( - description="Process PPO benchmark CSV for paper tables" - ) - parser.add_argument("--input", type=Path, default=_default_input()) - parser.add_argument("--output-dir", type=Path, default=_default_output_dir()) - parser.add_argument("--include-non-finished", action="store_true") - args = parser.parse_args() - - written = run( - input_path=args.input, - output_dir=args.output_dir, - include_non_finished=bool(args.include_non_finished), - ) - for path in written: - print(path) - - -if __name__ == "__main__": - main() diff --git a/paper/src/chapters/figures/results/revenue_alpha_classic.py b/paper/src/chapters/figures/results/revenue_alpha_classic.py deleted file mode 100644 index a91f2f6..0000000 --- a/paper/src/chapters/figures/results/revenue_alpha_classic.py +++ /dev/null @@ -1,63 +0,0 @@ -from pathlib import Path - -import numpy as np -import pandas as pd -from scipy import stats - - -root = Path(__file__).resolve().parents[5] -runs = ( - root - / "engine/studies/results/wandb_sweep_bundles/bundle_20260317_122818/runs_finished.csv" -) - -df = pd.read_csv(runs) -df = df[ - (df["sweep_id"].astype(str) == "i88nw811") - & (df["study_mode"].astype(str) == "baseline") - & (pd.to_numeric(df["n_products"], errors="coerce") == 100.0) - & (pd.to_numeric(df["eta_ux"], errors="coerce") == 0.0) -].copy() - -alpha = pd.to_numeric(df["alpha"], errors="coerce") -revenue = pd.to_numeric(df["eval_revenue_mean"], errors="coerce") -mask = alpha.notna() & revenue.notna() -alpha = alpha[mask].to_numpy(dtype=float) -revenue = revenue[mask].to_numpy(dtype=float) - -if len(alpha) < 3 or np.unique(alpha).size < 2: - raise ValueError("Not enough data for regression") - -fit = stats.linregress(alpha, revenue) -n = len(alpha) -dof = n - 2 -t_stat = fit.slope / fit.stderr -p_val = 2.0 * stats.t.sf(abs(t_stat), df=dof) -r2 = fit.rvalue**2 -t_crit = stats.t.ppf(0.975, dof) -slope_ci = (fit.slope - t_crit * fit.stderr, fit.slope + t_crit * fit.stderr) - -x = np.column_stack([np.ones(n), alpha]) -beta = np.linalg.lstsq(x, revenue, rcond=None)[0] -resid = revenue - x @ beta -xtx_inv = np.linalg.pinv(x.T @ x) -meat = (x * resid[:, None]).T @ (x * resid[:, None]) -cov_hc1 = (n / (n - x.shape[1])) * (xtx_inv @ meat @ xtx_inv) -se_hc1 = np.sqrt(np.diag(cov_hc1)) -t_hc1 = beta[1] / se_hc1[1] -p_hc1 = 2.0 * stats.t.sf(abs(t_hc1), df=dof) -slope_ci_hc1 = (beta[1] - t_crit * se_hc1[1], beta[1] + t_crit * se_hc1[1]) - -print("Contamination-Revenue Slope") -print( - "cohort: bundle_20260317_122818, sweep=i88nw811, mode=baseline, n_products=100, eta_ux=0.0" -) -print(f"n={n}") -print(f"model: revenue = {fit.intercept:.2f} {fit.slope:+.2f} * alpha") -print( - f"OLS: t({dof})={t_stat:.2f}, p={p_val:.3e}, R^2={r2:.3f}, slope_95CI=[{slope_ci[0]:.2f}, {slope_ci[1]:.2f}]" -) -print( - f"HC1: t={t_hc1:.2f}, p={p_hc1:.3e}, slope_95CI=[{slope_ci_hc1[0]:.2f}, {slope_ci_hc1[1]:.2f}]" -) -print(f"effect: +0.1 alpha -> {0.1 * fit.slope:.2f} revenue units") diff --git a/paper/src/chapters/mdp_agent.pdf b/paper/src/chapters/mdp_agent.pdf index 24d141ea8f5e3b3d4d986e0cda0860d91e6d895b..17d299e68d5e4207c1cb1833a0049fb74da537c1 100644 GIT binary patch delta 291 zcmV+;0o?wyRkT&GeJFolYlAQp#ozrD=VeS8Xs)&P57LLWP{tTY+qbcY5Q7zlktB}w z+b_}T7p@yfu}<2U0Mc@j0M_ z>RSs6cDq4QYE|_@YJ`F}T7hC(#~H$#wVl}}$v|t(yIUhntaN`JMdLSGa@(l($gM

teS;p2x%N3&+mOe8UO(uV_@Z_w$zT{f^96s3q-euMBz4~!+nS1Fv+kkS~%&}YR z1P0lxIBC^)C<;ILd2|`*gf5P1-kQ$4iTi|KVRbL3>EO1G^1Bi8eA;{HQhR=uM)g`q pgT?H5aYA9o@WvM1;a|Z6xBN@hkACh3k9uzD7yFHGNwbkDBLNWClc)dy delta 291 zcmV+;0o?wyRkT&GeJFoXYr-%Th2Q%t&dZoGu({FJD#Zsa$QXlCeH(iSG1S6nN|KKG z@0V2DG4gaj?#VfaOJEVDNLa){2}QKVa-Bwbj`)1*6yj3U%0>XbMWkrw+VQR`*6K pi{<2b@83a~8A*Vw&v z0)u2#oU|GTl%*g1JiAPEK^I3g@6F)d#BISZuzHlsRJpCA{O*K2pAH_n)SjQEQKJ^p pV7q!=oKV;~ym1zH_*byvmVc@C-p}3OUe7K40{?|?PP30KA^{6VkLv&c delta 291 zcmV+;0o?wvU9nxTdn|v!YQr!PMDO~Fxs*B-Y)OtCH<%ofKq#dUlHN)WLKcpoSVodd z^Y@jU*rn*Sj~&en%RmV$QBcCc3P#up`Kly!jqLr}D`ZE}Dn|fhuex#g00+ehpCj6t zq4Qv5vl$gfYnnkSi$n@-4Oq&mbcy8Reec#;HqzFL{-UKzjctDyi`zF=39n7J7hWOR zuQ}x<&3VOGQSs#yWsj6DCX>HdcnH=}e-u{595LAl-c+N-*8GIHG(7%XR^Vl^nqzl9 z2ppEP;$Y3tqpsr+=bOt&Cv* Date: Wed, 8 Apr 2026 12:09:28 +0200 Subject: [PATCH 12/44] graceful fail --- .github/workflows/latex.yml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/latex.yml b/.github/workflows/latex.yml index a2b3a47..86d7912 100644 --- a/.github/workflows/latex.yml +++ b/.github/workflows/latex.yml @@ -9,6 +9,12 @@ on: paths: - 'paper/**' - '.github/**' + workflow_dispatch: + inputs: + skip_mirrors: + description: Skip Codex mirror generation (avoids API quota use) + type: boolean + default: false jobs: build: runs-on: ubuntu-latest @@ -24,8 +30,10 @@ jobs: - name: Prepare appendix code snapshot run: bash paper/concat_code.sh + # Repo variable SKIP_CODEX_MIRRORS=true skips on push/PR; workflow_dispatch can set skip_mirrors. - name: Generate mirrors with Codex - if: ${{ env.OPENAI_API_KEY != '' }} + if: ${{ env.OPENAI_API_KEY != '' && vars.SKIP_CODEX_MIRRORS != 'true' && (github.event_name != 'workflow_dispatch' || github.event.inputs.skip_mirrors != 'true') }} + continue-on-error: true uses: openai/codex-action@v1 with: openai-api-key: ${{ env.OPENAI_API_KEY }} From b287642ed04f158e9c1e82731c6070c411ad225b Mon Sep 17 00:00:00 2001 From: Daniel Rosel Date: Wed, 8 Apr 2026 17:42:51 +0200 Subject: [PATCH 13/44] cleaning reduncant --- paper/src/chapters/04-results.tex | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/paper/src/chapters/04-results.tex b/paper/src/chapters/04-results.tex index f661bd3..2dce74b 100644 --- a/paper/src/chapters/04-results.tex +++ b/paper/src/chapters/04-results.tex @@ -92,19 +92,7 @@ In our complete training runs we logged $\approx 180$ days of net compute time. \label{fig:final_focus_coi_preservation_grid} \end{figure} -\begin{figure}[ht] - \centering - \input{chapters/figures/results/includes/final_focus_revenue_delta.tex} - \caption{Defended-minus-baseline revenue delta over contamination for the final cohort. The strongest high-contamination deviation begins at $\alpha=0.7$, followed by recovery toward near parity by $\alpha=1.0$.} - \label{fig:final_focus_revenue_delta} -\end{figure} -\begin{figure}[ht] - \centering - \input{chapters/figures/results/includes/final_focus_risk_deltas.tex} - \caption{Defended-minus-baseline leakage and volatility deltas for the final cohort. Leakage remains lower for the defended policy across the full contamination range.} - \label{fig:final_focus_risk_deltas} -\end{figure} \subsection{Interpretation and Insights} The Mann-Whitney result ($p<0.001$) confirms that per-session divergence gaps distinguish the two actor classes with near-zero overlap in rank ordering. This is the condition required for distinguishability to act as a useful control signal in the pricing loop rather than just an auxiliary classifier score. From 392f9b15495aaa7a3b41652c1fdffbf035f76787 Mon Sep 17 00:00:00 2001 From: Daniel Rosel Date: Wed, 8 Apr 2026 19:21:49 +0200 Subject: [PATCH 14/44] adding docs --- .gitignore | 3 + Makefile | 15 +- README.md | 10 +- SETUP.md | 298 +++++++++++++++++++++++++++++++ docs/index.html | 8 + docs/mkdocs.yml | 53 ++++++ docs/requirements.txt | 1 + docs/src/architecture.md | 30 ++++ docs/src/business.md | 21 +++ docs/src/configuration.md | 63 +++++++ docs/src/glossary.md | 17 ++ docs/src/index.md | 21 +++ docs/src/platform-setup.md | 5 + docs/src/roadmap.md | 26 +++ paper/src/chapters/mdp_agent.pdf | Bin 10932 -> 10931 bytes paper/src/chapters/mdp_human.pdf | Bin 11953 -> 11953 bytes paper/src/main.tex | 2 +- 17 files changed, 570 insertions(+), 3 deletions(-) create mode 100644 SETUP.md create mode 100644 docs/mkdocs.yml create mode 100644 docs/requirements.txt create mode 100644 docs/src/architecture.md create mode 100644 docs/src/business.md create mode 100644 docs/src/configuration.md create mode 100644 docs/src/glossary.md create mode 100644 docs/src/index.md create mode 100644 docs/src/platform-setup.md create mode 100644 docs/src/roadmap.md diff --git a/.gitignore b/.gitignore index 11ff6b1..1107134 100644 --- a/.gitignore +++ b/.gitignore @@ -34,6 +34,9 @@ dist/ **/*.parquet **/_build/ +# mkdocs output (run make docs.platform locally or rely on CI) +docs/documentation/ + # paper build artifacts paper/src/bib/auto paper/src/auto/* diff --git a/Makefile b/Makefile index 754751c..c10eb10 100644 --- a/Makefile +++ b/Makefile @@ -44,7 +44,7 @@ SWEEP_ENV_LOAD = set -a; [ -f "$(SWEEP_ENV_FILE)" ] && . "$(SWEEP_ENV_FILE)" || .PHONY: help help: - @echo "pdf.build pdf.watch pdf.clean pdf.genpop pdf.genpop.watch pdf.arxiv | test.backend test.e2e test.all | web.dev | install | train | benchmark | benchmark.simple | benchmark.agent | train.agent | train.bootstrap | stats.lines | manim.defense manim.defense.hq manim.render manim.render.full manim.render.poster manim.render.appendix manim.render.all" + @echo "pdf.build pdf.watch pdf.clean pdf.genpop pdf.genpop.watch pdf.arxiv | test.backend test.e2e test.all | web.dev | install | train | benchmark | benchmark.simple | benchmark.agent | train.agent | train.bootstrap | stats.lines | docs.platform | manim.defense manim.defense.hq manim.render manim.render.full manim.render.poster manim.render.appendix manim.render.all" @echo "backend.server backend.provider backend.worker | platform.up platform.down platform.logs | docker.train.publish" @echo "data.pull data.push data.whoclicked.publish | study.margin-erosion study.margin-erosion.quick study.margin-erosion.plot" @echo "tpu.ray.bootstrap tpu.ray.deps tpu.ray.verify tpu.ray.teardown" @@ -186,6 +186,19 @@ study.margin-erosion: study.margin-erosion.quick: python -m engine.studies.margin_erosion_alpha --quick +DOCS_VENV ?= docs/.venv +DOCS_MKDOCS := $(DOCS_VENV)/bin/mkdocs +DOCS_PIP := $(DOCS_VENV)/bin/pip + +.PHONY: docs.platform +docs.platform: $(DOCS_VENV) + $(DOCS_MKDOCS) build -f docs/mkdocs.yml + +$(DOCS_VENV): + python3 -m venv $(DOCS_VENV) + $(DOCS_PIP) install --upgrade pip + $(DOCS_PIP) install -r docs/requirements.txt + .PHONY: wordcount wordcount: @$(NX) run paper:wordcount diff --git a/README.md b/README.md index a21d899..cc4a8aa 100644 --- a/README.md +++ b/README.md @@ -142,7 +142,10 @@ flowchart LR | `experiments/` | Data processing, ETL ideas, and analysis assets | | `docker/` | Dockerfiles for platform services | | `tests/e2e/` | Playwright end-to-end tests | -| `docs/` | Academic project page source | +| `docs/` | Academic project page (GitHub Pages root) + MkDocs config | +| `docs/src/` | Markdown sources for the operator documentation site | +| `docs/documentation/` | MkDocs build output (gitignored; run `make docs.platform`; served at `/documentation/` on Pages) | +| `SETUP.md` | Unified operator guide: stack, kernels, RL training, thesis refs by chapter | ## Operational notes @@ -151,6 +154,11 @@ flowchart LR - Research commands (`make train`, `make benchmark*`, `make train.agent`) auto-load `.env.sweep`. - Paper builds call `paper/concat_code.sh` before compilation to flatten code into the appendix. +## Operator documentation + +- Full setup guide (platform + research): [`SETUP.md`](SETUP.md) +- Hosted operator docs (after `make docs.platform`): […/PHANTOM/documentation/](https://velocitatem.github.io/PHANTOM/documentation/) on GitHub Pages + ## Research artifacts - Thesis PDF: `thesis-latest.pdf` or [hosted PDF](https://pub-d5b94a3c29fd40c6b3881946e463fdb7.r2.dev/thesis-latest.pdf) diff --git a/SETUP.md b/SETUP.md new file mode 100644 index 0000000..d2c37e0 --- /dev/null +++ b/SETUP.md @@ -0,0 +1,298 @@ +# PHANTOM: setup for operators and partners + +This guide walks a team from **business context** (what you sell, how you price, what traffic you worry about) through a **running PHANTOM stack**, **behavioral kernels and contamination**, and **RL training / benchmarking**. The math lives in the thesis PDF; here we tie operations to that math without re-deriving it. References to the thesis use **chapter numbers** only (build the PDF locally if you need line-level citations). + +**Thesis (PDF):** [thesis-latest.pdf](https://pub-d5b94a3c29fd40c6b3881946e463fdb7.r2.dev/thesis-latest.pdf) + +--- + +## 1. Who this is for / prerequisites + +**Audience:** Engineers and researchers who run Docker, a Next.js app, and Python tooling; product or risk stakeholders who define experiment goals and acceptable UX tradeoffs. + +**Skills:** Docker Compose, Node/npm, Python 3.8+, basic Kafka/Redis mental model. + +**Decide up front:** + +- **Vertical vs demo:** The repo ships `hotel` and `airline` storefront modes (`STORE_MODE`). Anything beyond that is custom integration work. +- **Data residency:** Event streams and training artifacts default to paths under the repo (overridable via `PHANTOM_`* env vars in `lib/config.py`). Decide where logs and models may live before you point production-like traffic at the stack. +- **Experiment governance:** Who may run human vs agent sessions, how sessions are labeled or weak-labeled for research, and retention policy for interaction logs. + +### Theoretical implications + +The formal model assumes each session is generated by a latent **actor class** $Y \in H,A$ (human vs agent). Your deployment choices implicitly assert **which sessions are valid for estimating human vs agent behavior** and whether experimental conditions are stable. If you mix exploratory QA traffic with labeled experiments without recording that fact, you blur the empirical partitions $D_H$ and $D_A$ that the methodology needs for transition kernels and contamination studies. See the **Introduction** (research questions) and **Methodology**, Problem Formalization, in the thesis PDF. + +--- + +## 2. Business fit framing + +**What PHANTOM is for:** Studying how **automated browsing and transaction orchestration** interact with **session-based pricing**: behavior generates a demand proxy $\hat{q}$; pricing policies map interaction history to prices; **Cost of Information (COI)** is the premium the platform can sustain above a floor when information is scarce. Agent-mediated **reconnaissance in one session** and **purchase in another** undermines that asymmetry; the thesis proves a **COI erosion** mechanism under many independent price queries. + +**What you must supply:** + +- A **product catalog** path: defaults assume Supabase-backed product data (`NEXT_PUBLIC_SUPABASE_URL`, `NEXT_PUBLIC_SUPABASE_ANON_KEY`). +- A plan for **interaction and price events** reaching the ingestion path (backend → Kafka) or an adapter you maintain. +- Clear **experiment goals:** e.g. compare human vs agent KPIs under the same task, measure margin under varying contamination $\alpha$. + +### Theoretical implications + +Aggregate demand in the thesis is a **mixture** over human and agent types with contamination $\alpha$ plus noise $\epsilon_t$; see the mixture demand discussion in **Chapter 3 (Methodology)**. COI is defined as $\mathbb{E}[P]-\underline{p}$; the **COI framework** and theorem in the same chapter explain why saturated agent querying collapses extractable premium. Your business scenario determines which **actions** enter $\hat{q}$ and how interpretable $\alpha$ is for your traffic. + +--- + +## 3. Environment and secrets + +**Bootstrap files (from repo root):** + +```bash +npm install +cp .env.example .env +cp .env.sweep.example .env.sweep +``` + +**Core `.env` (platform + web + docker):** See `[.env.example](.env.example)`. You must also set the variables called out in `[README.md](README.md)` for a full stack: `NEXT_PUBLIC_SUPABASE_URL`, `NEXT_PUBLIC_SUPABASE_ANON_KEY`, `AIRFLOW_FERNET_KEY`, `AIRFLOW_SECRET_KEY` (and provider ports per your compose file). + +**Training / sweeps (`.env.sweep`):** Used by `make train`, `make benchmark`, sweep agents. Typically `WANDB_API_KEY`, optional `WANDB_ENTITY` / `WANDB_PROJECT`, `GITHUB_TOKEN` for bootstrap flows, `SWEEP_ID` for W&B sweep workers. See `[.env.sweep.example](.env.sweep.example)`. + +**Security:** Never commit real `.env` or `.env.sweep` files. Rotate keys if they leak. + +### Theoretical implications + +Splitting **online platform credentials** (ingestion, catalog, Kafka) from **offline training credentials** (W&B, cloud TPUs, GitHub tokens for workers) mirrors the **hybrid Kappa–Lambda** data loop in the thesis: streaming observation vs batch / long-running training jobs. That split is named in the **Terminology** appendix of the thesis PDF. + +--- + +## 4. Bring-up (commands) + +Aligned with `[README.md](README.md)`: + +```bash +npm install +cp .env.example .env +cp .env.sweep.example .env.sweep +# edit .env: Supabase, Airflow keys, etc. + +make platform.up +make web.dev +``` + +**Sanity checks:** + + +| Endpoint | Role | +| ------------------------------------------------------------- | --------------------------------- | +| `http://localhost:3000` | Next.js storefront | +| `http://localhost:5000/health` | Backend ingest API | +| `http://localhost:5001/health` | Pricing provider | +| `http://localhost:8085` | Airflow UI (default compose port) | +| `http://localhost:8084` or configured `REDPANDA_CONSOLE_PORT` | Kafka console (see your `.env`) | + + +**Optional tests:** `make test.backend` (with venv/tooling as in Makefile); `make test.e2e` requires backend, web, and Airflow up per README. + +### Theoretical implications + +A correctly wired stack logs **trajectories** $\tau_s$ (sequences of events) and **price exposure** together. **Chapter 3** defines events $e_{s,k}=(a,i,t)$ and proxies $\hat{q}$ from weighted actions—without joint logging of behavior and quotes, you cannot recover the objects the theory reasons about (Problem Formalization). + +--- + +## 5. Service map + +```mermaid +flowchart LR + U[Human / Agent Browser] --> W[Next.js Web App] + W -->|Price requests| P[Pricing Provider] + W -->|Interaction events| B[Backend Ingest API] + B --> K[Kafka] + K --> A[Airflow + Worker Jobs] + A --> R[Redis Model Registry] + P -->|Session/global prices| W + E[Research Engine + Experiments] --> A + E --> R +``` + + + +**Ports (typical; confirm in `docker-compose` and `.env`):** `BACKEND_PORT` (5000), `PROVIDER_PORT` (5001), `KAFKA_PORT`, `REDIS_PORT`, Airflow `AIRFLOW_WEBSERVER_PORT` (8085 default), Redpanda console. + +### Theoretical implications + +The platform **observes** behavioral proxies and quoted prices, not the latent demand curve $d(p\mid\theta)$. The distinction between $\hat{q}$ and true demand is explicit in **Chapter 3**. Misattributing proxy noise to “true” elasticity breaks both estimation and any causal story about COI. + +--- + +## 6. Tailoring to your business + +**Storefront mode:** `STORE_MODE=hotel` or `airline` (see `[web/src/lib/config.ts](web/src/lib/config.ts)` and env). This switches catalog and UI, not the core ingestion pattern. + +**API base / environment:** `NEXT_PUBLIC_API_BASE`, `NEXT_PUBLIC_APP_ENV` (validated in `config.ts`). + +**Paths for data and runs:** Override with `PHANTOM_DATA_DIR`, `PHANTOM_SIM_RUNS_DIR`, `PHANTOM_MODEL_REGISTRY_DIR`, `PHANTOM_COLLECTED_DATA_DIR`, etc. (`[lib/config.py](lib/config.py)`). + +**Honest scope:** A new vertical (custom product ontology, checkout rules, pricing rules) means **new UI, events, and possibly new reward features** in the engine. Budget engineering time; the repo is a research platform, not a turnkey SaaS skin for arbitrary catalogs without code changes. + +### Theoretical implications + +Transition kernels $\hat{\mathcal{T}}_H,\hat{\mathcal{T}}_A$ are estimated on a **finite action / state space** derived from your instrumentation. Changing catalog depth or event taxonomy changes the MDP state space; old kernel estimates are not portable. See the transition kernel discussion in **Chapter 3**. + +--- + +## 7. Data collection and experiments + +**Flow:** Browser → backend → **Kafka** → downstream consumers (Airflow DAGs, notebooks, ETL under `experiments/`). Ensure **session identity**, **item identifiers**, and **action types** are consistent enough to build trajectories. + +**Weak labels:** The thesis discusses partitioning data into human vs agent subsets for MLE transition counts. In production you may only have heuristic labels—document bias explicitly. + +### Theoretical implications + +Distinguishability (sub-question SQ1 in the **Introduction**) asks whether $H$ vs $A$ is identifiable from behavior alone. Your labeling and experimental design determine whether $\Delta_H,\Delta_A$ and $f(\tau)$ are meaningful or dominated by noise. Symbols appear in the **Terminology** appendix ($\Delta_H,\Delta_A$, $f(\tau)$, contamination generator $\mathcal{G}(\alpha)$). + +--- + +## 8. Transition kernels and agent scoring (theory → practice) + +**Theory:** Sessions yield trajectories $\tau_s$. For each actor class $y\inH,A$, the thesis estimates a **Markov transition kernel** by counting transitions and normalizing (MLE): + +$$ +\hat{P}(s' \mid s) = \frac{N(s,s')}{\sum_k N(s,k)} +$$ + +Human and agent prototypes $\hat{\mathcal{T}}_H,\hat{\mathcal{T}}_A$ support comparing an empirical kernel from a partial trajectory to prototypes (e.g. KL-style divergences $\Delta_H,\Delta_A$) and mapping to a **weak agent probability** $f(\tau)$. See **Chapter 3** and the **Terminology** appendix. + +**Code:** `[engine/lib/coi.py](engine/lib/coi.py)` (`compute_agent_probability`: empirical transition counts vs human/agent reference dicts, KL-style terms, mapped via `[lib/agent_probability.py](lib/agent_probability.py)`). + +**Optional narrative:** `[blog/02-behavioral-fingerprinting.md](blog/02-behavioral-fingerprinting.md)` walks a concrete study design (not required for operators). + +### Theoretical implications + +If reference kernels are fit on **stale** or **mislabeled** partitions, $\Delta_H-\Delta_A$ is not interpretable as distinguishability. Ground claims in SQ1 (**Introduction**) and the kernel subsection of **Chapter 3**. + +--- + +## 9. Contamination generator $\mathcal{G}(\alpha)$ + +**Theory:** Given clean trajectories, $\mathcal{G}(\alpha)$ injects synthetic agent trajectories until the effective mixture reaches contamination $\alpha\in[0,1]$, defining training scenarios for robust policies (**Chapter 3**). Catalog-scale block expansion of kernels is discussed there with validation caveats—treat large product spaces as **research-grade** until your team signs off. + +**Code:** `[engine/engine.py](engine/engine.py)` — `MarketEngine` mixes human/agent demand, uses `get_adjusted_transitions` / `sample_behavior_from_transitions`, and `alpha` when combining actor types and building demand proxies (`estimate_demand`). This is the **simulator** path, not a drop-in replacement for your production database. + +### Theoretical implications + +$\alpha$ in mixture $Q(p)$ is **agentic demand contribution** in the formal model, not necessarily “bot share of page views” unless your instrumentation equates them. Mismeasuring $\alpha$ biases robust objectives tied to a fixed contamination level. + +--- + +## 10. Training and evaluation — local workflow + +**Environment:** Python venv via Nx (`make install` / `nx run research:install`). Training commands load `.env.sweep`. + +```bash +make train LOCAL_TRAIN_ARGS='--algo ppo --total-timesteps 50000' +make benchmark LOCAL_BENCHMARK_ARGS='--tiers static,surge,linear,qtable,ppo --alpha-values 0.0,0.3 --episodes 3 --no-wandb' +make benchmark.simple +``` + +Entrypoints: `[engine/train.py](engine/train.py)`, `[engine/benchmark.py](engine/benchmark.py)`, `[engine/spec.py](engine/spec.py)` (Nx wraps these—see `project.json` / research targets). + +**Artifacts:** `[lib/config.py](lib/config.py)` — `PHANTOM_SIM_RUNS_DIR` (default `sim/rl/runs`), `PHANTOM_MODEL_REGISTRY_DIR`, etc. + +**TensorBoard (optional):** `[docker-compose.yml](docker-compose.yml)` includes `tensorboard-rl` on host port **6007** (`./sim/rl/runs`) and `tensorboard-ml` on **6006** (`./experiments/ml/runs`). + +### Theoretical implications + +Local runs instantiate the **offline defense gym**: policies trained on simulator-induced distributions approximate the DR-RL narrative in **Chapter 3**, but hyperparameters ($\lambda$ on COI leakage, $\eta$ on UX, robust radius) change the effective ambiguity set. Cross-check `engine/` against the thesis before claiming figure-for-figure replication. + +--- + +## 11. Training and evaluation — remote / scaled deployment + +For **research at scale** (cloud quota and secrets required): + + +| Mechanism | Role | +| ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- | +| `[submit_ray_job.sh](submit_ray_job.sh)` | Ray jobs with `.env` injected; `RAY_MODE=single|distributed|benchmark|sweep`. Set the script’s `ROOT` to your clone path. | +| `make tpu.ray.bootstrap` / `tpu.ray.`* | TPU Ray bootstrap (`TPU_CONF`, e.g. `tpu_orchestration/configs/v4_spot_us.conf`). | +| `make train.agent` / `make benchmark.agent` | W&B sweeps: `SWEEP_ID` in `.env.sweep`. | +| `make train.bootstrap` | Worker bootstrap: `REPO_URL`, `SWEEP_ID`, `GITHUB_TOKEN`. | +| `make docker.train.publish` | Trainer image (`TRAIN_IMAGE_REF` in Makefile). | + + +See `submit_ray_job.sh` for env vars (`WANDB_*`, `PHANTOM_*` TPU toggles). + +### Theoretical implications + +Distributed training does not change the **definitions** of the Stackelberg game or Wasserstein ambiguity; it changes compute and variance of empirical estimates. Align random seeds and data protocol across nodes or split results explicitly—otherwise you mix distributions in a way a single empirical law $\hat{P}_N$ in the thesis does not describe. + +--- + +## 12. Evaluation, artifacts, and audit trail + +**Benchmarks:** `make benchmark`* sweeps tiers and $\alpha$; CLI includes robustness knobs (see default `BENCHMARK_ARGS` in `submit_ray_job.sh`: `--robust-radius`, `--lambda-coi`, `--eta-ux`, etc.). + +**Audit trail:** Store `git` SHA, CLI argv, non-secret `.env.sweep` keys, and W&B run IDs with published tables. For scientific claims, cite **Chapters 4–5 (Results, Discussion)** in the thesis PDF. + +### Theoretical implications + +Evaluation quality equals **simulator fidelity** plus **contamination modeling**. Separate theorem statements (assumption-based) from empirical curves (`engine`-dependent). + +--- + +## 13. Operational suggestions + +- **Staging:** Non-production namespaces; separate Kafka topics and Supabase projects where possible. +- **Rate limits / abuse:** Protect ingest endpoints; respect participant privacy. +- **Human vs agent sessions:** Comparable cohorts; record experimental condition in metadata. +- **Contracts:** `tests/e2e/` encodes minimal flows—use when APIs change. + +### Theoretical implications + +Non-stationary noise $\epsilon_t$ and drifting $\alpha$ confound benchmark interpretation. **Chapter 3** discusses mixture identification: isolate treatments when possible and document confounders when not. + +--- + +## 14. Roadmap / gaps (honesty) + +**Relatively turnkey:** Local dockerized stack, demo verticals, engine benchmarks, documented env and paths. + +**Typically custom:** Production catalog without Supabase, identity/fraud layers, legal review of logging, Kafka/Airflow SLAs, hardening the pricing provider for real money. + +**Thesis vs code:** The PDF is the **spec**; not every robustness term or large-catalog kernel construction is production-verified—see caveats in **Chapter 3**. + +### Theoretical implications + +Theorems in the thesis can be **stronger** than what observational firm logs support. The COI result assumes a clean experimental reading of the pricing policy; live market data may only support weaker claims. + +--- + +## 15. Theory and thesis cross-references (quick index) + +Use the **PDF table of contents** with these anchors: + + +| Topic | Thesis location | +| -------------------------------------------------------------------------- | ----------------------------------------------------- | +| Research questions (margin, distinguishability, contamination, mitigation) | **Introduction** | +| Sessions, events, $\hat{q}$, mixture $Q(p)$, $\alpha$ | **Chapter 3** — Problem Formalization, mixture demand | +| COI definition and erosion theorem | **Chapter 3** — COI framework | +| Transition kernels, MLE, $\mathcal{G}(\alpha)$ | **Chapter 3** | +| DR-RL, ambiguity sets, Stackelberg | **Chapter 3** | +| Symbol glossary (COI leakage, $f(\tau)$, UX, surrogates) | **Appendix — Terminology** | +| Empirical results and limitations | **Chapters 4–5** | + + +--- + +## 16. Quick file index (code) + + +| File | Role | +| ---------------------------------------------------------------------------------- | -------------------------------------------------- | +| `[engine/lib/coi.py](engine/lib/coi.py)` | KL-style trajectory comparison; agent probability. | +| `[engine/engine.py](engine/engine.py)` | `MarketEngine`, mixture, demand proxy path. | +| `[lib/agent_probability.py](lib/agent_probability.py)` | Divergence → probability score. | +| `[lib/config.py](lib/config.py)` | Paths and ports for artifacts. | +| `[engine/train.py](engine/train.py)`, `[engine/benchmark.py](engine/benchmark.py)` | CLI entrypoints. | +| `[tpu_orchestration/](tpu_orchestration/)` | TPU configs and helpers. | + + +You do **not** need a running storefront for many **offline** benchmarks if the research Python environment is installed; you **do** need aligned instrumentation to connect production trajectories to kernel estimation. \ No newline at end of file diff --git a/docs/index.html b/docs/index.html index 8eb6bd9..89062f6 100644 --- a/docs/index.html +++ b/docs/index.html @@ -183,6 +183,14 @@ + +

+ +
P4P Interaction Layer
diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml new file mode 100644 index 0000000..01f94e1 --- /dev/null +++ b/docs/mkdocs.yml @@ -0,0 +1,53 @@ +site_name: PHANTOM Platform +site_description: Operator and research documentation for the PHANTOM dynamic pricing research platform. +site_url: https://velocitatem.github.io/PHANTOM/documentation/ +site_author: Daniel Rösel + +repo_url: https://github.com/velocitatem/PHANTOM +repo_name: velocitatem/PHANTOM + +docs_dir: src +site_dir: documentation +strict: true + +theme: + name: material + palette: + - scheme: default + primary: indigo + toggle: + icon: material/brightness-7 + name: Switch to dark mode + - scheme: slate + primary: indigo + toggle: + icon: material/brightness-4 + name: Switch to light mode + features: + - navigation.instant + - navigation.tracking + - content.code.copy + - search.suggest + - search.highlight + +nav: + - Home: index.md + - Setup: platform-setup.md + - Business overview: business.md + - Architecture: architecture.md + - Configuration: configuration.md + - Glossary: glossary.md + - Roadmap & implementation notes: roadmap.md + +markdown_extensions: + - pymdownx.snippets: + base_path: + - .. + - pymdownx.superfences + - admonition + - tables + - toc: + permalink: true + +plugins: + - search diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 0000000..d14bca3 --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1 @@ +mkdocs-material>=9.5,<10 diff --git a/docs/src/architecture.md b/docs/src/architecture.md new file mode 100644 index 0000000..9da03b3 --- /dev/null +++ b/docs/src/architecture.md @@ -0,0 +1,30 @@ +# Architecture + +## System map + +```mermaid +flowchart LR + U[Human / Agent Browser] --> W[Next.js Web App] + W -->|Price requests| P[Pricing Provider] + W -->|Interaction events| B[Backend Ingest API] + B --> K[Kafka] + K --> A[Airflow + Worker Jobs] + A --> R[Redis Model Registry] + P -->|Session/global prices| W + E[Research Engine + Experiments] --> A + E --> R +``` + + + +## Event and training path (conceptual) + +1. **Online:** The browser emits events; the backend publishes to **Kafka**; schedulers and workers consume for ETL and model registry updates. +2. **Offline:** Notebooks and scripts under `experiments/` transform logs; `**engine/`** runs simulations, training, and benchmarks; artifacts land under paths from `[lib/config.py](https://github.com/velocitatem/PHANTOM/blob/main/lib/config.py)`. +3. **Feedback:** Trained or rule-based policies surface through the **pricing provider** to the web app. + +## Where to read more + +- Ports and health checks: [README](https://github.com/velocitatem/PHANTOM/blob/main/README.md) and [Configuration](configuration.md). +- Formal notation for sessions, $\hat{q}$, and mixture demand: **Chapter 3 (Methodology)** in the thesis PDF. + diff --git a/docs/src/business.md b/docs/src/business.md new file mode 100644 index 0000000..a6dc8bb --- /dev/null +++ b/docs/src/business.md @@ -0,0 +1,21 @@ +# Business overview + +PHANTOM targets **platform operators and researchers** who need to: + +1. **Observe** session-level behavior and price quotes together (trajectories and policies—not just clicks). +2. **Separate** human-driven demand signals from agent-mediated reconnaissance where possible (distinguishability and contamination \alpha in the thesis). +3. **Evaluate** pricing policies that remain useful when **Cost of Information (COI)** is under pressure from automated querying (formal COI framework and theorem in the thesis PDF). + +## What this product is not + +- A drop-in fraud API that returns “bot score” for every request without your event schema. +- A certified compliance guarantee for regulated pricing: it is a **research stack** with configurable experiments. +- A hosted SaaS: you run the stack (or adapt components) under your infrastructure policy. + +## Self-service story (ideal path) + +A team connects their **catalog** (today: Supabase-backed flows in this repo), streams **interaction events** through the ingest path, runs **labeled or weak-labeled** human vs agent sessions, estimates **behavioral kernels**, varies **contamination** in simulation, and **trains or benchmarks** robust policies via `engine/`. Steps and caveats are in [Setup](platform-setup.md) (same content as root `SETUP.md`). + +## Thesis link + +Problem statement, contributions, and research questions: **Introduction** and abstract in the [thesis PDF](https://pub-d5b94a3c29fd40c6b3881946e463fdb7.r2.dev/thesis-latest.pdf). \ No newline at end of file diff --git a/docs/src/configuration.md b/docs/src/configuration.md new file mode 100644 index 0000000..73d7438 --- /dev/null +++ b/docs/src/configuration.md @@ -0,0 +1,63 @@ +# Configuration reference + +This page condenses tables from `[README.md](https://github.com/velocitatem/PHANTOM/blob/main/README.md)` and points to code. Authoritative env templates: `[.env.example](https://github.com/velocitatem/PHANTOM/blob/main/.env.example)`, `[.env.sweep.example](https://github.com/velocitatem/PHANTOM/blob/main/.env.sweep.example)`. + +## Core runtime (`.env`) + + +| Variable | Purpose | Typical value | +| ------------------------------- | ------------------------------ | ----------------------- | +| `STORE_MODE` | Web mode (`hotel` / `airline`) | `hotel` | +| `BACKEND_PORT` | Backend API | `5000` | +| `PROVIDER_PORT` | Pricing provider | `5001` | +| `KAFKA_HOST` | Kafka broker host | `localhost` | +| `KAFKA_PORT` | Kafka port | `9092` | +| `REDIS_PORT` | Redis port | `6377` | +| `REDPANDA_CONSOLE_PORT` | Kafka UI | `8084` (see compose) | +| `NEXT_PUBLIC_SUPABASE_URL` | Catalog / data | required for full stack | +| `NEXT_PUBLIC_SUPABASE_ANON_KEY` | Catalog / data | required | +| `AIRFLOW_FERNET_KEY` | Airflow | required | +| `AIRFLOW_SECRET_KEY` | Airflow web | required | + + +Web client validation: `[web/src/lib/config.ts](https://github.com/velocitatem/PHANTOM/blob/main/web/src/lib/config.ts)`. + +## Training / sweeps (`.env.sweep`) + + +| Variable | Purpose | +| --------------- | ----------------------------------------------- | +| `WANDB_API_KEY` | Weights & Biases | +| `WANDB_ENTITY` | Optional override | +| `WANDB_PROJECT` | Project name (default `capstone`) | +| `GITHUB_TOKEN` | Bootstrap / workers | +| `SWEEP_ID` | Sweep agents (`train.agent`, `benchmark.agent`) | + + +## Path overrides (`PHANTOM_*`) + +Defined in `[lib/config.py](https://github.com/velocitatem/PHANTOM/blob/main/lib/config.py)`: + + +| Variable | Default (conceptual) | +| ---------------------------- | ----------------------------------- | +| `PHANTOM_DATA_DIR` | `data/` | +| `PHANTOM_EXPERIMENTS_DIR` | `experiments/` | +| `PHANTOM_SIM_RUNS_DIR` | `sim/rl/runs` | +| `PHANTOM_MODEL_REGISTRY_DIR` | `data/models` | +| `PHANTOM_COLLECTED_DATA_DIR` | `experiments/agents/collected_data` | + + +## Makefile entrypoints + + +| Goal | Command | +| ---------------- | ------------------------------------------- | +| Platform up/down | `make platform.up` / `make platform.down` | +| Web dev | `make web.dev` | +| Train | `make train` (+ `LOCAL_TRAIN_ARGS`) | +| Benchmark | `make benchmark` (+ `LOCAL_BENCHMARK_ARGS`) | +| Docs site | `make docs.platform` | + + +See `make help` for the full list. \ No newline at end of file diff --git a/docs/src/glossary.md b/docs/src/glossary.md new file mode 100644 index 0000000..5774101 --- /dev/null +++ b/docs/src/glossary.md @@ -0,0 +1,17 @@ +# Glossary + +Short definitions point to the thesis **Terminology** appendix in the [PDF](https://pub-d5b94a3c29fd40c6b3881946e463fdb7.r2.dev/thesis-latest.pdf) for full precision. + +| Term | Meaning (operational) | +| --- | --- | +| **COI (Cost of Information)** | Expected price premium above a floor under the platform’s policy; thesis KPI for pricing power. | +| **Trajectory \(\tau_s\)** | Ordered session events used as the behavioral record. | +| **Demand proxy \(\hat{q}\)** | Weighted aggregation of actions—what the platform observes instead of true demand. | +| **Contamination \(\alpha\)** | Agent share in the mixture demand model (thesis); not automatically “% of bots” in raw logs. | +| **Transition kernel \(\hat{\mathcal{T}}\)** | MLE Markov model over behavioral states / events for class \(H\) or \(A\). | +| **\(\Delta_H,\Delta_A\)** | Divergence scores vs human/agent prototypes (thesis notation). | +| **\(f(\tau)\)** | Weak agent probability from trajectory (implementation: `engine/lib/coi.py`). | +| **\(\mathcal{G}(\alpha)\)** | Contamination generator: synthetic agent trajectories to reach mixture level \(\alpha\). | +| **DR-RL** | Distributionally robust reinforcement learning training narrative in the thesis. | +| **Ambiguity set / Wasserstein** | Robust optimization neighborhood around an empirical demand law. | +| **Kappa–Lambda architecture** | Thesis term for streaming (online) vs batch/offline learning loops. | diff --git a/docs/src/index.md b/docs/src/index.md new file mode 100644 index 0000000..caa59e9 --- /dev/null +++ b/docs/src/index.md @@ -0,0 +1,21 @@ +# PHANTOM platform documentation + +Welcome. This site mirrors the **operator and research** documentation for the PHANTOM repository: a research platform for studying **dynamic pricing** under **LLM-mediated browsing and transaction orchestration**, with ties to the academic thesis. + +## Start here + +| Document | Audience | +| --- | --- | +| [Setup](platform-setup.md) | Full walkthrough: Docker/web/ingest, kernels, contamination, RL training, and audit—content from `SETUP.md` in the repo. | +| [Configuration reference](configuration.md) | Env vars, paths, and Makefile entrypoints in one place. | +| [Roadmap & implementation notes](roadmap.md) | What is turnkey vs research-grade; thesis vs code. | + +## Canonical sources in the repo + +- Thesis PDF: [thesis-latest.pdf](https://pub-d5b94a3c29fd40c6b3881946e463fdb7.r2.dev/thesis-latest.pdf) +- Root onboarding: single file [`SETUP.md`](https://github.com/velocitatem/PHANTOM/blob/main/SETUP.md) (included on this site via snippets—edit that file to change content). +- Quick start and command tables: [`README.md`](https://github.com/velocitatem/PHANTOM/blob/main/README.md) + +## Academic project page + +The research landing page (figures, abstract, links) is the site root on GitHub Pages: [velocitatem.github.io/PHANTOM/](https://velocitatem.github.io/PHANTOM/). Open **Documentation** in the Project Links menu there to return to this subsite. diff --git a/docs/src/platform-setup.md b/docs/src/platform-setup.md new file mode 100644 index 0000000..682f010 --- /dev/null +++ b/docs/src/platform-setup.md @@ -0,0 +1,5 @@ +# Setup + +The content below is included from the repository root file `SETUP.md` (single source of truth: platform bring-up, kernels, contamination, RL training, and thesis pointers by chapter). + +--8<-- "SETUP.md" diff --git a/docs/src/roadmap.md b/docs/src/roadmap.md new file mode 100644 index 0000000..d16f496 --- /dev/null +++ b/docs/src/roadmap.md @@ -0,0 +1,26 @@ +# Roadmap & implementation notes + +This page is the **honesty pass** from the documentation plan: what clients can expect today versus what remains research-heavy. + +## Turnkey in this repository + +- **Local stack:** Docker Compose services for backend, Kafka, Redis, Airflow, pricing provider, etc.; Next.js via `make web.dev` (see [Platform setup](platform-setup.md)). +- **Demo verticals:** `hotel` and `airline` storefront modes. +- **Engine:** Benchmarks and training entrypoints (`make train`, `make benchmark`), KL-based agent scoring in `[engine/lib/coi.py](https://github.com/velocitatem/PHANTOM/blob/main/engine/lib/coi.py)`, simulator mixing in `[engine/engine.py](https://github.com/velocitatem/PHANTOM/blob/main/engine/engine.py)`. +- **Orchestration hooks:** Ray/TPU scripts (`submit_ray_job.sh`, `make tpu.ray.`*), W&B sweep agents, Docker trainer publish target. + +## Usually requires custom engineering + +- **Non-Supabase catalog** or checkout flows without adapting the web + backend contracts. +- **Production SLAs** on Kafka, schema registry, or PII boundaries for your jurisdiction. +- **Tight coupling** to a legacy pricing engine without mapping its API to the provider abstraction. + +## Thesis vs code + +- The **thesis** states theorems and constructions (COI erosion, kernels, \mathcal{G}(\alpha), DR-RL). +- The **codebase** implements a **subset** of that story for experiments: verify CLI flags and simulator assumptions before claiming 1:1 equivalence with every equation. +- **Catalog-scale kernel expansion** is discussed in **Chapter 3** with explicit validation caveats—do not assume row-stochasticity and Markov structure are automatically preserved at full product cardinality without review. + +## Suggested client messaging + +Position PHANTOM as a **reproducible research and evaluation stack** for agent-aware pricing, with a path to custom integration—not as a black-box “turn on anti-agent pricing” product without data and engineering investment. \ No newline at end of file diff --git a/paper/src/chapters/mdp_agent.pdf b/paper/src/chapters/mdp_agent.pdf index 17d299e68d5e4207c1cb1833a0049fb74da537c1..b0911f18c069e6ea57ee7992c27bf88ad3495015 100644 GIT binary patch delta 322 zcmV-I0logTRkKyFeJFoXYlAQph2Q-Z=VeS8Xkx9kMfy;MGR8n`-^Lz73|1IMk~r3X zzeKBJP(8W^4dObKdeqZn_j2gw#6?Uo=-yIkA`Q)HY?RYJ;92FND zo7MATh1|^HwY?R?zXc0s*q3Y{``H^j*0V#u_8)IVlaVPO0yjC6vMJ6CML;ploeT^B U<*f-ilZ+}L2r&vJB}Gq03ayiqdjJ3c delta 323 zcmV-J0lfaRRkT&GeJFolYlAQp#ozrD=VeS8Xs)&P57LLWP{tTY+qbcY5Q7zlktB}w z+b_}T7p@yfu}<2U0Mc@j0M_ z>RSs6cDq4QYE|_@YJ`F}T7hC(#~H$#wVl}}$v|t(yIUhntaN`JMdLSGa@(l($gM

teS;p2x%N3&+mOe8UO(uV_@Z_w$zT{f^96s3q-euMBz4~!+nS1Fv+kkS~%&}YR z1P0lxIBC^)C<;ILd2|`*gf5P1-kQ$4iTi|KVRbL3>EO1G^1Bi8eA;{HQhR=uM)g`q zgT?H5aYA9o@WvM1;a|Z6xBN@hkACh3k9uzD7yFHGNt2K%9|AZqld&nz48=e(&0P!( V0Ohy|Jd=wmAP6!FB_%~qMhZNeon`<4 diff --git a/paper/src/chapters/mdp_human.pdf b/paper/src/chapters/mdp_human.pdf index af63cd53d332f9ca87871d6a0d72294ce431bc4a..cced37da2848d24b54e43f309be7bb7eee4c105e 100644 GIT binary patch delta 291 zcmV+;0o?wvU9nxTdn|v!YQr!PMDPBJxs=8fY)Nr!H<%ofKq#dUlHN)WLKcppSVodd z^Y>lZaZ1r;_pzgyWjR>HbF5e-z*jut1(rn~RTa|rYp;ogr{V`YD7l}mq&ZQ^+P#%k$X)9t0# zSnSt=iJWDuXA&wug?AQohkpz1q?cc&x$Q?cxUEM+zxo$%NwbeFA_1cki~s-t delta 291 zcmV+;0o?wvU9nxTdn|v=YQr!PgztWexs*B-Y)SE7gUKNdgi;D7>83a~8A*Vw&v z0)u2#oU|GTl%*g1JiAPEK^I3g@6F)d#BISZuzHlsRJpCA{O*K2pAH_n)SjQEQKJ^p pV7q!=oKV;~ym1zH_*byvmVc@C-p}3OUe7K40{?|?PP30KA^{6VkLv&c diff --git a/paper/src/main.tex b/paper/src/main.tex index 2046342..c3422cc 100644 --- a/paper/src/main.tex +++ b/paper/src/main.tex @@ -18,7 +18,7 @@ \end{titlepage} \begin{abstract} -With accelerated growth of Lager Language Model agents in e-commerce a novel adversarial dynamic to digital markets emerges. This paper address the vulnerability of dynamic pricing systems to AI intermediaries that decouple the information gather stages from the transaction execution. By conducing reconnaissance isolates sessions, agents circumvent the ``Cost of Information'' (COI) defined as the accumulated price premium typically thought demand expression estimators. +With accelerated growth of Large Language Model agents in e-commerce a novel adversarial dynamic to digital markets emerges. This paper address the vulnerability of dynamic pricing systems to AI intermediaries that decouple the information gather stages from the transaction execution. By conducing reconnaissance isolates sessions, agents circumvent the ``Cost of Information'' (COI) defined as the accumulated price premium typically thought demand expression estimators. We formally define this phenomenon and derive the Cost of Information Theorem, proving that as the saturation of independent, utility-maximizing agents increases, the platform’s ability to sustain a COI converges to zero, rendering standard dynamic pricing mechanisms incentive-incompatible. To respond to this threat we propose a defensive framework which integrates behavioral economics with Adversarially Distributionally Robust Optimization (DRO). We introduce a custom e-commerce research platform built on hybrid Kappa-Lambda architecture, designed to capture and simulate high-fidelity controlled interaction trajectories. We further demonstrate through modeling that human and agent behaviors exhibit distinct transition probability kernels, enabling the construction of discriminative models based on Kullback-Leibler divergence. These behavioral signals serve as inputs for a Distributionally Robust Reinforcement Learning (DR-RL) agent. We formulate the pricing problem as a Stackelberg game where the learner optimizes against an ambiguity set of demand distributions defined by the Wasserstein distance. This approach allows the pricing policy to remain robust against non-stationary contamination without overfitting to deterministic demand curves. The research validates a mechanism for preserving margin integrity and market equilibrium in an agent-mediated economy, while minimizing degradation to the legitimate human user experience (UX). From cc823ec63cedd876fece00337b0283dfd0151190 Mon Sep 17 00:00:00 2001 From: Daniel Rosel Date: Wed, 8 Apr 2026 20:27:27 +0200 Subject: [PATCH 15/44] chore: adding summary --- Makefile | 10 +++- paper/project.json | 17 ++++++ paper/src/summary.tex | 118 ++++++++++++++++++++++++++++++++++++++++++ scripts/nx_paper.sh | 10 ++++ 4 files changed, 154 insertions(+), 1 deletion(-) create mode 100644 paper/src/summary.tex diff --git a/Makefile b/Makefile index c10eb10..315651e 100644 --- a/Makefile +++ b/Makefile @@ -44,7 +44,7 @@ SWEEP_ENV_LOAD = set -a; [ -f "$(SWEEP_ENV_FILE)" ] && . "$(SWEEP_ENV_FILE)" || .PHONY: help help: - @echo "pdf.build pdf.watch pdf.clean pdf.genpop pdf.genpop.watch pdf.arxiv | test.backend test.e2e test.all | web.dev | install | train | benchmark | benchmark.simple | benchmark.agent | train.agent | train.bootstrap | stats.lines | docs.platform | manim.defense manim.defense.hq manim.render manim.render.full manim.render.poster manim.render.appendix manim.render.all" + @echo "pdf.build pdf.watch pdf.clean pdf.genpop pdf.genpop.watch pdf.summary pdf.summary.watch pdf.arxiv | test.backend test.e2e test.all | web.dev | install | train | benchmark | benchmark.simple | benchmark.agent | train.agent | train.bootstrap | stats.lines | docs.platform | manim.defense manim.defense.hq manim.render manim.render.full manim.render.poster manim.render.appendix manim.render.all" @echo "backend.server backend.provider backend.worker | platform.up platform.down platform.logs | docker.train.publish" @echo "data.pull data.push data.whoclicked.publish | study.margin-erosion study.margin-erosion.quick study.margin-erosion.plot" @echo "tpu.ray.bootstrap tpu.ray.deps tpu.ray.verify tpu.ray.teardown" @@ -102,6 +102,14 @@ pdf.genpop.watch: pdf.arxiv: @bash scripts/nx_paper.sh build-arxiv +.PHONY: pdf.summary +pdf.summary: + @bash scripts/nx_paper.sh build-summary + +.PHONY: pdf.summary.watch +pdf.summary.watch: + @bash scripts/nx_paper.sh watch-summary + .PHONY: test.backend test.backend: @$(NX) run research:test diff --git a/paper/project.json b/paper/project.json index 503c67a..6b922f8 100644 --- a/paper/project.json +++ b/paper/project.json @@ -44,6 +44,23 @@ "command": "bash scripts/nx_paper.sh build-arxiv", "cwd": "." } + }, + "build-summary": { + "executor": "nx:run-commands", + "outputs": [ + "{projectRoot}/build/summary.pdf" + ], + "options": { + "command": "bash scripts/nx_paper.sh build-summary", + "cwd": "." + } + }, + "watch-summary": { + "executor": "nx:run-commands", + "options": { + "command": "bash scripts/nx_paper.sh watch-summary", + "cwd": "." + } } }, "tags": [ diff --git a/paper/src/summary.tex b/paper/src/summary.tex new file mode 100644 index 0000000..749a7fb --- /dev/null +++ b/paper/src/summary.tex @@ -0,0 +1,118 @@ +% -*- TeX-master: t -*- +% Two-page summary: one self-contained source file (no \input chapters). +% Text is stitched from the thesis chapters using the author’s wording; trim if the PDF exceeds two pages. +\documentclass[10pt,letterpaper]{article} + +\input{preamble} + +\begin{document} +\singlespacing +\setlength{\parskip}{0.25em} +\small +\fancyhead[L]{PHANTOM Summary} + +\begin{center} +{\large\bfseries PHANTOM: Pricing Heuristics Against Non-human Transaction Orchestration Mechanisms}\\[0.3em] +{\normalsize Daniel Rösel}\\[0.12em] +{\small Bachelor of Computer Science \& Artificial Intelligence, IE University, Madrid}\\[0.12em] +{\small Supervised by Alberto Martín Izquierdo \quad\textbar\quad \today} +\end{center} + +\vspace{0.35em} + +\noindent +To better understand all wedges of the current works, we must start by exploring the nature of agents, agentic computer use and web automation, complementing that with economic reasoning and strategic interaction. +The final surface to cover leads us to data-driven dynamic pricing under uncertainty. +The key technical risk is not ``agents buying things'' per se, but agents shaping the behavioral and demand signals that downstream pricing systems consume and depend on \parencite{xia_evaluation-driven_2025}. +Dynamic pricing assumes demand proxies are behaviorally meaningful, while bot detection aims at security and access control. +The missing bridge is a principled framework for distinguishing non-human reconnaissance from genuine human demand expression and integrating that distinguishability into pricing heuristics without degrading legitimate user experience (in our research tracked by the user-experience index). +This economic framing also helps separate two related but distinct phenomena of agents as buyers (changing market demand composition), and agents as information gatherers (changing the observed interactions used by pricing/recommendation systems). +The thesis focuses on the second, where information acquisition strategically precedes purchase execution. +Our effort to combat contamination stems from research by \textcite{hardt_strategic_2015} on strategic classification, in conjunction with \textcite{liu_contextual_2024} who demonstrate a linear regret if contamination is ignored. +To bridge the gap between detection and robust pricing, we look at work in Distributionally Robust Optimization (DRO): by optimizing for the worst-case distribution within this set, pricing mechanisms can become resilient to the distributional shifts such as the ones caused by non-human actors \parencite{kuhn_wasserstein_2024}. +In order to create an environment in which prices can be tested against a demand estimate generated by some behavioral model, we take inspiration from the architecture proposed by \textcite{ie_recsim_2019} in the RecSim platform built for recommendation systems. +The key component of this mediation between agents and commercial platforms lays in the transaction costs related to information gathering and negotiation. +As proposed by \textcite{shahidi_coasean_2025} these costs are bound to collapse towards zero (which we demonstrate mathematically), calling for a re-evaluation of the boundaries between firms and markets. + +\vspace{0.3em} +\noindent +In this paper we present an exploration and defense against the presence of new commercial entities in digitally powered platforms, preserving market equilibrium in the age of AI. +We formally define interaction data as coming from some actor which can either be an agent ($A$) or human ($H$). +Dynamic pricing algorithms rely on directly translating demand features $q$ to new price assignments $\hat{p}$ across a catalogue of products of size $N$. +This opens opportunities to design a \textit{tabula rasa} of digital market mechanisms that will shape the future of commerce in the age of artificial intelligence. +We propose a robust optimization objective defined in our methodology, transforming the pricing problem into a form of Distributionally Robust Optimization \parencite{kuhn_distributionally_2025} where the learner must guard against adversarial contamination in observed demand distributors. +For purposes of this research, an agent is an algorithmic loop with the ability to access a web platform and perform actions such as clicks, scrolls, and input field fills. + +\vspace{0.3em} +\noindent +The platform does not directly observe the true underlying demand function $d(p)$ where $d \in \mathbb{R}^{+}$ and our proxy $\hat{q} \in \mathbb{R}^{+}$. +Instead, it observes a behavioral proxy $\hat{q}_t$, which is a composite signal derived from the mixture of actor types. +The total observed demand is a stochastic process governed by the naively defined mixture $Q(p) = (1-\alpha) \cdot \mathbb{E}_{\theta \sim \mathcal{D}_H}[d(p\mid Y=H,\theta)] + \alpha \cdot \mathbb{E}_{\theta \sim \mathcal{D}_A}[d(p\mid Y=A,\theta)] + \epsilon_t$ where $\alpha \in [0, 1]$ represents the contamination parameter (proportion of agents) and $\epsilon_t$ is non-stationary market noise. +The platform's pricing power comes from information asymmetry: users who express strong interest signals pay more than the base price. +We quantify this markup as the \textit{Cost of Information} (COI), which represents the average premium extracted above marginal cost. +We formally demonstrate that standard dynamic pricing mechanisms are not incentive-compatible with high-frequency agentic traffic. +As the number of independent competitive agents $N$ querying the system grows, the platform's ability to sustain a COI vanishes. + +\vspace{0.3em} +\noindent +In order for our research to have grounding in interactions we built a robust e-commerce web-platform. +The architecture of this platform begins with the deployed web-apps posting interaction data to our backend which processes them and stores each ingested interaction into a kafka cluster. +This serves as our data reservoir tracking and associating each interaction with its session and importantly with which experiment it belongs to. +Not only do we track the behavioral interactions, but our pricing provider micro-service, once called by the frontend reports the observed/queried price-product into kafka. +This kafka cluster is subscribed to by our pipeline which is configured on a schedule in Airflow, with the possibility of manual trigger. +The final stage of the pricing pipeline, submits computed dynamic pricing results into a redis database for quick updates which is then read by the pricing provider and displayed on the webapp. +This is a very generic end-to-end mechanism which is applicable to a variety of different e-commerce tasks. +We intentionally put emphasis on the development of this infrastructure to establish a reproducible framework for interaction and to minimize any noise. +In addition to behavioral events, the platform logs price observations to a separate Kafka topic. +Each price query generates a record $(i, p, \text{sid}, \phi, t)$ associating the product, displayed price, requesting session, platform mode, and timestamp. +This dual-stream architecture enables joint analysis of price exposure and behavioral response. +We transition the Kappa like architecture of the data collection to a Lambda architecture for actual learning in a surrogate environment. +This allows us to move faster on data which is provided and helps us create a feedback loop for production deployment. +Operationally, goals and experiment runs are tracked in PostgreSQL (goal table, run table, and assignment mapping). +This data-acquisition phase is the first half of the methodology and is intentionally a disconnected component that feeds the later contributions. +The second half uses collected behavioral traces to distinguish classes $Y \in \{A,H\}$ with session-conditioned probability estimates, then injects those estimates into the pricing learner. +Our process follows three stages: (1) observe and \textit{vectorize} behavioral interactions, (2) learn distinguishability to characterize human versus agent patterns, and (3) use the learned signal to train a defensive policy in a controlled dynamic-pricing simulator. +Our web platform (developed in similar spirit to RecSim \parencite{ie_recsim_2019}) gives us a controlled environment where tasks are assigned to human and agentic actors and then executed. + +\vspace{0.3em} +\noindent +Because sessions are collected under controlled experimental conditions where each actor is assigned a known type at the start of the trial, labels $Y_s \in \{H, A\}$ are available as ground truth rather than as the output of a heuristic classifier. +We therefore estimate separate transition kernels directly from each labeled partition $\mathcal{D}_H$ and $\mathcal{D}_A$, treating the resulting $\hat{\mathcal{T}}_H$ and $\hat{\mathcal{T}}_A$ as the ground-truth behavioral profiles for each class. +This allows us to construct a \textit{Contamination Generator} $\mathcal{G}(\alpha)$. +We formulate pricing as a Stackelberg game: the platform (leader) sets prices $p_t$, and the population (follower) responds through trajectories and demand. +Because contamination level $\alpha$ and demand shift are non-stationary online, a simple error term is not enough. +We therefore use a Distributionally Robust Optimization objective. +We define an ambiguity set $\mathcal{U}_\epsilon(\hat{P}_N)$ centered around our empirical reference distribution $\hat{P}_N$ (derived from the generator $\mathcal{G}$). +We utilize the Wasserstein distance metric to define the set of plausible demand distributions the agent might face. +The robust policy $\pi^*$ is obtained by solving the maximin problem $\pi^* = \arg \max_{\pi} \min_{Q \in \mathcal{U}_\epsilon} \mathbb{E}_{d \sim Q} \left[ R(p, d) - \lambda \cdot \text{COI}_{\text{leak}}(p,\tau') \right]$ where $R(p, d)$ is the revenue function and $\lambda$ weighs the information-leakage penalty. +In practice, we parameterize this with a session-level leakage term $\text{COI}_{\text{leak}}(p,\tau') = f(\tau')\cdot \text{InfoValue}(p,\tau')$ where $f(\tau')$ is the weak agent probability. +As part of reward engineering, we keep a UX factor ($UX\in[0,1]$) as an auxiliary evaluation axis. +Our training budget is provisioned through TPU Research Cloud and spans 384 chips across TPU v4, v5e, and v6e generations, with a spot-heavy allocation plus an on-demand reserve. +At peak BF16 throughput this corresponds to approximately $160$\,PFLOPS of aggregate compute. + +\vspace{0.3em} +\noindent +The sign structure is consistent with the theoretical expectation: human sessions produce negative gap scores (closer to the human centroid, far from the agent centroid) while agent sessions produce positive gap scores (closer to the agent centroid). +The two-sided test result ($p<0.001$) at $n_H=13$, $n_A=16$ indicates strong rank distinction between groups, providing evidence that the transition kernels are distinguishable enough to justify their use as a control signal in downstream pricing. +Interpreted on the contamination grid, a $+0.1$ increase in $\alpha$ corresponds to an average revenue decrease of about $9{,}014$ units, and the robust check preserves both direction and significance. +The ability to extract COI is greater in the presence of robustness within the training loop; empirical evidence shows that agent contamination reduces revenue and that robustness is condition-dependent, requiring explicit calibration rather than a one-size-fits-all penalty. + +\vspace{0.3em} +\noindent +Our analysis of the interaction dynamics between the platform and non-human actors suggests that the current static pricing models are insufficient for an agent-mediated economy. +This technology does not come without a more bitter side, ethical concerns do arise from the idea of deploying black-box like solutions to set prices based on a behavioral attributes. + +\vspace{0.3em} +\noindent +Contributions include formalization of non-human transaction orchestration in e-commerce as a distinct source of contamination, definition of COI together with a theorem showing its erosion under increasing agent saturation, a controlled e-commerce research platform built on a hybrid Kappa-Lambda architecture, empirical validation of behavioral distinguishability, translation of distinguishability into a distributionally robust reinforcement learning formulation, and release of a reusable public experimental artifact. + +\vspace{0.3em} +\noindent\textbf{Acknowledgments.} +This research was supported by the TPU Research Cloud program, which provided access to Google Cloud TPU accelerators (including TPU v4, v5e, and v6e). +Eugene Bykovets, PhD---ETH. +\textbf{Project page:} \url{https://velocitatem.github.io/PHANTOM/} + +\renewcommand*{\bibfont}{\footnotesize} +\printbibliography[title={References}] + +\end{document} diff --git a/scripts/nx_paper.sh b/scripts/nx_paper.sh index 036a3c4..7011730 100644 --- a/scripts/nx_paper.sh +++ b/scripts/nx_paper.sh @@ -71,6 +71,16 @@ case "$cmd" in pdflatex -interaction=nonstopmode -file-line-error main.tex cp main.pdf ../../../build/main-arxiv.pdf ;; + build-summary) + mkdir -p paper/build + cd paper/src + latexmk -pdf -jobname=summary -f -interaction=nonstopmode -file-line-error -r ../.latexmkrc -outdir=../build summary.tex + ;; + watch-summary) + mkdir -p paper/build + cd paper/src + latexmk -pvc -pdf -jobname=summary -f -interaction=nonstopmode -file-line-error -r ../.latexmkrc -outdir=../build summary.tex + ;; *) printf '%s\n' "Unknown paper command: $cmd" >&2 exit 1 From e72e3c81c1256afc2420e960ce8afbef85f4fe56 Mon Sep 17 00:00:00 2001 From: Daniel Rosel Date: Wed, 8 Apr 2026 21:46:54 +0200 Subject: [PATCH 16/44] adding missing ideas and apendix kl --- paper/src/auto/main.el | 2 +- paper/src/chapters/03-methodology.tex | 4 ++-- paper/src/main-genpop.tex | 7 +++++++ paper/src/main.tex | 12 +++++------- paper/src/mirrors/genpop/03-methodology.tex | 2 +- 5 files changed, 16 insertions(+), 11 deletions(-) diff --git a/paper/src/auto/main.el b/paper/src/auto/main.el index 31e5a9a..c83f350 100644 --- a/paper/src/auto/main.el +++ b/paper/src/auto/main.el @@ -21,6 +21,6 @@ (LaTeX-add-labels "app:compute_budget" "tab:compute_derivation" - "app:whoclicked_card")) + "app:kl_zeros")) :latex) diff --git a/paper/src/chapters/03-methodology.tex b/paper/src/chapters/03-methodology.tex index d6144d7..2e9be48 100644 --- a/paper/src/chapters/03-methodology.tex +++ b/paper/src/chapters/03-methodology.tex @@ -223,8 +223,7 @@ The dynamic pricing mechanism elicited immediate behavioral adjustments. Partici \subsubsection{Design of Training Factorial Study} -The simulator has multiple configurable factors. We design a multi-factor study across five axes derived from the sweep configurations: (1) RL algorithm (\texttt{ppo}, \texttt{a2c}, \texttt{dqn}, \texttt{qtable}; 4 levels), (2) contamination ratio $\alpha$ sampled from $[0.1, 0.6]$ at four representative levels, (3) robustness radius $\epsilon_\alpha \in \{0.0, 0.15, 0.3\}$ (3 levels), (4) COI penalty weight $\lambda_\text{coi}$ at two reference levels, and (5) pricing action granularity (two discretization settings for \texttt{action\_levels}); giving a grid of $4\times4\times3\times2\times2 = 192$ configurations. Statistical power for the behavioral comparisons is determined by a two-sample test over per-session KL divergence scores; a formal power analysis with minimum detectable effect size at $n_H=13$, $n_A=16$ is reported in the results. -% Power analysis plan: apply a two-sample Mann-Whitney U (or permutation test) on per-session (delta_H - delta_A) divergence scores comparing the human and agent groups. Compute minimum detectable effect size at alpha=0.05, power=0.8, given n_H=13 and n_A=16. Bootstrap confidence intervals on mean KL are a cleaner complement given the non-normality of divergence distributions. +The simulator has multiple configurable factors. We design a multi-factor study across five axes derived from the sweep configurations: (1) RL algorithm (\texttt{ppo}, \texttt{a2c}, \texttt{dqn}, \texttt{qtable}; 4 levels), (2) contamination ratio $\alpha$ sampled from $[0.1, 0.6]$ at four representative levels, (3) robustness radius $\epsilon_\alpha \in \{0.0, 0.15, 0.3\}$ (3 levels), (4) COI penalty weight $\lambda_\text{coi}$ at two reference levels, and (5) pricing action granularity (two discretization settings for \texttt{action\_levels}); giving a grid of $4\times4\times3\times2\times2 = 192$ configurations. Behavioral distinguishability is assessed with a two-sample Mann--Whitney test on per-session divergence gap scores at cohort sizes $n_H=13$ and $n_A=16$. While this scale is generally expensive for reinforcement learning, we execute it on a large TPU cluster to make the sweep tractable. Our training budget is provisioned through TPU Research Cloud and spans 384 chips across TPU v4, v5e, and v6e generations, with a spot-heavy allocation plus an on-demand reserve. At peak BF16 throughput this corresponds to approximately 160\,PFLOPS of aggregate compute (derivation in Appendix~\ref{app:compute_budget}), which makes repeated seeds, ablations, and sensitivity sweeps feasible within practical wall-clock limits. We allocate v6e capacity to the highest-intensity policy training jobs, use v5e for wider hyperparameter exploration where throughput-per-dollar is favorable, and reserve on-demand v4 capacity for runs that should not be interrupted. @@ -504,6 +503,7 @@ Practical implementation of browser agents is a strongly evolving field with nea As part of reward engineering, we keep a UX factor ($UX\in[0,1]$) as an auxiliary evaluation axis. In code, the UX index is implemented as a volatility penalty on relative price changes, with an extra upward-volatility component weighted by $0.5$ and scaled by $\eta_{\text{ux}}$ and an information-budget term. We also keep a separate supra-competitive penalty tied to persistent price excess above a competitive anchor, which punishes high-price behavior even when volatility is low. +We measure volatility as mean absolute relative price movement, $v_t=\frac{1}{N}\sum_{i=1}^N\bigl|(p_{t,i}-p_{t-1,i})/\max(p_{t-1,i},1)\bigr|$. \begin{figure}[ht] \centering diff --git a/paper/src/main-genpop.tex b/paper/src/main-genpop.tex index e54f1de..1c8fb1e 100644 --- a/paper/src/main-genpop.tex +++ b/paper/src/main-genpop.tex @@ -84,4 +84,11 @@ v4 & 64 & 275 & $64 \times 275 = 17{,}600$ \\ Converting to petaFLOPS: 160,320 TFLOPS equals approximately 160 PFLOPS. This is the theoretical peak under sustained arithmetic operations; realized throughput depends on memory bandwidth utilization and inter-chip communication overhead, but the figure serves as a useful upper bound for provisioning decisions. +\section{KL divergence when the reference has zeros} +\label{app:kl_zeros} + +The textbook definition $D_{\mathrm{KL}}(P\parallel Q)=\sum_k P(k)\log(P(k)/Q(k))$ is not usable as-is when our empirical reference puts $Q(k)=0$ somewhere the session distribution still visits: if $P(k)>0$ and $Q(k)=0$, that term wants to blow up to infinity. With only 29 sessions the estimated transition rows are incredibly sparse, so ``never seen in the prototype'' happens a lot. + +In code we do the boring fix: add a tiny floor $\varepsilon$ to both the numerator and denominator inside the log so nothing is exactly zero, which turns the sum into a finite, smoothed surrogate rather than a literal KL to raw counts. We also skip source states that do not exist at all in the reference kernel, because there is nowhere honest to compare against. This keeps the pipeline running and the divergence scores on a comparable scale, at the cost that the number is regularized KL-ish behavior, not a purist information-theoretic quantity---which is acceptable here because we only use the gap between human-anchored and agent-anchored scores as a weak separability signal, not as a calibrated physical constant. + \end{document} diff --git a/paper/src/main.tex b/paper/src/main.tex index c3422cc..49ee31f 100644 --- a/paper/src/main.tex +++ b/paper/src/main.tex @@ -111,15 +111,13 @@ v4 & 64 & 275 & $64 \times 275 = 17{,}600$ \\ Converting to petaFLOPS: $160{,}320\;\text{TFLOPS} = 160.32\;\text{PFLOPS} \approx 160\;\text{PFLOPS}$. This is the theoretical peak under sustained BF16 arithmetic; realized throughput depends on memory bandwidth utilization and inter-chip communication overhead, but the figure serves as a useful upper bound for provisioning decisions. -\section{whoclickedit Dataset Card} -\label{app:whoclicked_card} +\section{KL divergence when the reference has zeros} +\label{app:kl_zeros} -For transparency and reproducibility, this appendix includes the full dataset card used for the public release of the \texttt{whoclickedit} dataset. +The textbook definition $D_{\mathrm{KL}}(P\parallel Q)=\sum_k P(k)\log(P(k)/Q(k))$ is not usable as-is when our empirical reference puts $Q(k)=0$ somewhere the session distribution still visits: if $P(k)>0$ and $Q(k)=0$, that term wants to blow up to infinity. With only 29 sessions the estimated transition rows are incredibly sparse. + +In code we do the basic fix: add a tiny floor $\varepsilon$ to both the numerator and denominator inside the log so nothing is exactly zero, which turns the sum into a finite, smoothed surrogate rather than a literal KL to raw counts. We also skip source states that do not exist at all in the reference kernel, because there is nowhere honest to compare against. This keeps the pipeline running and the divergence scores on a comparable scale, at the cost that the number is regularized KL behavior, not a purist information-theoretic quantity, which is acceptable here because we only use the gap between human-anchored and agent-anchored scores as a weak separability signal. -\lstinputlisting[ - caption={whoclickedit dataset card (README snapshot)}, - label={lst:whoclicked_dataset_card} -]{chapters/auto/whoclicked_dataset_card.md} % \input{../build/concatenated_code} diff --git a/paper/src/mirrors/genpop/03-methodology.tex b/paper/src/mirrors/genpop/03-methodology.tex index 222f553..505aa20 100644 --- a/paper/src/mirrors/genpop/03-methodology.tex +++ b/paper/src/mirrors/genpop/03-methodology.tex @@ -132,7 +132,7 @@ The dynamic pricing mechanism elicited immediate behavioral adjustments. Partici \subsubsection{Design of Training Factorial Study} -The simulator has multiple configurable factors. We design a multi-factor study across five axes derived from the sweep configurations: (1) RL algorithm (PPO, A2C, DQN, Q-table; 4 levels), (2) contamination ratio sampled at four representative levels between 0.1 and 0.6, (3) robustness radius (3 levels), (4) COI penalty weight at two reference levels, and (5) pricing action granularity (two discretization settings for action levels); giving a grid of 192 configurations. Statistical power for the behavioral comparisons is determined by a two-sample test over per-session divergence scores. +The simulator has multiple configurable factors. We design a multi-factor study across five axes derived from the sweep configurations: (1) RL algorithm (PPO, A2C, DQN, Q-table; 4 levels), (2) contamination ratio sampled at four representative levels between 0.1 and 0.6, (3) robustness radius (3 levels), (4) COI penalty weight at two reference levels, and (5) pricing action granularity (two discretization settings for action levels); giving a grid of 192 configurations. Behavioral distinguishability is assessed with a two-sample Mann--Whitney test on per-session divergence gap scores at cohort sizes $n_H=13$ and $n_A=16$. While this scale is generally expensive for reinforcement learning, we execute it on a large TPU cluster to make the sweep tractable. From 97a6bf397431650a2ab8abea9e1f5f8723e557a3 Mon Sep 17 00:00:00 2001 From: Daniel Rosel Date: Wed, 8 Apr 2026 22:00:25 +0200 Subject: [PATCH 17/44] making proper api citation --- paper/.latexmkrc | 5 +++-- paper/src/chapters/mdp_agent.pdf | Bin 10931 -> 10932 bytes paper/src/chapters/mdp_human.pdf | Bin 11953 -> 11953 bytes paper/src/preamble.tex | 5 +++-- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/paper/.latexmkrc b/paper/.latexmkrc index 38c50d1..531ea3e 100644 --- a/paper/.latexmkrc +++ b/paper/.latexmkrc @@ -1,6 +1,7 @@ $pdf_mode = 1; $pdflatex = 'pdflatex -synctex=1 -interaction=nonstopmode -file-line-error %O %S'; -$bibtex_use = 2; # run bibtex when needed -$bibtex = 'bibtex %O %B'; +$bibtex_use = 2; # run biber when biblatex .bcf changes +# biber runs with cwd = -outdir (paper/build); .bib paths are relative to paper/src +$biber = 'biber --input-directory=../src %O %S'; $pdf_previewer = 'zathura %O %S'; $clean_ext = 'synctex.gz bbl bcf run.xml fls fdb_latexmk glg glo gls ist blg lof lot out toc'; diff --git a/paper/src/chapters/mdp_agent.pdf b/paper/src/chapters/mdp_agent.pdf index b0911f18c069e6ea57ee7992c27bf88ad3495015..f9a1eb978ebeec22047c2ad833246010b28cac89 100644 GIT binary patch delta 321 zcmV-H0lxmTRkT&Ge<**#FcgK~`zy}NSY^;AG1e-@2P?=J15w|`9zq&wU^FF3$Ncw8 zs&$M!-H&^6&f!v!gcewkgn=$-LYJ5?i=?WMpRc__eh{s434r`fHx3_QAUNW4M70?@ z51MQ?qvBvqGe~8TN};U*&1ji%q!;gdx6bpCwpR2PEnQ}8JDY#qzOhPpZMvQC3h8dm z2rCFDC8G<@ix2Kj(xWg2`g0=AC%e41?;Rg45Vd)nG9dA&RkSQMmI53m3Db5VVKrzi-3=9C} TxCuOyiz*-pG72RnMNdWw8Df*o delta 320 zcmV-G0l)sVRkKyFe<**0FcgK~{T1hBOc`imt+hq^P=zwaKy2T}9zqOO7)Fvf)_=c5 zt7GKl-j927&gD`NkEYlXF9zLGkM6M9rCy#Ry50wYsA8?q9)RerYcxI(gP_LegbF$K z2GlzqCq<=XGjgHebJi&d8qzFG;NOfH^dX8S+A22OI<7;h+8}?JzfppjPWB};0)Baj zNt}{|WYiWkc_HyLA;Dtu7Yk3;+UYB%gv;TQP2gQL9XrV%8<*G@XUQHkrTaB@Z>&Hg zSrum`#{qfn8rRP*6J5~7QpI{bI5&P@@GFcO#nKgasubTH7xnq%piS*~EwmgJ7aE(@ z^J0bE%;B}Y!@m;+3uf4tY#;mC8$8ytL%;SPZ$y)kDIWqiIg_#}&J0CBG0mL}3;^Y= S2|AOEDj*0k3MC~)PeuyDBEUXO~sv!_96hnbSbS+JZf^AfXj2)$nw#YkHx`?FC#x2}K9vg-_t;&C z0*~^p*f~4&sOvPO`RX##2^}MKvh#g%Q883`r9ad*liMwsS`hFZwIHe p$NcVjj6xNz;e8go!@q)C8RVC3Z~fv8ZuR2QF9#KGRkM#RA^}=3k)Hqn delta 291 zcmV+;0o?wvU9nxTdn|v!YQr!PMDPBJxs=8fY)Nr!H<%ofKq#dUlHN)WLKcppSVodd z^Y>lZaZ1r;_pzgyWjR>HbF5e-z*jut1(rn~RTa|rYp;ogr{V`YD7l}mq&ZQ^+P#%k$X)9t0# zSnSt=iJWDuXA&wug?AQohkpz1q?cc&x$Q?cxUEM+zxo$%NwbeFA_1cki~s-t diff --git a/paper/src/preamble.tex b/paper/src/preamble.tex index 9b680c1..910ae67 100644 --- a/paper/src/preamble.tex +++ b/paper/src/preamble.tex @@ -25,6 +25,7 @@ \usepackage{graphicx} \usepackage{hyperref} \usepackage{booktabs} +\usepackage[american]{babel} \usepackage{csquotes} \usepackage{subcaption} \usepackage{siunitx} @@ -57,8 +58,8 @@ literate={·}{{\textperiodcentered}}1 {−}{{\textminus}}1 {—}{{---}}1 {–}{{--}}1 } -% Use biblatex with authoryear style for in-text citations like (Author, Year) -\usepackage[backend=bibtex,style=authoryear,natbib=true,maxcitenames=2]{biblatex} +% APA 7-style references and citations (requires biber) +\usepackage[style=apa,backend=biber]{biblatex} \addbibresource{bib/references.bib} % Page headers (SciTech format) From 86c06176aee9e3657f4f6d3b3932c3b1a0540a04 Mon Sep 17 00:00:00 2001 From: Daniel Rosel Date: Wed, 8 Apr 2026 22:19:14 +0200 Subject: [PATCH 18/44] building sumamry properly --- paper/.latexmkrc | 5 +++-- paper/src/summary.tex | 44 ++++++++++++++++++------------------------- scripts/nx_paper.sh | 12 ++++++++++++ 3 files changed, 33 insertions(+), 28 deletions(-) diff --git a/paper/.latexmkrc b/paper/.latexmkrc index 531ea3e..e1b5f1f 100644 --- a/paper/.latexmkrc +++ b/paper/.latexmkrc @@ -1,7 +1,8 @@ $pdf_mode = 1; $pdflatex = 'pdflatex -synctex=1 -interaction=nonstopmode -file-line-error %O %S'; $bibtex_use = 2; # run biber when biblatex .bcf changes -# biber runs with cwd = -outdir (paper/build); .bib paths are relative to paper/src -$biber = 'biber --input-directory=../src %O %S'; +# biber cwd is paper/build; scripts/nx_paper.sh symlinks ../build/bib -> ../src/bib so +# datasources log as bib/references.bib and latexmk's -e check works from paper/src +$biber = 'biber %O %S'; $pdf_previewer = 'zathura %O %S'; $clean_ext = 'synctex.gz bbl bcf run.xml fls fdb_latexmk glg glo gls ist blg lof lot out toc'; diff --git a/paper/src/summary.tex b/paper/src/summary.tex index 749a7fb..1a5e9f4 100644 --- a/paper/src/summary.tex +++ b/paper/src/summary.tex @@ -1,26 +1,26 @@ % -*- TeX-master: t -*- % Two-page summary: one self-contained source file (no \input chapters). -% Text is stitched from the thesis chapters using the author’s wording; trim if the PDF exceeds two pages. \documentclass[10pt,letterpaper]{article} \input{preamble} \begin{document} \singlespacing -\setlength{\parskip}{0.25em} +\setlength{\parskip}{0.35em} +\setlength{\parindent}{0pt} \small -\fancyhead[L]{PHANTOM Summary} +\fancyhead[L]{} \begin{center} -{\large\bfseries PHANTOM: Pricing Heuristics Against Non-human Transaction Orchestration Mechanisms}\\[0.3em] -{\normalsize Daniel Rösel}\\[0.12em] -{\small Bachelor of Computer Science \& Artificial Intelligence, IE University, Madrid}\\[0.12em] -{\small Supervised by Alberto Martín Izquierdo \quad\textbar\quad \today} + {\small\url{https://velocitatem.github.io/PHANTOM/}}\\[0.65em] + {\large\bfseries PHANTOM: Pricing Heuristics Against Non-human\\[0.15em] Transaction Orchestration Mechanisms}\\[0.55em] + {\normalsize Daniel Rösel\footnote{Bachelor of Computer Science \& Artificial Intelligence @ IE University, Madrid}}\\[0.55em] + {\small Supervised by Alberto Martín Izquierdo}\\[0.35em] + {\small \today} \end{center} -\vspace{0.35em} +\vspace{0.75em} -\noindent To better understand all wedges of the current works, we must start by exploring the nature of agents, agentic computer use and web automation, complementing that with economic reasoning and strategic interaction. The final surface to cover leads us to data-driven dynamic pricing under uncertainty. The key technical risk is not ``agents buying things'' per se, but agents shaping the behavioral and demand signals that downstream pricing systems consume and depend on \parencite{xia_evaluation-driven_2025}. @@ -34,8 +34,7 @@ In order to create an environment in which prices can be tested against a demand The key component of this mediation between agents and commercial platforms lays in the transaction costs related to information gathering and negotiation. As proposed by \textcite{shahidi_coasean_2025} these costs are bound to collapse towards zero (which we demonstrate mathematically), calling for a re-evaluation of the boundaries between firms and markets. -\vspace{0.3em} -\noindent +\vspace{0.5em} In this paper we present an exploration and defense against the presence of new commercial entities in digitally powered platforms, preserving market equilibrium in the age of AI. We formally define interaction data as coming from some actor which can either be an agent ($A$) or human ($H$). Dynamic pricing algorithms rely on directly translating demand features $q$ to new price assignments $\hat{p}$ across a catalogue of products of size $N$. @@ -43,8 +42,7 @@ This opens opportunities to design a \textit{tabula rasa} of digital market mech We propose a robust optimization objective defined in our methodology, transforming the pricing problem into a form of Distributionally Robust Optimization \parencite{kuhn_distributionally_2025} where the learner must guard against adversarial contamination in observed demand distributors. For purposes of this research, an agent is an algorithmic loop with the ability to access a web platform and perform actions such as clicks, scrolls, and input field fills. -\vspace{0.3em} -\noindent +\vspace{0.5em} The platform does not directly observe the true underlying demand function $d(p)$ where $d \in \mathbb{R}^{+}$ and our proxy $\hat{q} \in \mathbb{R}^{+}$. Instead, it observes a behavioral proxy $\hat{q}_t$, which is a composite signal derived from the mixture of actor types. The total observed demand is a stochastic process governed by the naively defined mixture $Q(p) = (1-\alpha) \cdot \mathbb{E}_{\theta \sim \mathcal{D}_H}[d(p\mid Y=H,\theta)] + \alpha \cdot \mathbb{E}_{\theta \sim \mathcal{D}_A}[d(p\mid Y=A,\theta)] + \epsilon_t$ where $\alpha \in [0, 1]$ represents the contamination parameter (proportion of agents) and $\epsilon_t$ is non-stationary market noise. @@ -53,8 +51,7 @@ We quantify this markup as the \textit{Cost of Information} (COI), which represe We formally demonstrate that standard dynamic pricing mechanisms are not incentive-compatible with high-frequency agentic traffic. As the number of independent competitive agents $N$ querying the system grows, the platform's ability to sustain a COI vanishes. -\vspace{0.3em} -\noindent +\vspace{0.5em} In order for our research to have grounding in interactions we built a robust e-commerce web-platform. The architecture of this platform begins with the deployed web-apps posting interaction data to our backend which processes them and stores each ingested interaction into a kafka cluster. This serves as our data reservoir tracking and associating each interaction with its session and importantly with which experiment it belongs to. @@ -74,8 +71,7 @@ The second half uses collected behavioral traces to distinguish classes $Y \in \ Our process follows three stages: (1) observe and \textit{vectorize} behavioral interactions, (2) learn distinguishability to characterize human versus agent patterns, and (3) use the learned signal to train a defensive policy in a controlled dynamic-pricing simulator. Our web platform (developed in similar spirit to RecSim \parencite{ie_recsim_2019}) gives us a controlled environment where tasks are assigned to human and agentic actors and then executed. -\vspace{0.3em} -\noindent +\vspace{0.5em} Because sessions are collected under controlled experimental conditions where each actor is assigned a known type at the start of the trial, labels $Y_s \in \{H, A\}$ are available as ground truth rather than as the output of a heuristic classifier. We therefore estimate separate transition kernels directly from each labeled partition $\mathcal{D}_H$ and $\mathcal{D}_A$, treating the resulting $\hat{\mathcal{T}}_H$ and $\hat{\mathcal{T}}_A$ as the ground-truth behavioral profiles for each class. This allows us to construct a \textit{Contamination Generator} $\mathcal{G}(\alpha)$. @@ -90,27 +86,23 @@ As part of reward engineering, we keep a UX factor ($UX\in[0,1]$) as an auxiliar Our training budget is provisioned through TPU Research Cloud and spans 384 chips across TPU v4, v5e, and v6e generations, with a spot-heavy allocation plus an on-demand reserve. At peak BF16 throughput this corresponds to approximately $160$\,PFLOPS of aggregate compute. -\vspace{0.3em} -\noindent +\vspace{0.5em} The sign structure is consistent with the theoretical expectation: human sessions produce negative gap scores (closer to the human centroid, far from the agent centroid) while agent sessions produce positive gap scores (closer to the agent centroid). The two-sided test result ($p<0.001$) at $n_H=13$, $n_A=16$ indicates strong rank distinction between groups, providing evidence that the transition kernels are distinguishable enough to justify their use as a control signal in downstream pricing. Interpreted on the contamination grid, a $+0.1$ increase in $\alpha$ corresponds to an average revenue decrease of about $9{,}014$ units, and the robust check preserves both direction and significance. The ability to extract COI is greater in the presence of robustness within the training loop; empirical evidence shows that agent contamination reduces revenue and that robustness is condition-dependent, requiring explicit calibration rather than a one-size-fits-all penalty. -\vspace{0.3em} -\noindent +\vspace{0.5em} Our analysis of the interaction dynamics between the platform and non-human actors suggests that the current static pricing models are insufficient for an agent-mediated economy. This technology does not come without a more bitter side, ethical concerns do arise from the idea of deploying black-box like solutions to set prices based on a behavioral attributes. -\vspace{0.3em} -\noindent +\vspace{0.5em} Contributions include formalization of non-human transaction orchestration in e-commerce as a distinct source of contamination, definition of COI together with a theorem showing its erosion under increasing agent saturation, a controlled e-commerce research platform built on a hybrid Kappa-Lambda architecture, empirical validation of behavioral distinguishability, translation of distinguishability into a distributionally robust reinforcement learning formulation, and release of a reusable public experimental artifact. -\vspace{0.3em} -\noindent\textbf{Acknowledgments.} +\vspace{0.65em} +\noindent\textbf{Acknowledgments.}\quad This research was supported by the TPU Research Cloud program, which provided access to Google Cloud TPU accelerators (including TPU v4, v5e, and v6e). Eugene Bykovets, PhD---ETH. -\textbf{Project page:} \url{https://velocitatem.github.io/PHANTOM/} \renewcommand*{\bibfont}{\footnotesize} \printbibliography[title={References}] diff --git a/scripts/nx_paper.sh b/scripts/nx_paper.sh index 7011730..f4a28d8 100644 --- a/scripts/nx_paper.sh +++ b/scripts/nx_paper.sh @@ -21,18 +21,26 @@ sync_mdp_figures() { cp "$sim_dir/agent_mdp_viz.pdf" "$chapters_dir/mdp_agent.pdf" } +# Biber runs with cwd paper/build; \addbibresource{bib/references.bib} must resolve there. +# Symlink makes biber log 'bib/references.bib' (not ../src/...) so latexmk's post-check passes. +link_build_bib() { + ln -sfn ../src/bib ../build/bib +} + case "$cmd" in build) mkdir -p paper/build sync_mdp_figures bash paper/concat_code.sh cd paper/src + link_build_bib latexmk -pdf -jobname=main -f -interaction=nonstopmode -file-line-error -r ../.latexmkrc -outdir=../build main.tex ;; watch) mkdir -p paper/build sync_mdp_figures cd paper/src + link_build_bib latexmk -pvc -pdf -jobname=main -f -interaction=nonstopmode -file-line-error -r ../.latexmkrc -outdir=../build main.tex ;; clean) @@ -54,12 +62,14 @@ case "$cmd" in mkdir -p paper/build sync_mdp_figures cd paper/src + link_build_bib latexmk -pdf -jobname=main-genpop -f -interaction=nonstopmode -file-line-error -r ../.latexmkrc -outdir=../build main-genpop.tex ;; watch-genpop) mkdir -p paper/build sync_mdp_figures cd paper/src + link_build_bib latexmk -pvc -pdf -jobname=main-genpop -f -interaction=nonstopmode -file-line-error -r ../.latexmkrc -outdir=../build main-genpop.tex ;; build-arxiv) @@ -74,11 +84,13 @@ case "$cmd" in build-summary) mkdir -p paper/build cd paper/src + link_build_bib latexmk -pdf -jobname=summary -f -interaction=nonstopmode -file-line-error -r ../.latexmkrc -outdir=../build summary.tex ;; watch-summary) mkdir -p paper/build cd paper/src + link_build_bib latexmk -pvc -pdf -jobname=summary -f -interaction=nonstopmode -file-line-error -r ../.latexmkrc -outdir=../build summary.tex ;; *) From 97902f39a347bb4efc8451b43d1bde61cd125b13 Mon Sep 17 00:00:00 2001 From: Daniel Rosel Date: Wed, 8 Apr 2026 22:33:15 +0200 Subject: [PATCH 19/44] updated main --- paper/src/chapters/03-methodology.tex | 2 +- paper/src/chapters/mdp_agent.pdf | Bin 10932 -> 10932 bytes paper/src/chapters/mdp_human.pdf | Bin 11953 -> 11953 bytes paper/src/main.tex | 6 ++---- 4 files changed, 3 insertions(+), 5 deletions(-) diff --git a/paper/src/chapters/03-methodology.tex b/paper/src/chapters/03-methodology.tex index 2e9be48..5e68c23 100644 --- a/paper/src/chapters/03-methodology.tex +++ b/paper/src/chapters/03-methodology.tex @@ -143,7 +143,7 @@ The architecture of this platform begins with the deployed web-apps posting inte \paragraph{Public Web Artifact} We transition the Kappa like architecture of the data collection to a Lambda architecture for actual learning in a surrogate environment. This allows us to move faster on data which is provided and helps us create a feedback loop for production deployment. To support further research in this intersection of fields we release P4P \footnote{\url{https://github.com/velocitatem/p4p}} as a public repository providing the interaction layer of the PHANTOM framework. This provides a configurable storefront which can be tailored to any commercial setting with a standardized session-level event tracking. We document the API adapters or what the framework expects in terms of schemas for pricing providers and log ingestion servicse. The repository is intended for controlled experimentation and method replication rather than production commerce deployment. -\paragraph{Public Dataset} For reproducibility of the behavioral analysis and distinguishability experiments, we also release the interaction dataset used in this thesis as \textit{WhoClickedIt}. The dataset is hosted on Hugging Face \footnote{\url{https://huggingface.co/datasets/velocitatem/whoclickedit}} and is distributed as one flattened event sheet (\texttt{whoclicked.csv}) with explicit labels (\texttt{actor\_type}, \texttt{is\_agent}, and \texttt{record\_type}). The associated dataset card specifies the schema, collection process, and known limitations; a full copy is included in Appendix~\ref{app:whoclicked_card}. +\paragraph{Public Dataset} For reproducibility of the behavioral analysis and distinguishability experiments, we also release the interaction dataset used in this thesis as \textit{WhoClickedIt}. The dataset is hosted on Hugging Face \footnote{\url{https://huggingface.co/datasets/velocitatem/whoclickedit}} and is distributed as one flattened event sheet (\texttt{whoclicked.csv}) with explicit labels (\texttt{actor\_type}, \texttt{is\_agent}, and \texttt{record\_type}). The dataset card on that page documents the schema, collection process, and known limitations. \subsubsection{DevOps Principles} diff --git a/paper/src/chapters/mdp_agent.pdf b/paper/src/chapters/mdp_agent.pdf index f9a1eb978ebeec22047c2ad833246010b28cac89..7ec2159365eff0dcb20d6163f3c9d72ce4f61b46 100644 GIT binary patch delta 280 zcmV+z0q6d-RkT&Gh$w%UWnhz7Yn9@I3Npr^sBdErAzo@>G$l#L{P#;*+cENVKkmsn zhs(hto?*!%0lwrBUtzJzqM|@@xwnL*loi)`7ljMW^E~K%YAB6ItCEMHY+6^X!Gn4dJc7 e!@q)i>ExHL9{mga4IcIU(k}u9Z%?z4DI)=al8cT2 delta 280 zcmV+z0q6d-RkT&Gh$w$pWzZ%u)+)sZE65lFQQyWMLKzw&hwGBR`eGwU1n@Mo87*#N_cI$o$!AO>2A#kD+nhgqYKW9 z7s8$inN22tvG5eEqy8YQiaBDk5xlELi>>(yaY=ZrV)P1 diff --git a/paper/src/chapters/mdp_human.pdf b/paper/src/chapters/mdp_human.pdf index b40208093adfb0bb4468b1819ca692f1e28754fe..ebe5c34a7095a60196d73e820ef1a1bde5179fe7 100644 GIT binary patch delta 291 zcmV+;0o?wvU9nxTdn|v!YQr!PMDO~Fxs*B-Y)N(;H<%paKq#dUlHN)WLJ^LjSVodd z^Y@jU*rn*Sj~&en%RmV$v8059Eg4}euaF%?s~iE4z3Q{W2RJB>_#9DV zhR%bL&1O^_tT_)-StL?uYrs-gr3I3U_q|(Z*+^R}`iqt>HMW0UEND>PI!f6 zx8{_Ww4fE|IWMXw${#6ROeTM^@DQw{{vfQ1IbyOAys5JmTk{j*((w3mS%H_!)f~I? zLEuo#ioG>MkGhUSoNq28ozN*zD>~OlH+h}#3%niFG&Nysr@pmR$Ne_KkUH|Ca%Qkn pIpnkFDF~&%g?AR=4*v=^!iz7{-us0c-0OvBEUXO~sv!_96hnbSbS+JZf^AfXj2)$nw#YkHx`?FC#x2}K9vg-_t;&C z0*~^p*f~4&sOvPO`RX##2^}MKvh#g%Q883`r9ad*liMwsS`hFZwIHe p$NcVjj6xNz;e8go!@q)C8RVC3Z~fv8ZuR2QF9#KGRkM#RA^}=3k)Hqn diff --git a/paper/src/main.tex b/paper/src/main.tex index 49ee31f..afc4daf 100644 --- a/paper/src/main.tex +++ b/paper/src/main.tex @@ -18,10 +18,7 @@ \end{titlepage} \begin{abstract} -With accelerated growth of Large Language Model agents in e-commerce a novel adversarial dynamic to digital markets emerges. This paper address the vulnerability of dynamic pricing systems to AI intermediaries that decouple the information gather stages from the transaction execution. By conducing reconnaissance isolates sessions, agents circumvent the ``Cost of Information'' (COI) defined as the accumulated price premium typically thought demand expression estimators. -We formally define this phenomenon and derive the Cost of Information Theorem, proving that as the saturation of independent, utility-maximizing agents increases, the platform’s ability to sustain a COI converges to zero, rendering standard dynamic pricing mechanisms incentive-incompatible. -To respond to this threat we propose a defensive framework which integrates behavioral economics with Adversarially Distributionally Robust Optimization (DRO). We introduce a custom e-commerce research platform built on hybrid Kappa-Lambda architecture, designed to capture and simulate high-fidelity controlled interaction trajectories. We further demonstrate through modeling that human and agent behaviors exhibit distinct transition probability kernels, enabling the construction of discriminative models based on Kullback-Leibler divergence. -These behavioral signals serve as inputs for a Distributionally Robust Reinforcement Learning (DR-RL) agent. We formulate the pricing problem as a Stackelberg game where the learner optimizes against an ambiguity set of demand distributions defined by the Wasserstein distance. This approach allows the pricing policy to remain robust against non-stationary contamination without overfitting to deterministic demand curves. The research validates a mechanism for preserving margin integrity and market equilibrium in an agent-mediated economy, while minimizing degradation to the legitimate human user experience (UX). +With accelerated growth of Large Language Model agents in e-commerce, a novel adversarial dynamic to digital markets emerges. This paper addresses the vulnerability of dynamic pricing systems to AI intermediaries that decouple the information gather stages from the transaction execution. By conducting reconnaissance in isolated sessions, agents circumvent the ``Cost of Information'' (COI) defined as the accumulated price premium typically via demand expression estimators. We formally define this phenomenon and derive the Cost of Information Theorem, proving that as the saturation of independent, utility-maximizing agents increases, the platform's ability to sustain a COI converges to zero, rendering standard dynamic pricing mechanisms incentive-incompatible. To respond to this threat, we propose a defensive framework which integrates behavioral economics with Adversarially Distributionally Robust Optimization (DRO). We introduce a custom e-commerce research platform built on a hybrid Kappa-Lambda architecture, designed to capture and simulate high-fidelity controlled interaction trajectories. We further demonstrate through modeling that human and agent behaviors exhibit distinct transition probability kernels, enabling the construction of discriminative models based on Kullback-Leibler divergence. These behavioral signals serve as inputs for a Distributionally Robust Reinforcement Learning (DR-RL) agent. We formulate the pricing problem as a Stackelberg game where the learner optimizes against an ambiguity set of demand distributions defined by the Wasserstein distance. This approach allows the pricing policy to remain robust against non-stationary contamination without overfitting to deterministic demand curves. Extensive TPU-accelerated factorial training demonstrates that while agent contamination causally reduces short-term revenue, our robust mechanism successfully preserves COI margin integrity and market equilibrium, particularly under higher contamination ratios and larger catalog sizes. Additionally, we show that integrating a balanced UX penalty drastically reduces supra-competitive pricing tendencies, minimizing degradation to the legitimate human user experience. Finally, we release our custom interaction framework and dataset as public artifacts to support future research in agent-mediated traffic. \end{abstract} \noindent\textbf{Keywords:} Dynamic Pricing, LLM Agents, Adversarial Machine Learning, E-commerce, Behavioral Detection, Reinforcement Learning @@ -111,6 +108,7 @@ v4 & 64 & 275 & $64 \times 275 = 17{,}600$ \\ Converting to petaFLOPS: $160{,}320\;\text{TFLOPS} = 160.32\;\text{PFLOPS} \approx 160\;\text{PFLOPS}$. This is the theoretical peak under sustained BF16 arithmetic; realized throughput depends on memory bandwidth utilization and inter-chip communication overhead, but the figure serves as a useful upper bound for provisioning decisions. + \section{KL divergence when the reference has zeros} \label{app:kl_zeros} From 7e3bcf2520039847eb1e2843c90af3be75cfece8 Mon Sep 17 00:00:00 2001 From: Daniel Rosel Date: Thu, 9 Apr 2026 09:20:55 +0200 Subject: [PATCH 20/44] better alignment of arrows in diagram --- .../src/chapters/hero_architecture_figure.tex | 49 ++++++++++--------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/paper/src/chapters/hero_architecture_figure.tex b/paper/src/chapters/hero_architecture_figure.tex index a706781..d5d97de 100644 --- a/paper/src/chapters/hero_architecture_figure.tex +++ b/paper/src/chapters/hero_architecture_figure.tex @@ -20,7 +20,7 @@ bA/.style={rectangle, rounded corners=3pt, draw=heroAmberBorder, fill=heroAmber, line width=0.9pt, align=center, minimum height=0.85cm}, bY/.style={rectangle, rounded corners=3pt, draw=heroGrayBorder, fill=heroGray, - line width=0.9pt, align=center, minimum height=0.82cm}, + line width=0.9pt, align=center, minimum height=0.85cm}, pill/.style={ellipse, draw=black!50, fill=black!4, line width=0.75pt, align=center, minimum width=1.6cm, minimum height=0.68cm}, arr/.style={->, draw=black!80, line width=0.88pt}, @@ -79,36 +79,37 @@ -- (provider.north); %% ============================================================ -%% Panel B x: 11.6–20.4 y: 2.2–10.0 +%% Panel B x: 11.6–20.0 y: 2.2–10.0 %% ============================================================ -\draw[panel] (11.6,2.2) rectangle (19.8,10.0); +\draw[panel] (11.6,2.2) rectangle (20.0,10.0); \node[anchor=west, font=\small\bfseries] at (11.85,9.72) {(b) Distinguishability layer}; -\node[bG, minimum width=2.4cm] (session) at (14.0, 8.9) {Session prefix\\$\tau'$}; -\node[bB, minimum width=2.4cm] (empKern) at (13.65,7.45) {Empirical kernel\\$\hat T'$}; -\node[bY, minimum width=2.4cm] (weakLab) at (17.55,8.9) {Weak labels\\$\mathcal{D}_H,\mathcal{D}_A$}; -\node[bY, minimum width=2.2cm] (protoH) at (12.8, 5.9) {Prototype\\$\bar T_H$}; -\node[bA, minimum width=2.4cm] (kldist) at (15.55,5.9) {KL distances\\$\Delta_H,\Delta_A$}; -\node[bY, minimum width=2.2cm] (protoA) at (18.3, 5.9) {Prototype\\$\bar T_A$}; -\node[bB, minimum width=2.9cm] (calHead) at (13.55,4.25) {Contrastive\\calibration head}; -\node[bG, minimum width=2.55cm] (score) at (17.75,4.25) {Session score\\$f(\tau'),\hat\alpha(\tau')$}; +% x positions shifted +0.3 from the original layout (between left-heavy and +0.55 which hugged the right edge) +\node[bG, minimum width=2.4cm] (session) at (14.3, 8.9) {Session prefix\\$\tau'$}; +\node[bB, minimum width=2.4cm] (empKern) at (13.95,7.45) {Empirical kernel\\$\hat T'$}; +\node[bY, minimum width=2.4cm] (weakLab) at (17.85,8.9) {Weak labels\\$\mathcal{D}_H,\mathcal{D}_A$}; +\node[bY, minimum width=2.2cm] (protoH) at (13.1, 5.9) {Prototype\\$\bar T_H$}; +\node[bA, minimum width=2.4cm] (kldist) at (15.85,5.9) {KL distances\\$\Delta_H,\Delta_A$}; +\node[bY, minimum width=2.2cm] (protoA) at (18.6, 5.9) {Prototype\\$\bar T_A$}; +\node[bB, minimum width=2.9cm] (calHead) at (13.85,4.25) {Contrastive\\calibration head}; +\node[bG, minimum width=2.55cm] (score) at (18.05,4.25) {Session score\\$f(\tau'),\hat\alpha(\tau')$}; -\node[lbl] at (15.55, 3.15) {$\hat\alpha(\tau')=\sigma\!\left(\beta(\Delta_H-\Delta_A)\right)$}; +\node[lbl] at (15.85, 3.15) {$\hat\alpha(\tau')=\sigma\!\left(\beta(\Delta_H-\Delta_A)\right)$}; \draw[arr, rounded corners=4pt] (session.south) -- (empKern.north); -\draw[arr, rounded corners=4pt] (empKern.south) -- (13.65, 6.8) -| (protoH.north); -\draw[arr, rounded corners=4pt] (weakLab.south) -- (17.55, 6.8) -| (protoA.north); +\draw[arr, rounded corners=4pt] (empKern.south) -- (13.95, 6.8) -| (protoH.north); +\draw[arr, rounded corners=4pt] (weakLab.south) -- (17.85, 6.8) -| (protoA.north); % weak labels -> protoH: go south then hard-left below weakLab -\draw[arr, rounded corners=4pt] (weakLab.south) -- (17.55,6.8) -| (protoH.north east); +\draw[arr, rounded corners=4pt] (weakLab.south) -- (17.85,6.8) -| (protoH.north east); \draw[arr] (protoH.east) -- (kldist.west); \draw[arr] (protoA.west) -- (kldist.east); \draw[arr] (kldist.south) -- (calHead.north east); \draw[arr] (calHead.east) -- (score.west); %% ============================================================ -%% Panel C x: 20.8–31.0 y: 2.2–10.0 +%% Panel C x: 20.4–31.0 y: 2.2–10.0 %% ============================================================ -\draw[panel] (20.8,2.2) rectangle (31.0,10.0); +\draw[panel] (20.4,2.2) rectangle (31.0,10.0); \node[anchor=west, font=\small\bfseries] at (21.05,9.72) {(c) Distributionally robust control}; \node[bB, minimum width=3.1cm] (state) at (23.15, 8.9) @@ -129,13 +130,13 @@ \draw[arr, rounded corners=4pt] (ambSet.south) -- (23.15, 6.6) -| ([xshift=-2cm]contScen.north); \draw[arr, rounded corners=4pt] (innerMin.south) -- (28.55, 6.6) -| ([xshift=2cm]contScen.north); \draw[arr] (contScen.south) -- (reward.north); -\draw[arr, rounded corners=6pt] (reward.south) -- (25.9, 3.7) -| (policy.north); +% join reward to policy along policy.north y so the last segment never approaches north from below (avoids upward arrowhead on top edge) +\draw[arr, rounded corners=4pt] (reward.south) -- (reward.south |- policy.north) -- (policy.north); \draw[arr] (policy.east) -- (publish.west); -% market response: up the right edge of panel C, entirely inside, rounded +% market response: up the right edge, then left into state summary from the east \draw[arrG, rounded corners=6pt] (publish.east) -- (30.6, 3.05) - -- (30.6, 9.8) - -- node[midway, lbl] {market response} (state.north |- 0, 9.8) - -- (state.north); + -- (30.6, 8.9) + -- node[midway, above, lbl] {market response} (state.east); %% ============================================================ %% Cross-panel connectors – gutter at y = 1.0..2.2 @@ -152,8 +153,8 @@ % 2. Score -> State (depth y=1.45) \draw[crossG, rounded corners=6pt] (score.south) -- (score.south |- 0, 1.45) - -- node[pos=0.5, lbl] {contamination signal} (20.6, 1.45) - -- (20.6, 8.9) + -- node[pos=0.5, lbl] {contamination signal} (20.2, 1.45) + -- (20.2, 8.9) -- (state.west); % 3. Publish -> Provider (depth y=1.05, deepest) From 47b07daa6cc039b48faf868d0c6128703d6a71b4 Mon Sep 17 00:00:00 2001 From: Daniel Rosel Date: Thu, 9 Apr 2026 09:28:33 +0200 Subject: [PATCH 21/44] fixing diagram lines --- paper/src/chapters/hero_architecture_figure.tex | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/paper/src/chapters/hero_architecture_figure.tex b/paper/src/chapters/hero_architecture_figure.tex index d5d97de..5e94c3c 100644 --- a/paper/src/chapters/hero_architecture_figure.tex +++ b/paper/src/chapters/hero_architecture_figure.tex @@ -24,7 +24,6 @@ pill/.style={ellipse, draw=black!50, fill=black!4, line width=0.75pt, align=center, minimum width=1.6cm, minimum height=0.68cm}, arr/.style={->, draw=black!80, line width=0.88pt}, - bidir/.style={<->, draw=black!80, line width=0.88pt}, darr/.style={->, draw=black!60, line width=0.80pt, densely dashed}, crossA/.style={->, draw=heroAmberBorder!90!black, line width=1.15pt, dash pattern=on 3.5pt off 2pt}, crossG/.style={->, draw=heroGreenBorder!90!black, line width=1.15pt, dash pattern=on 3.5pt off 2pt}, @@ -55,7 +54,8 @@ \draw[arr] (human.east) -- (web.west); \draw[arr] (agent.east) -- (web.west); \draw[arr] (web.east) -- (provider.west); -\draw[bidir] (provider.east) -- (redis.west); +% single arrow: bidir on a short edge stacks two tips and reads as a messy cross +\draw[arr] (provider.east) -- (redis.west); % web/provider -> kafka \draw[arr] (web.south) -- (kBehav.north) @@ -63,9 +63,9 @@ \draw[arr] (provider.south) -- (kQuotes.north) node[midway, right, lbl] {$(i,p,\mathrm{sid},\phi,t)$}; -% kafka -> worker (straight south) -\draw[arr] (kBehav.south) -- (worker.north); -\draw[arr] (kQuotes.south) -- (worker.north); +% kafka -> worker: behavior stream vertical; price quotes L-shaped so both meet worker without a diagonal across the panel +\draw[arr] (kBehav.south) -- (worker.north); +\draw[arr, rounded corners=3pt] (kQuotes.south) -- (7.5, 5.35) -| (worker.north); % worker -> registry \draw[arr] (worker.east) -- (registry.west); @@ -100,10 +100,10 @@ \draw[arr, rounded corners=4pt] (empKern.south) -- (13.95, 6.8) -| (protoH.north); \draw[arr, rounded corners=4pt] (weakLab.south) -- (17.85, 6.8) -| (protoA.north); % weak labels -> protoH: go south then hard-left below weakLab -\draw[arr, rounded corners=4pt] (weakLab.south) -- (17.85,6.8) -| (protoH.north east); +\draw[arr, rounded corners=4pt] (weakLab.south) -- (17.85,6.8) -| (protoH.north); \draw[arr] (protoH.east) -- (kldist.west); \draw[arr] (protoA.west) -- (kldist.east); -\draw[arr] (kldist.south) -- (calHead.north east); +\draw[arr, rounded corners=4pt] (kldist.south) -- (calHead.north); \draw[arr] (calHead.east) -- (score.west); %% ============================================================ From ace52e8e14e0f7fa96ab5eb113c0c898b0bce1a0 Mon Sep 17 00:00:00 2001 From: Daniel Rosel Date: Thu, 9 Apr 2026 09:30:23 +0200 Subject: [PATCH 22/44] rephrasing some things and updating language --- docs/static/images/banner.svg | 2 +- paper/src/auto/main.el | 1 + paper/src/chapters/01-intro.tex | 4 +- paper/src/chapters/02-literature-review.tex | 18 ++++---- paper/src/chapters/03-methodology.tex | 48 ++++++++++---------- paper/src/chapters/05-discussion.tex | 14 +++--- paper/src/chapters/06-conclusion.tex | 28 +++++------- paper/src/chapters/acknowledgements.tex | 8 +++- paper/src/chapters/mdp_agent.pdf | Bin 10932 -> 10932 bytes paper/src/chapters/mdp_human.pdf | Bin 11953 -> 11953 bytes paper/src/main.tex | 14 ++++-- 11 files changed, 70 insertions(+), 67 deletions(-) diff --git a/docs/static/images/banner.svg b/docs/static/images/banner.svg index 517491c..7a6400e 100644 --- a/docs/static/images/banner.svg +++ b/docs/static/images/banner.svg @@ -49,7 +49,7 @@ average information rent - COI := E[P] - p + COI = E[P] - p diff --git a/paper/src/auto/main.el b/paper/src/auto/main.el index c83f350..df170dd 100644 --- a/paper/src/auto/main.el +++ b/paper/src/auto/main.el @@ -16,6 +16,7 @@ "chapters/04-results" "chapters/05-discussion" "chapters/06-conclusion" + "chapters/acknowledgements" "article" "art12") (LaTeX-add-labels diff --git a/paper/src/chapters/01-intro.tex b/paper/src/chapters/01-intro.tex index d66b0c2..63f3aef 100644 --- a/paper/src/chapters/01-intro.tex +++ b/paper/src/chapters/01-intro.tex @@ -18,7 +18,7 @@ The current innovation boom in generative artificial intelligence and its applic The key stakeholders affected by the threat of increasing agent-driven traffic include online businesses and platform operators (especially in bot-heavy sectors like retail, travel, and financial services), their security, fraud, and engineering teams, end users whose accounts and data are exposed and whose experience degrades, regulators and legal stakeholders responding to breaches and fraud, and the attackers or bot operators driving the automation \parencite{imperva_rapid_2025}. -The industry has already seen legal action in cases like Amazon against Perplexity \parencite{ghaffary_amazon_2025}, stemming from the difficulty of identifying traffic from hybrid systems like the Commet browser. This paper explores such systems to better understand what the interaction data looks like and what it means for dynamic pricing and recommendation systems downstream. This observed impact indicates a need for prevention of secondary negative effects on the ``legacy'' systems which power modern revenue sources for many companies. Dynamic pricing algorithms rely on directly translating demand features $q$ to new price assignments $\hat{p}$ across a catalogue of products of size $N$. This opens opportunities to design a \textit{tabula rasa} of digital market mechanisms that will shape the future of commerce in the age of artificial intelligence. +The industry has already seen legal action in cases like Amazon against Perplexity \parencite{ghaffary_amazon_2025}, stemming from the difficulty of identifying traffic from hybrid systems like the Comet browser. This paper explores such systems to better understand what the interaction data looks like and what it means for dynamic pricing and recommendation systems downstream. This observed impact indicates a need for prevention of secondary negative effects on the ``legacy'' systems which power modern revenue sources for many companies. Dynamic pricing algorithms rely on directly translating demand features $q$ to new price assignments $\hat{p}$ across a catalogue of products of size $N$. This opens opportunities to design a \textit{tabula rasa} of digital market mechanisms that will shape the future of commerce in the age of artificial intelligence. \subsection{Solution Space Overview} Dynamic pricing systems, as presented by \textcite{mueller_low-rank_2019}, often deal with sparse low-rank data of demand signals which, combined with contamination from agents, creates complex interactions that impact pricing. To further complicate the problem, certain commercial settings such as the one presented by \textcite{amjad_censored_2017} must address the true demand of products under censored observations. This provides a formulation for handling demand in our case with multiple kinds of commercial mediators: $\hat{q} \gets q_A + q_H$ where $q_A$ represents the distribution of demand generated by agentic mediators and $q_H$ represents that of true human demand, these are two distinct populations with divergent objective functions. @@ -64,4 +64,4 @@ Extract final result $r$ from terminal state\; \end{algorithm} -The previously described goal of distinguishability allows us to formulate a task which entails taking raw interaction data for either actor and creating a composite demand estimate $\hat{q}$. We propose a robust optimization objective defined in our methodology, transforming the pricing problem into a form of Distributionally Robust Optimization \parencite{kuhn_distributionally_2025} where the learner must guard against adversarial contamination in observed demand distributors. In this setting we must learn to make decision that perform under the assumption of not having a single estimated probability distribution but under an ambiguity set of any distribution, of which we have limited information. In our case as stated is a mixture of distributions with a parameter which is unknown and non-stationary. +The previously described goal of distinguishability allows us to formulate a task which entails taking raw interaction data for either actor and creating a composite demand estimate $\hat{q}$. We propose a robust optimization objective defined in our methodology, transforming the pricing problem into a form of distributionally robust optimization \parencite{kuhn_distributionally_2025} in which the learner guards against adversarial contamination in observed demand \emph{distributions}. The decision rule must perform when the data-generating law is not a single known distribution but any member of an ambiguity set described only partially. Here that law is a mixture whose weight and components need not be stationary. diff --git a/paper/src/chapters/02-literature-review.tex b/paper/src/chapters/02-literature-review.tex index 5aad893..7e3e63d 100644 --- a/paper/src/chapters/02-literature-review.tex +++ b/paper/src/chapters/02-literature-review.tex @@ -1,15 +1,15 @@ \section{Literature Review} -To better understand all wedges of the current works, we must start by exploring the nature of agents, agentic computer use and web automation, complementing that with economic reasoning and strategic interaction. The final surface to cover, leads us to data-driven dynamic pricing under uncertainty. The key technical risk is not ``agents buying things'' per se, but agents shaping the behavioral and demand signals that downstream pricing systems consume and depend on. This latter case of agents shopping is currently pending legal action in the case of \textcite{noauthor_amazoncom_2026} which is currently being treated as a violation of the Computer Fraud and Abuse Act. The introduction of these mediating actor entities into economic systems, is further creating a threat of false-name bidding \parencite{yokoo_effect_2004}, which prior research has explored in a trading context. Other research on pseudonyms in dynamic systems, demonstrate whitewashing in AI agents which can ignore defensive mechanisms by re-entry with different identities \parencite{feldman_free-riding_2004}. Dynamic pricing assumes demand proxies are behaviorally meaningful, while bot detection aims at security and access control. The missing bridge is a principled framework for distinguishing non-human reconnaissance from genuine human demand expression and integrating that distinguishability into pricing heuristics without degrading legitimate user experience (in our research tracked by the user-experience index). This gap, is what our contribution aims to address, particularly for the aforementioned stakeholder groups. +To situate the work we review agents and agentic computer use, web automation, economic reasoning, and strategic interaction, then turn to data-driven dynamic pricing under uncertainty. The main technical risk is not ``agents buying things'' in isolation but agents reshaping the behavioral and demand signals on which downstream pricing depends. Related litigation is already underway---for example \textcite{noauthor_amazoncom_2026} under the Computer Fraud and Abuse Act. Mediating actors also revive classic concerns such as false-name bidding \parencite{yokoo_effect_2004}; pseudonymous re-entry can whitewash reputation and weaken defenses \parencite{feldman_free-riding_2004}. Dynamic pricing assumes demand proxies are behaviorally meaningful, whereas classical bot detection targets security and access control. The gap we target is a principled way to separate non-human reconnaissance from genuine human demand expression and to fold that signal into pricing without degrading legitimate users (we track harm with a user-experience index), for the stakeholders named in the introduction. \subsection{Agent Taxonomy and Definitions} An agent in the context of artificial intelligence is generally defined by anything that can reason and act upon observations of its environments (collected through some sensory inputs) and carry out actions through effectors. Moreover, a rational agent is an entity that is capable of perceiving the world around them and taking actions to advance specified goals. This definition by \textcite{russell_artificial_2021} is further developed in an economic context by \textcite{parkes_economic_2015}, suggesting AI research attempts to construct a synthetic \textit{homo economicus}, which may also be termed \textit{machina economicus}. A specific class or taxon of this \textit{machina economicus}, the Large Language Model (LLM) agent, is defined as an autonomous system capable of achieving goals and adapting post-training, often without needing explicit code or fundamental model changes \parencite{xia_evaluation-driven_2025}. -We must however acknowledge the current SOTA as presented by OSWORLD simulations by \textcite{xie_osworld_2024} have demonstrated that multi-modal tasks across desktop and web interaction modes, have a top-performing score of only 12.24\% success, whereas humans have a higher 72\% success rate; this is linked to the lack of grounding of these agents and their inability of handling unexpected errors. This weakness matters for this research because it clarifies the near-term threat model: practical exploitation does not require a fully competent ``computer assistant'', only enough automation to perform high-volume reconnaissance actions (search/filter/open product pages, probe availability/price boundaries) that can contaminate behavioral signals. With the expected growth of these capabilities, this threat only becomes more perilous to revenue management systems. +We must however acknowledge that OSWORLD simulations by \textcite{xie_osworld_2024} report a top success rate of only 12.24\% on multi-modal desktop and web tasks, versus about 72\% for humans, reflecting limited grounding and brittle recovery from unexpected errors. This weakness matters for this research because it clarifies the near-term threat model: practical exploitation does not require a fully competent ``computer assistant'', only enough automation to perform high-volume reconnaissance actions (search/filter/open product pages, probe availability/price boundaries) that can contaminate behavioral signals. With the expected growth of these capabilities, this threat only becomes more perilous to revenue management systems. -We model an agent session as producing some events with lower in-session conversion levels relative to humans, this we state in our assumption that $P(\text{purchase} \vert A) < P(\text{purchase} \vert H)$ but with a potentially higher volatility in $\hat{q}$, which we observe through the look-to-book metrics in our simulation. +We model agent sessions as producing lower in-session conversion than humans, i.e.\ $P(\text{purchase} \vert A) < P(\text{purchase} \vert H)$, with potentially higher volatility in $\hat{q}$, which we proxy with look-to-book metrics in the simulator. \subsection{Economic Agents: From Homo Economicus to Machina Economicus} @@ -21,9 +21,9 @@ A HAP (HTTP Agent Profile) protocol has been developed as an internet draft by \ \subsection{Problem Evidence and Market Impact} -The statistical issue of contamination in dynamic pricing systems that observe demand features as a means to update prices has been documented in various previous contexts. The airline industry (which has accounted for 24\% of observed disruptions) has seen malicious activity with a measureable impact on skewing key performance indicators by behavior visible in the look-to-book metrics. Excessive reconnaissance traffic inflates search volume without corresponding completed bookings, thereby skewing demand forecasts and disrupting dynamic pricing models. Demand proxies have also been observed to cause significant threat to inventory management by creating artificial scarcity that distorts the demand-supply relationships in the enterprise model. Censored demand as shown by \textcite{amjad_censored_2017} can also be observed in low-bias demand under-estimation caused by a distortion effect coming from non-human traffic data \parencite{imperva_rapid_2025}. +Contamination in dynamic pricing systems that observe demand features to update prices appears across several industries. Aviation (about 24\% of observed disruptions in one industry survey) illustrates how malicious or scripted traffic can skew KPIs visible in look-to-book metrics. Excessive reconnaissance traffic inflates search volume without corresponding completed bookings, thereby skewing demand forecasts and disrupting dynamic pricing models. Demand proxies have also been observed to cause significant threat to inventory management by creating artificial scarcity that distorts the demand-supply relationships in the enterprise model. Censored demand as shown by \textcite{amjad_censored_2017} can also be observed in low-bias demand under-estimation caused by a distortion effect coming from non-human traffic data \parencite{imperva_rapid_2025}. -When dynamic pricing algorithms operate on highly contaminated or noisy data, the risk grows significantly in creating inaccurate price inferences. The emergent mitigation driven by un-informed reward and regret signals might lead to price suppression for sales continuity which results in harming margins and resulting in a revenue loss. System that poorly fit undesired behavior might result in price gouging, which calls for strong guardrails while preserving targeted business strategy \parencite{mullapudi_reinforcement_2025}. +When dynamic pricing algorithms train on highly contaminated or noisy data, mis-inference risk rises. Mis-specified reward and regret signals can push prices down to preserve volume, eroding margins, while misfit to legitimate demand can produce the opposite failure mode; both call for guardrails that preserve commercial intent \parencite{mullapudi_reinforcement_2025}. %Documented instances of agent-driven market disruptions - Quantitative evidence of pricing manipulation - Case studies from affected industries @@ -31,11 +31,11 @@ When dynamic pricing algorithms operate on highly contaminated or noisy data, th \subsection{Theoretical Foundations: Economic Parallels} -Early hints of exploration of prices in a standard English auction explored by \textcite{varian_economic_1995} which hints at exploration of prices in a sequential manner, which leads to a marginally different cost to the bidder than the reservation price of the seller. This is a setting in which there is no cost incured by the buyer for their actions or exploring prices in the market. They propose that any agent responsable for the pricing of a good must be imune to dynamic strategies which might extract private information from a market. A key take-away which relates to the Vickery auction mechanism (also called a \textit{direct mechanism}) suggests that not only would defenses against such exploitation be necessary, but the construction of a mechanism in which revelation of the true willingness to pay is the dominant strategy for commerce. +\textcite{varian_economic_1995} studies sequential exploration of prices in an English auction: the bidder's cost can differ slightly from the seller's reservation price. In that setting the buyer incurs no separate cost for searching or exploring prices. The authors argue that any party \emph{responsible} for pricing must be immune to dynamic strategies that extract private information. The link to the Vickrey (second-price) auction, a \textit{direct mechanism}, is that defenses against exploitation may need to pair with mechanisms in which truthful revelation of willingness to pay is incentive-compatible. Like in classical revenue-maximizing auctions \parencite{roughgarden_cs364a_2013} we assume that the human actor in our system has a private valuation $v$ which we formally draw from intrinsically defined distributions. The important note here is that the agent proxy does not have a mechanism to convey this private information into the demand data which directly impacts the pricing systems. -The key component of this mediation between agents and commercial platforms lays in the transaction costs related to information gathering and negotiation. As proposed by \textcite{shahidi_coasean_2025} these costs are bound to collapse towards zero (which we demonstrate mathematically), calling for a re-evaluation of the boundaries between firms and markets. As argued by \textcite{coase_nature_1937}, the market participation and time associated with that participation, is critical part of the Coasean transaction cost logic which includes the discovery or relevant pricing within a given market. This process of price discovery without the presence of AI Agents can be time consuming and resource intensive. To build on top of this work we provide a proof of optimal conditions theorised by Coaes as an extension to AI-mediated markets. +The mediation between agents and commercial platforms turns on transaction costs of information gathering and negotiation. \textcite{shahidi_coasean_2025} argue these costs tend toward zero (we give a complementary formal result in Section~3). \textcite{coase_nature_1937} treats search and participation time as central to Coasean transaction costs, including discovery of relevant prices. Price discovery without AI intermediaries is already costly; we extend the classical Coasean logic to AI-mediated markets. % Economic foundations: relating the problem to options pricing theory. Cost of Information (COI) concept and its relevance @@ -43,13 +43,13 @@ The key component of this mediation between agents and commercial platforms lays \subsection{Landscape of Existing Work} -Explorations of the algorithmic collusion by LLMs \parencite{fish_algorithmic_2025} has demonstrated a cross-model tendency of market division with a strong sensitivity to instructions provided in the ``system prompt''. If a dynamic pricing algorithm which is trained to respond to market signals learns to coordinate with competitor agents (or become manipulated by those agents), the market equilibrium is under threat of destabilization. This is particularly true for Q-learning pricing learners as demonstrated by \textcite{calvano_artificial_2018}. +Work on algorithmic collusion by LLMs \parencite{fish_algorithmic_2025} reports cross-model sensitivity to instructions in the ``system prompt,'' including tendencies toward market division. If a dynamic pricing algorithm which is trained to respond to market signals learns to coordinate with competitor agents (or become manipulated by those agents), the market equilibrium is under threat of destabilization. This is particularly true for Q-learning pricing learners as demonstrated by \textcite{calvano_artificial_2018}. Our effort to combat contamination stems from research by \textcite{hardt_strategic_2015} on strategic classification, in conjunction with \textcite{liu_contextual_2024} who demonstrate a linear regret if contamination is ignored. The strategic classification adversarial effect comes from an effort to manipulate some representative features used in a learning pipeline, which can result in lower prices on loans or lower prices from dynamic pricing algorithms. To bridge the gap between detection and robust pricing, we look at work in Distributionally Robust Optimization (DRO). As defined by \textcite{kuhn_wasserstein_2024}, DRO provides a framework for decision-making under ambiguity, where the true data distribution is unknown but lies within a ``Wasserstein ball'' of a target distribution. In our context, the ``ambiguity set'' represents the uncertainty introduced by agentic reconnaissance. By optimizing for the worst-case distribution within this set, pricing mechanisms can become resilient to the distributional shifts such as the ones caused by non-human actors, effectively robustifying the revenue function against the contamination described in our problem statement. -In order to create an environment in which prices can be tested against a demand estimate generated by some behavioral model, we take inspiration from the architecture proposed by \textcite{ie_recsim_2019} in the RecSim platform built for recommendation systems. By modeling the distinct user behavior as partially observable Markov decision processes, we can generate faithful interactions which allow us to generalize, past the constraint which is also present in recommendation systems, of rarely having enough experience with individual actor's interactions for good recommendations without generalization. The key inspiration comes from the user choice modeling which we translate to a user transition model for each distinct actor type (agent or human). We further consider the possibility of modeling our quantitative research platform using dynamic Bayesian networks for the sake of tractability within the system. The contribution or RecSim enables researchers to better understand learning algorithms in fixed environments, a gap we identify as needing to be bridged within the space of dynamic pricing. +To build an environment where prices face a demand estimate from a behavioral model, we draw on RecSim \parencite{ie_recsim_2019}. Modeling user behavior as partially observable Markov decision processes yields synthetic interaction that generalizes past the usual cold-start limit of per-user data. We translate RecSim-style user choice modeling into per-class transition models (human versus agent). Dynamic Bayesian networks remain a tractability option for the full platform. RecSim's main contribution is a sandbox for recommender learners; we adapt that idea to dynamic pricing under contamination. % TODO: mention https://github.com/meta-pytorch/OpenEnv/tree/main/envs/browsergym_env We also acknowledge the difficulty in similarly affected fields such as authorship, where \textcite{ganie_uncertainty_2025} demonstrate the theoretical limits of the distributional divergence between text authored by a human or large language model. Their approach of computing the divergence between two distributions demonstrates purely theoretically that no classifier can outperform random guessing on their particular task. This is yet another factor to take into consideration when exploring the potential mitigation strategies. diff --git a/paper/src/chapters/03-methodology.tex b/paper/src/chapters/03-methodology.tex index 5e68c23..e98e27f 100644 --- a/paper/src/chapters/03-methodology.tex +++ b/paper/src/chapters/03-methodology.tex @@ -128,7 +128,7 @@ Since the integrand vanishes as $N \to \infty$ for all $t > \underline{p}$, the \end{proof} -This result naively proves that standard pricing policies $\pi$ fail to extract surplus in the presence of large-scale agentic search, necessitating a robust counter-mechanism. +This result implies that standard pricing policies $\pi$ cannot extract the same surplus under large-scale agentic search without additional structure, which motivates the robust control layer below. % The DRO objective creates a lower bound on COI extraction, effectively guaranteeing a minimum margin even in the presence of adversarial agents. we need to prove this and demonstrate that in a theorem. @@ -137,22 +137,22 @@ This result naively proves that standard pricing policies $\pi$ fail to extract \subsection{System Architecture: Hybrid Kappa-Lambda Architecture} -In order for our research to have grounding in interactions we built a robust e-commerce web-platform. We initially conducted a survey of the leading platforms of airlines and hotel booking sites to identify the specific interface patterns that effectively manage complex travel data. Our analysis revealed a clear industry standard: while both sectors rely on tabbed service selection and left-sidebar filtering to streamline navigation, they diverge in result presentation: airlines utilize visual date-price bars and multi-step wizards to optimize for logistical transparency, whereas hotel platforms leverage image-led cards and scarcity triggers to drive emotional engagement and urgency. Our web framework defines a highly agnostic boilerplate which can be seeded with any data-modality with an easy-to-tailor pattern, which we leverage to define a \texttt{hotel} and \texttt{airline} mode. Both modes are then individually deployed via an environment level argument which adjusts the proxy routing with a custom middleware inside next.js to render only the desired mode. The purpose of this was to create a baseline adaptable to any use-case or desired commercial application. +In order for our research to have grounding in interactions we built a robust e-commerce web-platform. We initially conducted a survey of the leading platforms of airlines and hotel booking sites to identify the specific interface patterns that effectively manage complex travel data. Our analysis revealed a clear industry standard: while both sectors rely on tabbed service selection and left-sidebar filtering to streamline navigation, they diverge in result presentation: airlines utilize visual date-price bars and multi-step wizards to optimize for logistical transparency, whereas hotel platforms leverage image-led cards and scarcity triggers to drive emotional engagement and urgency. Our web framework defines a highly agnostic boilerplate which can be seeded with any data-modality with an easy-to-tailor pattern, which we leverage to define a \texttt{hotel} and \texttt{airline} mode. Both modes are then individually deployed via an environment-level argument which adjusts the proxy routing with custom middleware in Next.js to render only the desired mode. The purpose of this was to create a baseline adaptable to any use-case or desired commercial application. -The architecture of this platform begins with the deployed web-apps posting interaction data to our backend which processes them and stores each ingested interaction into a kafka cluster. This serves as our data reservoir tracking and associating each interaction with its session and importantly with which experiment it belongs to. Not only do we track the behavioral interactions, but our pricing provider micro-service, once called by the frontend reports the observed/queried price-product into kafka. This kafka cluster is subscribed to by our pipeline which is configured on a schedule in Airflow, with the possibility of manual trigger. The final stage of the pricing pipeline, submits computed dynamic pricing results into a redis database for quick updates which is then read by the pricing provider and displayed on the webapp. This is a very generic end-to-end mechanism which is applicable to a variety of different e-commerce tasks. We intentionally put emphasis on the development of this infrastructure to establish a reproducible framework for interaction and to minimize any noise. +The architecture begins with deployed web applications posting interaction data to a backend that stores each record in Apache Kafka. Kafka acts as the reservoir linking sessions to experiments. Behavioral events and, separately, price observations from the pricing-provider microservice (invoked by the frontend) land in Kafka topics. A scheduled Airflow pipeline (with manual triggers) consumes the stream; the final pricing stage writes vectors to Redis for low-latency reads by the provider and display in the client. The pattern is deliberately standard---Kafka for durability and replay, Redis for serving---so the same skeleton applies across e-commerce settings. We invested in this stack to keep runs reproducible and to limit extraneous variance. -\paragraph{Public Web Artifact} We transition the Kappa like architecture of the data collection to a Lambda architecture for actual learning in a surrogate environment. This allows us to move faster on data which is provided and helps us create a feedback loop for production deployment. To support further research in this intersection of fields we release P4P \footnote{\url{https://github.com/velocitatem/p4p}} as a public repository providing the interaction layer of the PHANTOM framework. This provides a configurable storefront which can be tailored to any commercial setting with a standardized session-level event tracking. We document the API adapters or what the framework expects in terms of schemas for pricing providers and log ingestion servicse. The repository is intended for controlled experimentation and method replication rather than production commerce deployment. +\paragraph{Public Web Artifact} We transition the Kappa like architecture of the data collection to a Lambda architecture for actual learning in a surrogate environment. This allows us to move faster on data which is provided and helps us create a feedback loop for production deployment. To support further research in this intersection of fields we release P4P \footnote{\url{https://github.com/velocitatem/p4p}} as a public repository providing the interaction layer of the PHANTOM framework. This provides a configurable storefront which can be tailored to any commercial setting with a standardized session-level event tracking. We document the API adapters and expected schemas for pricing providers and log ingestion services. The repository is intended for controlled experimentation and method replication rather than production commerce deployment. \paragraph{Public Dataset} For reproducibility of the behavioral analysis and distinguishability experiments, we also release the interaction dataset used in this thesis as \textit{WhoClickedIt}. The dataset is hosted on Hugging Face \footnote{\url{https://huggingface.co/datasets/velocitatem/whoclickedit}} and is distributed as one flattened event sheet (\texttt{whoclicked.csv}) with explicit labels (\texttt{actor\_type}, \texttt{is\_agent}, and \texttt{record\_type}). The dataset card on that page documents the schema, collection process, and known limitations. \subsubsection{DevOps Principles} -Reproducible results are key to quality research platforms, this is taken into mind when deploying and working with our research platform. From a deployment standpoint the platform can be deployed across a large variety of providers and can be run locally. When developing a new interaction modality apart from the ones that come out of the box, a simple template pattern can be followed. The middleware of the framework is designed to properly render the chosen modality from environmental variables, thus deployment of different or parallel version of the software can be easily parametrized. +Reproducibility guided deployment choices: the stack runs locally or on common cloud providers. New interaction modalities follow a small template; middleware reads environment variables so parallel deployments (e.g.\ staging versus production-like experiments) differ only in configuration, not in forked codebases. \subsubsection{Online Dynamic Pricing} -In order to collect data from actors under correct conditions we replicate a naive and simple dynamic pricing algorithm which runs in the background during the experiments. +To expose participants to state-dependent prices without over-constraining the study, we run a transparent surge--discount heuristic in the background during data collection. The dynamic pricing done is handled by a pipeline which computes a demand estimate on a per-product basis of a specific window of the data, defined by the period $T$ which by default is 5 minutes. This dynamic pricing pipeline computes a demand estimate vector $\hat{q} \in \mathbb{R}^N$ by a weighted sum of interactions for each product, it additionally computes a price elasticity vector $\hat{\epsilon}$ in the same dimensions as our demand. The final features matrix is of the size $N \times 2$ which we translate to a new price vector $\hat{p} \in \mathbb{R}^N$. @@ -181,7 +181,7 @@ where $p_0 \in \mathbb{R}^N$ is the base price vector (which is seeded into our We start from a practical constraint: we do not have access to proprietary production data. Because of that, we design our own fictional platform that still represents how commercial platforms work in the real world. The design comes from a survey of hotel and airline websites, where we extracted common interface components and used them as a high-level template for dynamic pricing environments. -The interface is organized as a product catalog where each product belongs to a time-bounded price vector (for example, a daily pricing period). During each period we collect interaction data by instrumenting UI components and predefined action templates that are still customizable. This gives us control without losing realism. +The interface is organized as a product catalog where each product belongs to a time-bounded price vector (for example, a daily pricing period). During each period we collect interaction data by instrumenting UI components and predefined action templates that are still customizable. That yields controlled variation while keeping the interface credible. Since users act with motivations, we define a pool of tasks (jobs to be done) and assign tasks randomly to participants. We discuss limitations and choices made in this experimental design in Section~\ref{sec:limitations_risks}. @@ -218,7 +218,7 @@ Our web platform (developed in similar spirit to RecSim \parencite{ie_recsim_201 To speak to realism, user interviews reported that the platform architecture mirrored standard booking interfaces and reduced the cognitive load required to learn the system. One participant described the flow as ``intuitive'' and close to a ``normal'' transaction, suggesting observed behavior was primarily driven by pricing treatment rather than interface novelty. -The dynamic pricing mechanism elicited immediate behavioral adjustments. Participants were sensitive to price volatility: sudden boosts triggered urgency and faster booking attempts, while large listing-to-final discrepancies triggered deeper comparison behavior. This is comforting because the controlled setup still produces commercially relevant interaction data. +The dynamic pricing mechanism elicited immediate behavioral adjustments. Participants were sensitive to price volatility: sudden boosts triggered urgency and faster booking attempts, while large listing-to-final discrepancies triggered deeper comparison behavior. The responses match what one expects from live commerce: sharp reactions to volatility and to list--checkout gaps, which supports external validity despite the lab setting. \subsubsection{Design of Training Factorial Study} @@ -264,9 +264,9 @@ v4 & 64 (32 + 32) & us-central2-b & 32 Spot + 32 On-demand \\ For connections from Madrid, we prioritize the europe-west4 allocation for latency-sensitive runs with the benefit of having the most grouped chips within a single region. This regional grouping is important for the deployment of our Kubernetes cluster which cannot span multiple regions. All sweep metadata, model checkpoints, and reward traces are logged in Weights \& Biases. % TODO: cite this (from bib) Hardware specifications are from the official Google Cloud TPU documentation \parencite{noauthor_tpu_2026,noauthor_tpu_2025-1,noauthor_tpu_2025}. -Design of training processes: we build docker image with the fact in mind of different caching over layers in order to most speed up docker re-building and such we place the most volatile steps towards the end of the image building. What is means in practice is that any dependency installations are isolated so edits to source code do no trigger rebuilds. Only if we update our entry point of training a sweep, Docker will also rebuild the source-code copy stage. % TODO: cite Docker best practices on cache-efficient Dockerfile layering. +Training images follow Docker layer caching: dependency layers are separate from the copy of application source so routine code edits do not invalidate the entire build; only changes to the training entrypoint or dependencies force a full rebuild. -Due to the preemptive nature of the current demand of TPU chips we sttle for running our on demeaned as the primary source of compute. The on demand TPU pod of 32 chips spread across 4 virtual hosts creates a relatively unique parallelization setup. Despite our desire to use a traditional approach of clustering and perhaps deploying SLURM jobs of our sweep agent, the lack of predictability in provisioning each instance of a compute resource makes this an high friction layer we do not want to add. +TPU capacity is scarce and often preemptible, so we rely primarily on on-demand pods for workloads that must finish without interruption. A typical reservation is a 32-chip pod across four worker VMs; that layout already gives enough parallelism for our sweep driver without adding a separate cluster scheduler. We considered SLURM-style job arrays, but fluctuating provisioning times would have added operational overhead with little benefit for our workload, so orchestration stays in the container and Ray layer described below. \subsubsection{Interaction Schema} @@ -301,9 +301,7 @@ $\mathcal{A}_{\text{filter}}$ & \texttt{search}, \texttt{filter\_date}, \texttt{ \end{table} This partition enables the weight function $\omega$ from Eq.~\ref{eq:qhat} to assign category-specific signal strengths, with $\omega(\mathcal{A}_{\text{cart}}) > \omega(\mathcal{A}_{\text{dwell}}) > \omega(\mathcal{A}_{\text{nav}}) > \omega(\mathcal{A}_{\text{filter}})$ reflecting decreasing commitment. -It's important to acknowledge that this creates a very blatant assumption in the weighting, and we motivate the scale of each weight by the per-category observed divergence between each behavioral profile. -In the simulator baseline this order is encoded with a compact fixed scale: cart $=4.0$, dwell $=2.0$, nav $=1.0$, filter $=0.5$. Unknown actions are mapped by prefix heuristics to the nearest category. -We back this up by saying that each weight was assigned by observing an initial small dataset and computing KL divergence between each interaction type; the ones with the highest divergence receive a proportionately high weight in our demand estimation. From the order which we observe in divergences, we assign a multiple of 2 increase in weight ascending form the lowest weight of $0.5$ in rare filtering operations. +The ordering cart $>$ dwell $>$ nav $>$ filter is a deliberate simplification: we set it from early data by ranking categories by KL divergence between human and agent transition rows and then spacing weights in powers of two. The simulator encodes cart $=4.0$, dwell $=2.0$, nav $=1.0$, filter $=0.5$; unknown actions map by prefix to the nearest category. The metadata record $\mu$ varies by action type. For product views, $\mu$ contains the observed price $p_{\text{obs}}$ and product attributes. For dwell events, $\mu$ includes the element text and accumulated hover duration. This heterogeneous structure is captured via a schema-on-read approach in our Kafka ingestion pipeline, where events are validated against type-specific schemas before storage. @@ -320,9 +318,9 @@ To train a robust pricing learner, we need a simulator that can generate realist \subsubsection{Ground-Truth Distinguishability} Because sessions are collected under controlled experimental conditions where each actor is assigned a known type at the start of the trial, labels $Y_s \in \{H, A\}$ are available as ground truth rather than as the output of a heuristic classifier. We therefore estimate separate transition kernels directly from each labeled partition $\mathcal{D}_H$ and $\mathcal{D}_A$, treating the resulting $\hat{\mathcal{T}}_H$ and $\hat{\mathcal{T}}_A$ as the ground-truth behavioral profiles for each class. We then ask a direct methodological question: are the kernels distinguishable enough to justify downstream pricing control that depends on that distinguishability? -To answer this, we compute per-session KL divergence scores against both class-level centroids. For each session $s$ in either partition, we fit a session-level event transition kernel $\hat{\mathcal{T}}_s$ from that session's trajectory alone, then compute its average KL divergence to the human centroid ($\Delta_{H,s}$) and to the agent centroid ($\Delta_{A,s}$). The per-session distinguishability score is the gap $\Delta_{H,s} - \Delta_{A,s}$: a negative value indicates proximity to human behavior, a positive value indicates proximity to agent behavior. The reason behind KL divergence for profile analysis is grounded in its nature and tailored characteristics for probability distributions. +For each session $s$ we fit a session-level transition kernel $\hat{\mathcal{T}}_s$, then average KL divergence to the human centroid ($\Delta_{H,s}$) and to the agent centroid ($\Delta_{A,s}$). The distinguishability score is the gap $\Delta_{H,s} - \Delta_{A,s}$ (negative $\approx$ human-like, positive $\approx$ agent-like). KL is used because it compares full categorical rows, not single features. -The normality assumption cannot be made for KL divergence distributions, which are right-skewed and bounded below by zero, so we do not use a Student's $t$-test. Instead we apply a Mann-Whitney $U$ test \parencite{mann_test_1947} on the per-session gap scores between the two groups. The Mann-Whitney test is a rank-based nonparametric test that compares the stochastic ordering of two independent samples without distributional assumptions, making it appropriate for small samples drawn from skewed populations. We report $U$, the exact two-sided $p$-value, and group-level descriptive statistics for the gap scores. +Gap scores are skewed and nonnegative, so we test cohort differences with a Mann--Whitney $U$ test \parencite{mann_test_1947} rather than a $t$-test. We report $U$, the two-sided $p$-value, and descriptive statistics for each group. \begin{definition}[Kullback-Leibler Divergence for Transition Distributions] Let $P_e$ and $Q_e$ be categorical distributions over destination states following event $e$, derived from human and agent trajectories respectively. The KL divergence between these distributions is: @@ -331,7 +329,7 @@ Let $P_e$ and $Q_e$ be categorical distributions over destination states followi \end{equation} where $\mathcal{S}_e$ denotes the set of destination events that follow $e$ in the human trajectories. \end{definition} -The asymmetry of KL divergence is a point we leverage to natively create divergence from human behavior, to gather signal of the dissimilarity from human-like interactions. +We exploit KL asymmetry so that ``distance from human-like'' is explicit in the score, not only distance from agents. To obtain this statistic, we aggregate transitions by triggering event $e$ and treat normalized outgoing probabilities as categorical distributions $P_e$ (human) and $Q_e$ (agent). We intersect shared event labels, then accumulate log-ratio contributions over shared destinations. Large contributions, including near-zero $Q_e(k)$ cases, identify transitions where one actor class is difficult to mimic. @@ -382,27 +380,27 @@ Because contamination level $\alpha$ and demand shift are non-stationary online, From these two divergences we define the gap score: \begin{equation} -g(\tau') := \Delta_H(\tau') - \Delta_A(\tau'). +g(\tau') = \Delta_H(\tau') - \Delta_A(\tau'). \end{equation} Positive values indicate trajectories farther from the human centroid and closer to the agent centroid. We map this gap to a weak agent probability using a temperature-controlled logistic map: \begin{equation} -f(\tau') := P(Y=A\mid\tau') = \operatorname{softmax}(-\Delta_A,-\Delta_H)_A = \sigma\left(\frac{\Delta_H-\Delta_A}{T}\right), \quad T>0. +f(\tau') = P(Y=A\mid\tau') = \operatorname{softmax}(-\Delta_A,-\Delta_H)_A = \sigma\left(\frac{\Delta_H-\Delta_A}{T}\right), \quad T>0. \end{equation} The session-level control signal injected into pricing is then \begin{equation} -\hat{\alpha}(\tau') := f(\tau'). +\hat{\alpha}(\tau') = f(\tau'). \end{equation} This turns distinguishability into an operational control input in the engine. On a per-customer or use-case basis, a similar data collection and fitting process should be repeated to obtain domain-specific behavior kernels. -In implementation, we maintain an alternating game-history stack (our \textit{Limbo} stack) and execute it explicitly every epoch with exactly two transitions: first the platform publishes a price vector (leader move), then the market responds with trajectory-derived demand (follower move). +In implementation we keep an alternating game-history buffer and advance it each epoch with two transitions: the platform publishes a price vector (leader move), then the environment returns trajectory-derived demand (follower move). The codebase names this structure \textit{Limbo}; the appendix lists it under the same label for readers who inspect the repository. To avoid notation drift, we separate two COI objects used for different purposes: \begin{align} -\text{COI}_{\text{level}}(\pi) &:= \mathbb{E}[P]-\underline{p} \quad \text{(global reporting KPI)} \\ -\text{COI}_{\text{leak}}(p,\tau') &:= f(\tau')\cdot \text{InfoValue}(p,\tau') \quad \text{(local control penalty)} +\text{COI}_{\text{level}}(\pi) &= \mathbb{E}[P]-\underline{p} \quad \text{(global reporting KPI)} \\ +\text{COI}_{\text{leak}}(p,\tau') &= f(\tau')\cdot \text{InfoValue}(p,\tau') \quad \text{(local control penalty)} \end{align} where $\text{COI}_{\text{level}}$ is evaluated at policy level and $\text{COI}_{\text{leak}}$ is evaluated per observed quote during training. We connect local leakage to expected global erosion with the operational assumption \begin{equation} @@ -485,7 +483,7 @@ In practice, we parameterize this with a session-level leakage term: \end{equation} where $f(\tau')$ is the weak agent probability and $\text{InfoValue}$ is implemented either as a constant query-tax surrogate or as a revelation surrogate $-\log\pi(p\mid\tau')$. -To make the intuition of our $\max \min$ easier in connection to the COI term which we are subtracting, we introduce the strongest possible penalization and try to maximize only for the worst case scenario in which the leakage is extremely high and that negation sends a signal to pick the candidate of the hardest problem. +The inner minimization selects the contamination candidate that makes the penalized reward smallest, so the outer policy update faces the worst plausible leakage scenario inside the ambiguity set rather than an average case. For the baseline engine reported here, we intentionally use the constant query-tax surrogate to keep the mechanism minimal: \begin{equation} @@ -547,13 +545,13 @@ We now present the complete pricing mechanism that integrates the behavioral dis \end{algorithm} -The algorithm operates in discrete epochs indexed by $t$. At each epoch, the platform applies one discrete multiplicative price action, the environment samples a batch of sessions, and demand is recomputed from weighted events. Robustness is implemented as an inner minimization over a small local grid of contamination candidates around nominal $\alpha_0$, matching the current engine implementation. The history buffer $\mathcal{L}$ (what we are calling the ``Limbo'' stack in our implementation) enforces the alternating Stackelberg structure by preserving the temporal sequence of price publications and demand observations. +The algorithm operates in discrete epochs indexed by $t$. At each epoch, the platform applies one discrete multiplicative price action, the environment samples a batch of sessions, and demand is recomputed from weighted events. Robustness is implemented as an inner minimization over a small local grid of contamination candidates around nominal $\alpha_0$, matching the current engine implementation. The history buffer $\mathcal{L}$ enforces the alternating Stackelberg structure by preserving the temporal sequence of price publications and demand observations. %The defensive price update in Line 24 implements contamination-aware margin shrinkage: as estimated contamination $\hat{\alpha}_t$ rises, the margin $(p^{\mathrm{ref}} - c)$ is reduced by factor $\kappa\in[0,1]$, with projection $\Pi_{\mathcal{P}}$ ensuring feasibility. In subsequent experiments this heuristic rule is replaced by DR-RL policy $\pi^*$ from Eq.~\ref{eq:robust_policy}. \subsection{Parallelization Strategy} -To avoid preemption of compute mid-training we settle on using a v4 generation, 40 chip compute node with 5 parallel workers. The login node creates an orchestration node with Ray \parencite{moritz_ray_2018} and we distribute ray compute nodes per each other worker. +To reduce mid-job preemption we standardize on a TPU v4 allocation with 40 chips and five workers. A head process launches Ray \parencite{moritz_ray_2018} and attaches workers across the remaining hosts. \subsubsection{Computational Cost Analysis of the Simulation Step} The per-step cost of Algorithm~\ref{alg:phantom_loop_clean} is not uniform across its components. To inform hardware provisioning and to identify where algorithmic improvements are most impactful, we profile the hot path of the engine using Python's \texttt{cProfile} instrumentation over 20 environment steps under two configurations: a baseline with the robustness inner loop disabled ($K=1$, $\epsilon_\alpha=0$) and a standard robust setting ($K=5$, $\epsilon_\alpha=0.2$). Both runs use $M=10$ sessions per market call and $N=3$ products. diff --git a/paper/src/chapters/05-discussion.tex b/paper/src/chapters/05-discussion.tex index c926ad8..fa5ad55 100644 --- a/paper/src/chapters/05-discussion.tex +++ b/paper/src/chapters/05-discussion.tex @@ -4,18 +4,16 @@ \subsection{Transition to Agentic Market Microstructure} -Our analysis of the interaction dynamics between the platform and non-human actors suggests that the current static pricing models are insufficient for an agent-mediated economy. If we assume a transition toward a direct revelation mechanism, where actors must reveal their true valuation of a good through bidding dynamics, we inevitably introduce significant stochasticity into the pricing system. Unlike traditional e-commerce where prices are relatively sticky, such a mechanism implies a high volatility characteristic of financial equity markets (without the fungability however). - -However, ecommerce commodities differ fundamentally from financial securities: they possess a hard floor defined by unit economics and reservation prices. The market might react enthusiastically to an iPhone priced at \$1, such a transaction is not permissible. The platform must establish an initial valuation anchor ($P_{0}$) defined by the marginal cost plus a target margin, around which the market price is permitted to fluctuate. We float the introduction of GenAI Agents as Institutional Market Makers. As the arms race for greater autonomy of agnetic systems grows, the commercial viability of AI agents has the potential to disseminate into every-day users directly interacting with them rather than e-commerce platforms. This is also under the assumption of expected transactional capabilities being given to AI Agents. - +Our analysis of interaction dynamics between the platform and non-human actors suggests that static posted-price models are a weak match for an economy in which software agents mediate search and purchase. If one pushes toward direct-revelation or auction-like pricing, volatility rises: prices behave more like traded claims than like sticky retail quotes, though without the fungibility of securities. +E-commerce goods differ from financial assets in a hard way: unit economics and reservation values set a floor. The market might ``want'' an iPhone at \$1; the platform cannot honor that. Pricing therefore needs an anchor $P_{0}$ (cost plus target margin) around which offers may move. In that setting, large language model (LLM) agents resemble institutional liquidity providers: they quote, probe, and clear subsets of flow. As autonomy of agentic systems increases, end users may delegate browsing and checkout to assistants rather than to retailer sites directly, which shifts where demand signals originate. The scenario presumes agents eventually hold delegated payment authority; until then, our results bound a near-term reconnaissance-heavy regime. \subsection{Risk Assessment and Limitations} \label{sec:limitations_risks} -This technology does not come without a more bitter side, ethical concerns do arise from the idea of deploying black-box like solutions to set prices based on a behavioral attributes. Approaches like universal behavioral profile modeling (UBPM) used in recommendation systems is very broadly utilized. +Behavior-based pricing raises predictable ethics questions when models are opaque: a behavioral profile can become a basis for price discrimination or exclusion if deployed without governance. Universal behavioral profile modeling (UBPM) in recommendation already shows how fine-grained traces enable strong personalization; the same machinery applied to prices needs guardrails. -In our experimental setup we randomly assign each user to a platform and, within that platform, assign them to a task. Figure~\ref{fig:exp_design_tree} summarizes this design decision tree. +In our experiments participants are randomized to platform mode and task. Figure~\ref{fig:exp_design_tree} summarizes the assignment tree. \begin{figure}[ht] \centering @@ -26,8 +24,8 @@ In our experimental setup we randomly assign each user to a platform and, within \label{fig:exp_design_tree} \end{figure} -Although our participant sample size is somewhat low for humans, we do a one-to-one balance of human-to-agent experimental sessions. This way we are observing a uniform distribution of participation from each participating side. Our sample size of participants might look scarce, but each participant generates a rich amount of data, with a totality of 3,874 rows of data. +The human sample is small but each session is long-form; we balance human and agent sessions one-to-one so cohorts are comparable despite different population sizes. The row-level dataset still contains thousands of events. -With a system like this there is potential for strong drift given the rapid advance of agentic systems and user preference. Our intent behind adding the UX term into the reward shaping process was to further address the risk of degraded user experience. Looking deeper at the underlying methodology, reinforcement learning does not come without it's complications such as reward hacking and often the lack of intepretability which is quite critical in systems that have a strong impact on the revenue of a company. +Rapid change in agent capabilities and user expectations induces model drift; the UX term in reward shaping was included partly to penalize policies that sacrifice legitimate users for short-run revenue. Reinforcement learning adds its own risks---reward hacking and limited interpretability---which matter when policies touch live revenue; deployment would require monitoring and constraints beyond what we exercised here. % \subsection{Implications of Findings} Interpretation of results and altenrative scenarios with broader market implications. diff --git a/paper/src/chapters/06-conclusion.tex b/paper/src/chapters/06-conclusion.tex index 34b01c7..cb0d174 100644 --- a/paper/src/chapters/06-conclusion.tex +++ b/paper/src/chapters/06-conclusion.tex @@ -1,26 +1,22 @@ \section{Conclusion} -Our research has explored how reinforcement learning works within pricing systems and environments which are substantially disrupted by an adversarial participant. Our findings include the optimization for our newly introduced metrics. +This thesis examined reinforcement-learning policies for dynamic pricing when a fraction of traffic is orchestrated by non-human agents intent on extracting information before purchase. We introduced COI-oriented metrics, a behavioral distinguishability layer, and a distributionally robust training loop; empirical runs show where robustness helps and where it must be tuned. \subsection{Summary of contributions} -The contribution was not without the advice of many experienced experts in the field. We thank Marco Casalaina VP Products, Core AI and AI Futurist at Microsoft for the initial critical discussion on the topic of dynamic pricing systems and the spark which has lead to this work. Eugene Bykovets, PhD pointing out the parallels in blockchain systems and the complexity of anonymous interaction and understanding of intent. Importantly, the contributions of Alberto Martín Izquierdo, my academic advisor for the support over and for taking on the challenge of this ambitious work. Many breakthroughs were thanks to numerous discussions with my peers on the topics covered here. -A thanks to the head of innovation at Amadeus for insight into the industry split on the topic of collapsing margins. Finally we acknowledge the power and use of generative AI technologies for in depth research, rapid prototyping and surfacing of key topics and niches. - -Now we very explicitly mention what we contribute in this paper: \begin{itemize} - \item TPU-accelerated parallelization of the behavioral simulation and reinforcement learning pipeline, making large-scale factorial sweeps tractable. + \item TPU-accelerated parallelization of the behavioral simulation and reinforcement learning pipeline, making large factorial sweeps tractable. \item Formalization of non-human transaction orchestration in e-commerce as a distinct source of contamination in dynamic pricing systems. - \item Definition of the Cost of Information (COI) as a mechanism-level quantity for pricing power, together with a theorem showing its erosion under increasing agent saturation. - \item Design and implementation of a controlled e-commerce research platform, built on a hybrid Kappa-Lambda architecture, for collecting and replaying high-fidelity interaction trajectories. - \item Construction and empirical validation of a behavioral distinguishability framework that distinguishes human and agent sessions from interaction signals alone using transition kernels and KL-based divergence. - \item Development of a generative contamination mechanism that injects learned agent behavior into the pricing environment for controlled robustness experiments. - \item Translation of behavioral distinguishability into a defensive pricing mechanism through a distributionally robust reinforcement learning formulation of pricing under non-stationary contamination. - \item Empirical evidence that agent contamination reduces revenue and that robustness is condition-dependent, requiring explicit calibration rather than a one-size-fits-all penalty. - \item Release of a reusable public experimental artifact for reproducing and extending research on dynamic pricing under agent-mediated traffic. + \item Definition of the Cost of Information (COI) as a mechanism-level quantity for pricing power, together with a theorem on its erosion under increasing agent saturation. + \item Design and implementation of a controlled e-commerce research platform on a hybrid Kappa--Lambda architecture for collecting and replaying high-fidelity interaction trajectories. + \item Construction and empirical validation of a behavioral distinguishability framework that separates human and agent sessions from interaction signals alone using transition kernels and KL-based divergence. + \item A generative contamination mechanism that injects learned agent behavior into the pricing environment for controlled robustness experiments. + \item Translation of distinguishability scores into defensive pricing via distributionally robust reinforcement learning under non-stationary contamination. + \item Evidence that contamination depresses revenue and that robustness gains are regime-dependent, so penalties and radii need calibration rather than a single default. + \item Release of a public experimental artifact (code and dataset) for reproducing and extending work on agent-mediated traffic. \end{itemize} -\subsection{Future Works and Next Steps} +\subsection{Limitations and future work} -In our effort to tackle this work we initiated a set of constraints which we hope to relax in future iterations and hope that some of these will be addressed in industry. First of these constraints is the weighting of different actions within the demand estimation, which we would ideally find through learned methodology. Next, assumption of perfect alternating turns between the platform and the market calls for a fixed length non-strictly alternating state definition with a history of actions to possibly allow for the development of multi agentic or multi platform simulation. In our simulation we also make assumptions of non-perishable supply of items, which creates the biggest sim-to-real gap in our system. We also would like to further remove intra-session stationary nature of the contamination parameter to further create high-fidelity non-stationarity within a single evaluation window. +Several constraints are intentional and could be relaxed later. Action weights in the demand proxy are hand-set; learning them from data is an obvious next step. The Stackelberg interface assumes a clean alternation between platform move and market response; richer histories (multi-agent, multi-platform) would need a less rigid state definition. Non-perishable catalog supply in the simulator widens the sim-to-real gap for inventory-constrained domains. Within-session contamination is modeled as stable; time-varying $\alpha$ inside a session would better match some attack patterns. -For deployment of this it is advised to collect a higher sample size of human baselines and to complement this with the simulated agentic sessions and to mind the matrix scaling for very large catalog sizes. +Before any deployment, human baselines should grow beyond the convenience sample used here, and catalog scaling laws should be re-checked when transition matrices grow with SKU count. For the deployment of this methodology presented in our work. diff --git a/paper/src/chapters/acknowledgements.tex b/paper/src/chapters/acknowledgements.tex index 160bad2..d7529d7 100644 --- a/paper/src/chapters/acknowledgements.tex +++ b/paper/src/chapters/acknowledgements.tex @@ -1,3 +1,7 @@ -\section{Acknowledgements} +\section*{Acknowledgements} -Eugene Bykovets, PhD - ETH +This research was supported by the TPU Research Cloud program, which provided access to Google Cloud Tensor Processing Unit (TPU) accelerators, including TPU v4, v5e, and v6e. + +I am grateful to Marco Casalaina (VP of Product, Core AI, Microsoft) for an early conversation on dynamic pricing that helped frame the problem. Eugene Bykovets (Ph.D.) pointed out useful parallels with blockchain systems and the difficulty of inferring intent under pseudonymity. Alberto Mart\'{i}n Izquierdo supervised this work and accepted an unusually wide brief. Several peers contributed through discussion of the topics covered here. The head of innovation at Amadeus offered industry perspective on margin compression under automation. + +Generative tools were used for literature search, prototyping, and drafting support; all claims, experiments, and final wording remain the author's responsibility. diff --git a/paper/src/chapters/mdp_agent.pdf b/paper/src/chapters/mdp_agent.pdf index 7ec2159365eff0dcb20d6163f3c9d72ce4f61b46..a11184db8673c5f5c62f4435f634fbcea6b7adb9 100644 GIT binary patch delta 284 zcmV+%0ptF(RkT&GgeZUWE6&SUWzZ%ytyYQ;D##cEslJUpgcxdIG$l#L{P#<$b&NdS zk9%^?;WALdN)(jjU_vXq6)XvNwHp_&^Sd2A?C^nxXSxWW630 z2W!rQR2GR8+8VHwEmMx<;(hN{SvJzvivFUdOO0)3v)eaT39o-mw-a6=*{yP#mz2|r zE;%ju3(cP?olPcxvG5eEqy8YQiaBDk5xlFj78~;u;?nT)C9l9rRy|^OJ_sE6L$SAJ z=uy{ki1W>5q!T&@YDMSz=q9fdeucM#nx<{o+Np0XRkPnj7*a>xC}##MmBakuc??47 iZ{c0I!@q)C;l&S^Y482Q4es^A(k}xmZ&0(5DI)=oUyKj{ delta 284 zcmV+%0ptF(RkT&GgeZUeE6&T9Wnhz7Yn9@I3Npr^sBdErAzo@>G$l#L{P#;*+cENV zKkmsnhs(hto?*!%0lwrBUtzJzqM|@@xwnL*loi*gRGVRqOECNh?? zT!@88^A{$b8H-1gzgT$k))`7ljMW^E~K%YAB6ItCEMHY+6^X!Gn i4dJc7!@q)i>EsWWt{(mT4IcIU(k}u9Z%?z4DI)=V!;Vb= diff --git a/paper/src/chapters/mdp_human.pdf b/paper/src/chapters/mdp_human.pdf index ebe5c34a7095a60196d73e820ef1a1bde5179fe7..5723a31e6de0b08b0774c1b41263e667c3454a19 100644 GIT binary patch delta 291 zcmV+;0o?wvU9nxTdn|uZYr-%Th2Qfl&dXS3V3XL^D#ZsY$QT1r-^Lz78fsuPB}vEp z_e-jEj6B_sdvea^#Hdcna20e-Kv195LAl-c_T;*8GIHG(5g4O7J3IK4N!1 z2pqD9VsFjRqpIQ%=bOt&Cv*zbiq7@XOlWkoEfZC p4vUB9DF~&%g?AR=4*v?)!iz7{-us0c-0OvuaF%?s~iE4z3Q{W2RJB>_#9DV zhR%bL&1O^_tT_)-StL?uYrs-gr3I3U_q|(Z*+^R}`iqt>HMW0UEND>PI!f6 zx8{_Ww4fE|IWMXw${#6ROeTM^@DQw{{vfQ1IbyOAys5JmTk{j*((w3mS%H_!)f~I? zLEuo#ioG>MkGhUSoNq28ozN*zD>~OlH+h}#3%niFG&Nysr@pmR$Ne_KkUH|Ca%Qkn pIpnkFDF~&%g?AR=4*v=^!iz7{-us0c-0Ov Date: Thu, 9 Apr 2026 10:09:18 +0200 Subject: [PATCH 23/44] chore: cleaning figures --- .../final/final_focus_headline_summary.json | 2 +- .../final/plots/final_focus_coi_by_alpha.pdf | Bin 24919 -> 24885 bytes .../final_focus_coi_preservation_grid.pdf | Bin 21304 -> 21304 bytes .../plots/final_focus_revenue_by_alpha.pdf | Bin 17518 -> 17490 bytes .../final/plots/final_focus_revenue_delta.pdf | Bin 19801 -> 19788 bytes .../final/plots/final_focus_risk_deltas.pdf | Bin 19550 -> 19531 bytes .../figures/results/process_final_sweeps.py | 4 ---- 7 files changed, 1 insertion(+), 5 deletions(-) diff --git a/paper/src/chapters/figures/results/generated/final/final_focus_headline_summary.json b/paper/src/chapters/figures/results/generated/final/final_focus_headline_summary.json index a7b1fd1..6e21166 100644 --- a/paper/src/chapters/figures/results/generated/final/final_focus_headline_summary.json +++ b/paper/src/chapters/figures/results/generated/final/final_focus_headline_summary.json @@ -3,7 +3,7 @@ "focus_cohort": "max_alpha_coverage", "focus_sweep_id": "i88nw811", "focus_run_count": 768, - "git_commit": "105b01497600fd31ec07ae49271e680517321cba", + "git_commit": "ace52e8e14e0f7fa96ab5eb113c0c898b0bce1a0", "alpha_cells": 11, "alpha_min": 0.0, "alpha_max": 1.0, diff --git a/paper/src/chapters/figures/results/generated/final/plots/final_focus_coi_by_alpha.pdf b/paper/src/chapters/figures/results/generated/final/plots/final_focus_coi_by_alpha.pdf index b47a4fd8a175372b94fc20f6e8e179d5444e99f5..653457672599f0505600914f7d0dcb99989afb7f 100644 GIT binary patch delta 8933 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�gpnq&ktu^J-4$N14H8d%12{aP88HFNfG%vap9H*{Sk@ z$cGkPg~rglTA!9+g(L;M*ewUUn#QjmFK6z@i}tZeAs)0zJWDu%4Q2V|Yn?B6NIbqK zS*z;AxHf3uHY82WFzR#z*l815&J??2*4bMS*XEf|gNTxCnc6JMZhLZ>kPnDEY6lXl zRTkBOweJpqtevymR;eh@5UtRKW$J-@%maJ$RT-|hzTKde;%7FpEW{YPiIe65thsb77!myzSG;mIE^Uf-#73<`7vX%`+* zJI=ngc868dxA~p5`E?;v_2WVmMXn+pFY$%sLhmibE>APRHa3-Ie)ar06#EO)xHnc_ zCuTSPWXNxm0B7EA&?k3K`1I<}J;_>uZtioR>)o%&-_x1b&pUqI|8Bp%8t8c5>UD8k zxz-Bzdq6*0XV>rEK?R%+3cNdPC$f~zeTx%YIbgAa=L>1PU}7LfmLza})P4iLj-maA zcQ@d{oQhrb35^ZS>oBA~Pmprap)GR4{a}TPt_7Xt3g#r&?xt&fxpgskgsRMT$q9Ah zhGKVU+1*kKIxkgv8X$%>;+t8w%rv(@Z~qc3aVTG|T8e0O;%*}JtKN3!cSu;O@RWBx zKh2_SW^W2bUiv8v5pKU4H12xsp0Kn|t>n`x;(8w{KhmmT^< z6Zq|r7&^fV2~GS>umAa4Z9oJX9Y0ZZroS^AI?`F@KsFeh#cb=1WxSvM7{&DmiqwBT zfSWuIiVSISyj)sORdndsl>GyAREl8-AVOUb&Q}h0N4kE;1L)LVeVhyHt-YL7d$&*F z8MlRg*QdMpXJ6KS@13pmJZD<=XVt z8s`o2eyq}{<*ZOk@PRLv{6FqDflUsd+eV+U^ZQC(5 zh%_6^;@eEy(afEjXCS-4oo4*1mlFf907Q>Erg@)l-FcPtcMVKzZ00T#_!e^MDE^52 zExIxJuCd3S_&P20*)c7#F499@^5+xjti;{$rfjZNa>_6P0~H4TK{dZDmtR$+h9R2{Q?j7L-!|0!nj$Rw`r@i8Ahn{yW!-)7h?Uw@jBTVri0+i8vYB!1 zc;XcOqnJXXc+pvN&Z{SD%$+&UB%sL3Q=AA-rWfo5p|PdwJbU>N&M;+@*foa##F0NUYkF_mIToJH#tVF-5LY_LaHa%ky_?SjXYf_aI0Aj@u_k zBfoMkea{4MudY=L?-Va0HKK^s1)q*n<*GLXO1J$oc0Te>SY~q7OnBY;^WbR(w)h?M zR;XE4B0no58Wi?Lk@ZXj7Xp9-;7|Y%kSJrQEDnYVu>k&kSR7dJ{__F?f#Kjs8U*|} zPAHMpn4DVZ|C2!&Ae?w@2uFv&g%ZI=oJfc;^if9P$Lqj=M;ZVQ5&C;A01koz{>le{ z!9l`*X+QuJ2>Vw<{#SVb7!>%Z6aXCjcR~n00<2H3jzYdVSjJ%4;b>_`Tp<$ ze+-Ag{A_)#1P2nPKf2LdA>LqJ0CM}c5~@FNHq z0)+gX5G?#?7=REU{E-U~$e)|Me@jRHzv%)(gyH{f&3~MMz=a=a&_84NHyHYOj(||1 z|J~#Nb`=VT{g2lD>pxKF@z4N;ppgF&_@6i-_#?0|0P@H!VbK3agTNoPH{uecv-3b5y delta 9024 zcmZvBbzD^YyEWaZbPwGzFvEc4&`1kNmvnc-kON9cgQOrKozl`GAfeLDP)c{#^_=^@ z_nzP3?!We4-?i6zp7lJR{m=f?gFM}XTvLOrX&V-TkAc>;aj~(VsKaNX`qE1nK&^;T z`n65e)Ve+TaHh@#c7HhLvLtbP6r=t&?{4eR|7LnVkKpK%=lTQY?aywbWg;Z!7eIT|jKr}gpk z)5ZIxeE(s>uL@+<`FZl?wG*#0*R(wdw=UOuk2>QI-d0O0%}sNd-v1&}tBg-{SFK+W zW;xY#9~P&F-BKl8l0DoxT#$dwg1KF;k&oRnPf5^RLtv4RS3F-QTAjcwu{hICNa8pl#ZEr zQTC~c8L~lAPG(m^2b3VB2%{@f-~p=D!k?6_xc>#NONy(myDd`dSI`70$5PKi}p&Yc$@s`el%t!`H-=Sf=Yko|}wXBqY2gooy zQ&vd}jnj`&889D~K$7|4YBUIf`@Z>Xl@Ee5*RDR~84U5rAxevQTlzKB@}u;3PXJR! zPN{Ds=}I+qk0uQBx~J?)W;uoB+gS&CrYK9EjKlC`e78kp4Q-$SGRiM}yBrc-k2uvu zP7PY}T(^T{m4xN#iehskrfD+!sF^HjKQi~^8*$8vo=pr@)M67-Zd&e~zQ}MojI_kE zkIi%WS97eXI$ZkQ z1P2yJ>gyyj1N=Tq%pljzGHtJ=Mw>~9PuG*52_QqPO)$XOA@LgP%oYS}Vl6hk<|yOd z?~YhSNl^I&F2+?Lx+DZ%uXFYzAhGsRe9QTvc3F4yrs^HtBTU(o%J&3*M=zAKAkS&OL`AHM%84f7J zxu;{hqTUnn`C`tK`!Yu3aQcOB_#HECxtYu54rSzY%FuU-Zy}`K*($zpH?r6DJuPvW z?!>tUd7J=9_f6Q!cf z$Of5Nhd4#x)IC?udJIv%U)usQ0}5L(M~I1g|CWlq268t1GNHcPP^fgW??RqNO<>T7#j3fKQ6$8iO3jK7 zebeO}FDx`?_{>@QbW(^C@5Ovbc z=M&=XI8B_RkS`r0B=PsSxC4-ff%1p0`aFm9G_6%LE}EIMZcc-Gj@SE*JFUu zCJ8}3^?ASG6_JC5By0w_pfYtXEp+IT*bQY=A4-Z2CnPom>UGub$l@_m@&}?gbbnB! zL9RBPw+W2;ozxArKh4z{RU>Bak-Kzy<0X|G)~7KL^@%z*x%(L|E4_6v+@&NmVnmJe zioa_fTj95@Y_gOPJ~3;M`;aH^;BH|OAy>xPK$9d@7`ak@5{wWK1E692=Fja5pOc$4 zlkNaJ(TSl}lh+|?$goT=HR8L?0Eirh>lf>(RI^0E^w3xQ5g{*@SVCAM@=P47m5Z$eAM!hIz)^d?m5 zpr&_@f(NPE42B_q1BKl(0S1T((z$w2kbCin_uQYwk?F2ati-%4(Z;|{ltzZWd|i@E zxY35RCMT_qo=gbf-!D!gY_pT_EoK^(eZs7efQy2LcnVv<2uELWbu$jcazR07PwYma z846h<%<)bjd}UsPMDxZ)EIfoPh5#Emk#|+Cep6c15tf)}7LgvcO{D@Jp0MvBvK$cI zWuOLfJ&nI;+YLPY5;vd+_^DmOND{GAQ>4txh!@PsXoE!#!^Kub)bQXY5jz9*@U%e? zof=y*>B2$C2f=D46Y2s#Y^Njw;tUeRaLO=9?I*zU%N|;5j&mknK`5!^pVZ!Uam33p z4ibW3e;&5F4T;qx`c^})af*JU48j@SF=qqDNQNn1AH|u$2aaR-O(*~md_JOfl#?>7fU3 zoRFYzv-*hjmIwhrs#`=`|ESqksLY1;W(4N?y%mMPmNb@#F1Jm3{cWIQr3`OGZ+4FY z8|~95HYqFxW0Lr0ZpEZSHhi)%4M4)eArf}-{ZKpqQGYs9h7lIjIm}#369CLcJr6-D zpe7K^Irno1&=fmiwoE3Zc5RwP5al?rHJzCVwj=;L%&cD>FJqBs$XwXBh5EB?vB0=P z$!IcuA-%ZDsPM=+8i3HhBDA3g`TZ%>`#|{c0XH0PDp+-R+N1GBPG6$+*HIR4EBSiDh zmnhVA#?cZ~94tk96H_x-wcP>tbFjv6WA3;zA;mWukX1}vCH*yMVQQHZ<|=hDHp9Cx ziu!qP*N@hj8iSE<{34o`c54fv*$a)~PCA0KqS`l{TXm{#Ce97Z;7lh>{emkwG!TDp zPmT0XJI|h;!AQLZK8`hY{HT)%Ew*`runjsqHp)iZOd<+xW&1cwti(+DFEE^Kp=?Wv zVaInptIR#pz;_^Q!;-EgRl#S2L=L>@QNdXgqJu;@f@wJns)B!rVco-Kq~sgC*udxM zvtdU`h;`^L&~&(+QjIr4m~WE@XOt^2p^NxMmSGn`MKlIo6fIPBR1Jr2)-}o8e1fS2 z;|EKx5?sLMM9Vcv4&X%x3oQB|FosMpZ8fVyXW1*-wr9hj!3|HwH=k5hBjXq&xnTNY z2AQDXLb6fL!Bdab(EpO~!Wr@!VcDDMNGVpCDXv}zpa^X7mUaB%YMb#HXEhE{g76H& zYAZh^=&VeCHVFTYH;TbS=L&ErqupCN4Y^}>evVaoy z7MG2j6P1ZTSd%PQ|58&rfl}RkfhRjqPX`ZOB5{U0J1}~nRUZp^tnIM-qS&2XhQRf( zXRy}zOhcYs(H74JHaw{1tu6cXx&ALz=Dv?f?1y1eo(P6M+2=${#3|g_u`}8vaVm%7 zqArX~);e-ZnJiT&taH*D`r4nmYesR)wkkdn<@ip+friovgJ#H>m^u~X*2m^r`YD=B zc;<-pUa4~JltJhxM8%9}svKns@TWj_gq}889CJsb+BZ%!*!yb00)mGe&r3E%pGTfR z!?IUjOD<*578!yYi^~Qe(~%36QQoPEjG)jVjXQPqqa`;&1=}oj>CxHEi#h`UU>&(D z32BEvRA_pEdnAS4!V@)lJkA)D15$;Q$`Hpx6m2rA4IwF(GFNvC9ep89yC@wy^YEI! z!d7-BqGOmGnVMRJzKWGRLp=iw&BCHpowlSk_QwbDwCw0pYZj9gF#4~e#HY#I1u7Hr z*~+ttI|4I0#w3uRRjg?tRpYz!4Pr#x&0;c4Y-E#iI<;|R3Mukxv9mu_An|j1wK?Y{ zt?W;C2N;w8ayKTKqewhsuL#}h9Dtr2s^IBdc}}r=Ji9$g(y`3@qVJFbO_~C zm|1yEL+J9`Moa!j~vlkXRnq>Ul{4gcR6~BQ_%5(2;7b^$qiqG}#bpueU6%dM6 z1L$C$soanp&mBch@|CiwMyYIu*_U&yDS|8zYzuPtDIZo$EETINYKHJfl9>XX9Mn{A8y ztH2ghVUSdZESt!-s(_tFF|8KB9C1sLUO{F5@^@I1kHwDLr0kJ(p*ra~mPgF2a)ayo z5b27?qo z0yEBi%Qsm|2})#Y#crEK?p-|J8L7DjLm$jex*Q;D`!zXngiPf*j;%C@Jvr<=;DamW zV-@9oN^d?KUp$+I&oiU(w!b$Fh!ur5Vh>0#mzXkC_egpA-TFV9V5F(e!J_PADOO^i zeR6ROG(@$sVfBpJi+z@CKYTdWEGRuWCiBo``A)G&a9^dpwt{y3hc|C%lD*AB3sG_H zCWQ}Olz3Q;*{}Ch-;8m+?_+gQ94@;aY9ULb%_r%*da+;3mO8=xoqy=BNKeCle$KgZ z=0xEM^tkBLUm6T>*I;v9he4`ZBCInEVx|{*XOejm9jd}w1T23~FTkhu9kQlX^fKl% zn=bkWw}=KxjZe5yNNd6)sRK;n#WClLez|2XF~$+v=8;Yl_M-LG9NV^iL_x-`s+)Ua1~T9o~EIe(T4m8 zs1wIEd_)ZFO|u7;8q1Ej7mu*)qPViIGz{*97{6}EAKFC;BtuI${@uU}FoU8$Y5dxX z78VRDZhaEQgOFk$hTTJj0-tL_Ej#HPL1MBk&F97rJ>%hpW+zz!(Z{2f6ABC8?mG>!5ky@}5~5 z{HTvpztm0*XMp5JHu8S5nkFF2#9P)fUrBg9B6B(Mju~A)vy;-b6ztk8wKH@zL1RaZ z*O0N%sx46~F{w+GOz_kJ7T>%Cv^IUixb(p;gU^^a%G)5+wcL|ft=@F{3l`HsV{t_J z2PMjD4f)?Y&VBkbu)j(Uz1)sQZ zsb6?0MJoJY#S5fOhTth=ZtvvoI)+iVrm8oImU@YiVziZU{+M&>hmDY%zkc;hciUTT zzL0&uc7fb4v{%W>0kye+|0vffxeBh*+*7hsYdHAMHGU%;2Xxfz@77T;Vlbfb!p#(! zZ3!-i+F?;Or{to0<3;Md8ZBMXv(I_lC*yS28bTPAzoj5PO?vxU{nj#XnF`G)iY0dk z9yY2o<=uE6j?I&K2s7@4@}WCA>?J=BkZ}3T!;`Qb?m()q|N>x2GeO^1zf?Gta*`gN@5c3}Ij29mDAjF74IX)Ns$uk~}{UFwmw{3lz>J~c6uc!k zB!yURXX_@P&{`KOnV3up);~C)K;EJH8_A|DUu9VRwB{PCbWSfh3(10J)bnL?7R9P9 zU)CMCvq6;t*(H_+uM>}%G_9oaaI+*Gl9xKxt2tlu!dxiSRuSsmt9f|zJ#z_f8D9GP zM#cc@(fg7nSN(fHFJHYu2=&$2ic>?mE1HD9>bJLHM+%ThkXB^oo!VJ?x_Pl*_t{(d-|@+3omPTffe#jLPlR@ za-okcJhu^!aT~#I)0rq$QXcxVOf=1_4 zcS*PL3X<4KX67lmM2-11Z=2!zeiV@cg}D#yLQoP}CAZ^+R^ts@*HwW^+I|O)n?Hur z_@WCkK}=J*_P!|I3N))>hiCqba+S&O#h7W6W+HIb>|JMW@4^$4iHI%d6ZT&b)Dz&v zy}W)HmuWZL|JDg0M=(w@Ys_m8#pq}x)|vq>B4fmJ&k@7+JNbVz5RrWAiYm*41{EHKhN=^i)vg&B>L`?!0K>% zgP+exEJVwx&3d(}h*%-puFi9MhH6~P7q<;Ft#n2ZKiw zCH5N8PiLQt#6zQ(OAD$>U4oT6A^RpQ2rBr`rv-k*p{l?T$EC^U-JY`Ihe2z_Yz~5fqpi!jeg#f z((|Ly$!`|JMf&?w*kROKr&~DW1j9}AUe_la!Tt7OmDJ*pREyHu!<6QNyrK!e`DTp3 zGd+_K_A|`{rnP&1Iz2Dwo1Qt4{H=E1Y`WB~D|+NErwU;l@r-C&+i#5&^eD*n5QzTB4^SPzn-Rj-7oQ+AJ^7kvg`zlk*v}IzQ zqCIk#DN|c-NG-2Zdkg0LZq_jd5p^`E?6FZXDxPim^IeiQl2&Ddo}p>8r`Ii zP(e3-Gx1wC7j zxO{)Td3klw5dPK@I~}vDi>ZFS_C_=VKd2ZW)ANq96nvY>pXoyueYe$KU&w$S=>B$u#LeXVLeSW~hjjV=N-XY!vaIP>(O zq~?v5`^x*|XCAw0Ocd6?eArs>gKO8lxtw81^Oe2|?iJP}eRi64gS3NZrc+~XP?pAF z3&g(R9r&^+X};Pw@sHtr(i#?CHi9NQ`}4QTyP>f=*A-}WGc$EIxrv2a$3fDE7SnAX zLYF0a$(uVE-Lm-Y31V?9nDR?!tZ$}2rDL)6($TiUSxi_^^@`g?=DqJ-F5LrfH|)SU zcyBI_ugL%C<<-JvSu#0Hp>L$^?5$}*rytW*Kj~Ul6U1d)xGP{Yf%=P=ijw?zd}SA zzr-7V%J&~SK>5b{>#YZ{RP`KJza>ZqI4%_r`I?z`>r2`CJ`=k6@=m5JQu+muG?iy^lZwINuvzgmN;2=d9eyS6@RYzx?3G{ZuLR-; z%ix2>Wcpv}CZPCNaDr7ocfr$@%nuwt`0hOv=dTufojPwa;1c`QgfC$}3es1L3fW7W zD{VQfOHWA^Bn?$*ya&{SvzuU;W=z@Zd+M-j(tzz-(YQf*7r*0^SVC{YJ4f&77uDUE>CPy&SP}(R9SS;XfB%Zik)hU(Q~@?(ShcKb=uyM` zBH(`u0{MkOkHX>y{&#c#&;tVb1^(UKA7kNv>izNO=NI@>_TSa~<7EK>Aowu`;{Q9r z|KNckg8%CHPgVktct8O#_z_PK_Sgg{417cs6cBi1A}AyT`M02;@S~Lx5CQ@pr6BZY z7yo`2_P;wQAOr#aGn&7X0rB%c#(=_)iup6SMM5f%}GJ=#pjpJn)WtdPf>E&u^R9(4?X2tG0q w0fHW5AkhD71pGgr00EId&i^(R5fT131{4&6@F#DWDPRkV;IOdBsmtU17xlOXhyVZp diff --git a/paper/src/chapters/figures/results/generated/final/plots/final_focus_coi_preservation_grid.pdf b/paper/src/chapters/figures/results/generated/final/plots/final_focus_coi_preservation_grid.pdf index c02bdf9b48cc9e1989c5681861bc3284b266daa9..4b09a6b28a758250a3f866e2b3ade679eb90eeec 100644 GIT binary patch delta 24 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 AF8}}l delta 2868 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 TK-vxqG8%vZw6gL+D>C|jAvE54 delta 3282 zcmZXTc|28lAIB|&+bkF(g_xhag^7+1h-(O1%>rf19emE=C;<&$9VDR2l z`Os%`*nZ}Hq~SS*SBh6d2RM{F!2`O1;t!OdMD|vsWU2_*+o@b|S}2VbE1EXUE|Hl1 z?NVZnxA#|#a?gITVEwBuLBffP75uuMwHx<4MsbzD1l7K=9#d%EmrQVdFR0(sKi|uV zX-Ny#Tls_J%`5Ceg;q*yq^BP7m{H{d_8k*2IjGr^F6W?|^t?-6LgUGL3({g}oHF}t zJuqd-HZ0(91yA$6{>V+OO95k_WSgw&H;5ALibkHc5h>|TGD7b@jNq_Rx(hYFx}8W1 zDT9?Fw}TG({`RlEfca2^lMzPF#?!n3A2|${Q(x4W9}-5C--aS%MYiJujrS>g8CsPS z%YHYD$G^%eynOvnDW$p9@~!h?3Kaxv0HB!hvqk35;#gL#)o#06Ts(zU z;7BQ#=gqQj`6ZXky(-zeMl(WRcBT~E*`%tQhVa#wUU}zhy5b>bpA%mQ7~E^#{~qb^ zS+6E&ppBD8L(YeK$`1(DvCan$|1`iGA`Rs-P{M0p$&5HUV%{dDvpGEHgLspFttlWj zlDnx_-3Cl(1n=`HnCB?|7Bxay@YR(n8Br`jF8eTgQ644Nyvvk1bU+chui`5YneNqq zaJ?+|>7tvemXacUMni1&G7<>m1_n#cD1k%N{o76A&DK_%_IrmsG&NJK za&YT-hn=6A4X%p2BU{!s2k0R>tMbHVC{5Jcgi`H#{r4_DC)=6$-J@bujJ&9(kMi2J zW@ZLVlb2@UZNnn!v>3^XJQH8deeVHpSii^45(h6Y;v_k%*}D*x$C(gYIVA@b7h-R16oTa^-;{795Uu*eG@3 zVv3V7>xYdMonnZ=Zy(huR+q@R7B^w53=85i`X2Fo{Ke@&Gr0vLo#V;tItp!SM$&@`(DDDO@mxtn%X^Jc8{wFqwq?{zj}(#&rLjK^Dj zFC|lxlGQy@H~7Vh_~P!fUbl931Wy>AKyf*(@FywDHS(*+UJc4bE!Ebgv$YlYCD&VJ zoi2+1vL@D_HB=ks^P)p-RB9Fa+4cQXo;poWCra-F`*$_|JX*iGIR`#roQYUB$!%lx z`n7c2psYwIPb$0i!t)#Zxf1>5tRUrblWUyNvj;^3M`Zy`wBc4U_kVI#&J7hU(hVJ1 z#6<@k=?w0W1h1)K51MPg*^NRnIoIaN1=Z;>_DRJ0Kwo77L+DQVp1sxxNU^=$568iR z%-Qs!CRg4z?0E({cvt%RuowhRE`GeKsfX}-42-XeCrL^_&VBHEUy@+?dQ44J~*fr<3!v^w!%eyFIc457Rj%O6J*HD(F9dzSj}?u&=j4 zYmoD0_n~vGW%UlxPix;~9$E6HDnsUG+4alQm&z;5E6wRW8&E*_+mGIEa^6SXpH~_? z)VIt8_ZDwdsVO0=#y;h0m6n-upymec*lTWjh>9;1?MbDW)Gsqc^`H)vM_S#<}#-heE z<9RH@@>rOPLUpDbPnQGby=!LZSH`C4czc)q;{A_*43yFbFHZDuMse$C4mDs91vdRz z#ub_aDKh1!zC>rWYd`{u^}ZIN25Lfv9c=q$vc}02+lDRKKCU}2PK-_d;dy*dX7Pm@ zwk|cvBo4)O^sQThx4Q42Q*YN`(f#0QI#{4d|M1Sl+yT|fC=^_bQ`bnBFgiH# zbxHE^8rPJ*YFN7fs_}rZNM1}#SMkczaes9gmhQU~FQ=+NSARArjX!j>UB~8PmD?O1 z%Y^!mY2yjR6S&4okv;;a@0vS4e;)diRrbkVO1-00;nSCy7Y|F*ulp1xRoW zi-v1>AUsc7g^h#@pV5|u2v}GI4iB$H1Tjo;Xbcw5oQuYQSms0^zhsI4sNzi9m$mCy|IhQr+PXpaGECA%Mn`m>UCVJf0affF^;= zje(t;gT(T?v3elVHPP0OEHI`um|{ zuo#$Y3=y_m02Y8fF#wB2e=qoVJuIHcyfXlCXRZGk*vXM*8_s>2hyVdJ;?N;2eG1~g Dk`VLC diff --git a/paper/src/chapters/figures/results/process_final_sweeps.py b/paper/src/chapters/figures/results/process_final_sweeps.py index 550eb8a..57254c5 100644 --- a/paper/src/chapters/figures/results/process_final_sweeps.py +++ b/paper/src/chapters/figures/results/process_final_sweeps.py @@ -332,7 +332,6 @@ def _plot_focus_revenue_by_alpha(alpha_mode: pd.DataFrame, out_path: Path) -> Pa color=color, label=label, ) - ax.axvline(0.7, color="#666666", linewidth=1.0, linestyle="--") ax.set_xlabel(r"Contamination $\alpha$") ax.set_ylabel("Mean episode revenue") ax.set_title("Final Cohort Revenue Curves") @@ -392,7 +391,6 @@ def _plot_focus_coi_by_alpha(alpha_mode: pd.DataFrame, out_path: Path) -> Path: label="Gap", ) - ax.axvline(0.7, color="#666666", linewidth=1.0, linestyle="--") ax.set_xlabel(r"Contamination $\alpha$") ax.set_ylabel("Mean COI level") ax.set_title("Final Cohort COI Curves") @@ -489,7 +487,6 @@ def _plot_focus_revenue_delta(alpha_deltas: pd.DataFrame, out_path: Path) -> Pat ax.plot(x, y, marker="o", linewidth=2.0, markersize=4, color="#C44E52") ax.fill_between(x, y, 0.0, color="#C44E52", alpha=0.12) ax.axhline(0.0, color="#444444", linewidth=1.0, linestyle="--") - ax.axvline(0.7, color="#666666", linewidth=1.0, linestyle="--") high = alpha_deltas[alpha_deltas["alpha"] >= 0.7] if not high.empty: best = high.reindex( @@ -537,7 +534,6 @@ def _plot_focus_risk_deltas(alpha_deltas: pd.DataFrame, out_path: Path) -> Path: label="Volatility delta", ) ax.axhline(0.0, color="#444444", linewidth=1.0, linestyle="--") - ax.axvline(0.7, color="#666666", linewidth=1.0, linestyle="--") ax.set_xlabel(r"Contamination $\alpha$") ax.set_ylabel("Defended minus baseline") ax.set_title("Leakage and Stability Deltas (Final Cohort)") From 02328b20f2917650a46bdff993b8dd4fe16cc37f Mon Sep 17 00:00:00 2001 From: Daniel Rosel Date: Thu, 9 Apr 2026 10:17:53 +0200 Subject: [PATCH 24/44] feat: adding clarity and rewording --- paper/src/chapters/03-methodology.tex | 21 +++++++++------------ paper/src/chapters/04-results.tex | 4 ++-- paper/src/chapters/05-discussion.tex | 2 +- paper/src/main-genpop.tex | 9 +++++++++ paper/src/main.tex | 14 ++++++++++++++ paper/src/mirrors/genpop/03-methodology.tex | 4 ++-- 6 files changed, 37 insertions(+), 17 deletions(-) diff --git a/paper/src/chapters/03-methodology.tex b/paper/src/chapters/03-methodology.tex index e98e27f..fce3c71 100644 --- a/paper/src/chapters/03-methodology.tex +++ b/paper/src/chapters/03-methodology.tex @@ -221,9 +221,9 @@ To speak to realism, user interviews reported that the platform architecture mir The dynamic pricing mechanism elicited immediate behavioral adjustments. Participants were sensitive to price volatility: sudden boosts triggered urgency and faster booking attempts, while large listing-to-final discrepancies triggered deeper comparison behavior. The responses match what one expects from live commerce: sharp reactions to volatility and to list--checkout gaps, which supports external validity despite the lab setting. -\subsubsection{Design of Training Factorial Study} +\subsubsection{Design of Training Sweeps} -The simulator has multiple configurable factors. We design a multi-factor study across five axes derived from the sweep configurations: (1) RL algorithm (\texttt{ppo}, \texttt{a2c}, \texttt{dqn}, \texttt{qtable}; 4 levels), (2) contamination ratio $\alpha$ sampled from $[0.1, 0.6]$ at four representative levels, (3) robustness radius $\epsilon_\alpha \in \{0.0, 0.15, 0.3\}$ (3 levels), (4) COI penalty weight $\lambda_\text{coi}$ at two reference levels, and (5) pricing action granularity (two discretization settings for \texttt{action\_levels}); giving a grid of $4\times4\times3\times2\times2 = 192$ configurations. Behavioral distinguishability is assessed with a two-sample Mann--Whitney test on per-session divergence gap scores at cohort sizes $n_H=13$ and $n_A=16$. +The simulator has multiple configurable factors. Training runs are driven by Weights \& Biases sweep definitions versioned with the codebase, mixing random and grid schedules rather than a single full factorial. For the contamination ratio $\alpha$, exploratory sweeps draw $\alpha$ uniformly on $[0.1,0.6]$; some sweeps use the narrower interval $[0.1,0.5]$. Grid sweeps fix explicit level sets, for example $\alpha\in\{0.1,0.2,0.3,0.4,0.6,0.8\}$ (six levels, including $0.8$ beyond the typical exploratory upper endpoint) or five levels $\{0.1,0.2,0.3,0.4,0.6\}$. Auxiliary schedules also include $\alpha=0$ alongside positive values. Robustness radius $\epsilon_\alpha$, COI penalty $\lambda_\text{coi}$, RL algorithm (\texttt{ppo}, \texttt{a2c}, \texttt{dqn}, \texttt{qtable}), and the discretization of the price action grid vary by sweep. Broad random search may use uniform $\epsilon_\alpha\in[0,0.3]$ and $\lambda_\text{coi}\in[0.05,0.6]$; tighter grids may fix $\epsilon_\alpha=0.2$ and restrict $\lambda_\text{coi}$ to $\{0.15,0.30\}$. Behavioral distinguishability is assessed with a two-sample Mann--Whitney test on per-session divergence gap scores at cohort sizes $n_H=13$ and $n_A=16$. While this scale is generally expensive for reinforcement learning, we execute it on a large TPU cluster to make the sweep tractable. Our training budget is provisioned through TPU Research Cloud and spans 384 chips across TPU v4, v5e, and v6e generations, with a spot-heavy allocation plus an on-demand reserve. At peak BF16 throughput this corresponds to approximately 160\,PFLOPS of aggregate compute (derivation in Appendix~\ref{app:compute_budget}), which makes repeated seeds, ablations, and sensitivity sweeps feasible within practical wall-clock limits. We allocate v6e capacity to the highest-intensity policy training jobs, use v5e for wider hyperparameter exploration where throughput-per-dollar is favorable, and reserve on-demand v4 capacity for runs that should not be interrupted. @@ -261,8 +261,9 @@ v4 & 64 (32 + 32) & us-central2-b & 32 Spot + 32 On-demand \\ \end{tabular} \end{table} -For connections from Madrid, we prioritize the europe-west4 allocation for latency-sensitive runs with the benefit of having the most grouped chips within a single region. This regional grouping is important for the deployment of our Kubernetes cluster which cannot span multiple regions. All sweep metadata, model checkpoints, and reward traces are logged in Weights \& Biases. % TODO: cite this (from bib) -Hardware specifications are from the official Google Cloud TPU documentation \parencite{noauthor_tpu_2026,noauthor_tpu_2025-1,noauthor_tpu_2025}. +For connections from Madrid, we prioritize the europe-west4 allocation for the sake of latency and the benefit of having the most grouped chips within a single region. This regional grouping is important for the deployment of our Kubernetes cluster which cannot span multiple regions. All sweep metadata, model checkpoints, and reward traces are logged in Weights \& Biases. \parencite{noauthor_tpu_2026,noauthor_tpu_2025-1,noauthor_tpu_2025}. +% TODO: cite this (from bib) + Training images follow Docker layer caching: dependency layers are separate from the copy of application source so routine code edits do not invalidate the entire build; only changes to the training entrypoint or dependencies force a full rebuild. @@ -395,18 +396,14 @@ The session-level control signal injected into pricing is then This turns distinguishability into an operational control input in the engine. On a per-customer or use-case basis, a similar data collection and fitting process should be repeated to obtain domain-specific behavior kernels. -In implementation we keep an alternating game-history buffer and advance it each epoch with two transitions: the platform publishes a price vector (leader move), then the environment returns trajectory-derived demand (follower move). The codebase names this structure \textit{Limbo}; the appendix lists it under the same label for readers who inspect the repository. +In implementation we keep an alternating game-history buffer and advance it each epoch with two transitions where the platform publishes a price vector (leader move), then the environment returns trajectory-derived demand (follower move). We call this the \textit{Limbo}. To avoid notation drift, we separate two COI objects used for different purposes: \begin{align} -\text{COI}_{\text{level}}(\pi) &= \mathbb{E}[P]-\underline{p} \quad \text{(global reporting KPI)} \\ +\text{COI}_{\text{level}}(\pi) &= \mathbb{E}[P]-\underline{p}\\ \text{COI}_{\text{leak}}(p,\tau') &= f(\tau')\cdot \text{InfoValue}(p,\tau') \quad \text{(local control penalty)} \end{align} -where $\text{COI}_{\text{level}}$ is evaluated at policy level and $\text{COI}_{\text{leak}}$ is evaluated per observed quote during training. We connect local leakage to expected global erosion with the operational assumption -\begin{equation} -\mathbb{E}[\Delta\text{COI}_{\text{level},t} \mid \tau_t'] \approx -\kappa\,\text{COI}_{\text{leak}}(p_t,\tau_t') + \xi_t, -\end{equation} -where $\kappa>0$ and $\xi_t$ is residual noise. This keeps theorem-level COI erosion (global, asymptotic) distinct from training-time leakage control (local surrogate). +where $\text{COI}_{\text{level}}$ is evaluated at policy level and $\text{COI}_{\text{leak}}$ is evaluated per observed quote during training. Subsequently, when discussing the reward structure, we will better understand the term of the information value. % Mention discretized action space and the clipping and over shotting in continuous action spaces % Also talk about catastrophic economics, we add termination on bankrupcy or zero demand so market collaps @@ -481,7 +478,7 @@ In practice, we parameterize this with a session-level leakage term: \begin{equation} \text{COI}_{\text{leak}}(p,\tau') = f(\tau')\cdot \text{InfoValue}(p,\tau') \end{equation} -where $f(\tau')$ is the weak agent probability and $\text{InfoValue}$ is implemented either as a constant query-tax surrogate or as a revelation surrogate $-\log\pi(p\mid\tau')$. +where $f(\tau')$ is the weak agent probability and $\text{InfoValue}$ is implemented either as a constant query-tax surrogate or as a revelation surrogate $-\log\pi(p\mid\tau')$. In the latter case, leakage is \emph{contamination-weighted surprisal}: $f(\tau')$ scales how much we treat the session as agentic, and $-\log\pi(p\mid\tau')$ scores how unexpected the realized quote is under the policy's own distribution over prices. Appendix~\ref{app:revelation_log} records why the logarithm is the conventional choice for that second factor. The inner minimization selects the contamination candidate that makes the penalized reward smallest, so the outer policy update faces the worst plausible leakage scenario inside the ambiguity set rather than an average case. diff --git a/paper/src/chapters/04-results.tex b/paper/src/chapters/04-results.tex index 2dce74b..1dc220d 100644 --- a/paper/src/chapters/04-results.tex +++ b/paper/src/chapters/04-results.tex @@ -2,7 +2,7 @@ \begin{figure}[ht] \centering \input{chapters/figures/supra/supra.tex} - \caption{Evolution of price distributions over experiment steps. The heatmap illustrates the density of price offerings. This is an early baseline simulation which demonstrates supra-competitive price-setting in deep learning agents such as SAC as can be clearly seen by the high density at the highest available price.} + \caption{Evolution of price distributions over experiment steps. The heatmap illustrates the density of price offerings. This is an early baseline simulation which demonstrates supra-competitive price-setting in deep learning agents such as Soft Actor Critic as can be clearly seen by the high density at the highest available price.} \label{fig:supra_heatmap} \end{figure} @@ -44,7 +44,7 @@ The contamination--revenue slope is estimated on a controlled cohort (single swe \begin{table}[ht] \centering -\caption{Slope verification table for contamination versus revenue (OLS-style report).} +\caption{Slope verification table for contamination versus revenue.} \label{tab:contamination_slope_table} \begin{tabular}{@{}lrrrrr@{}} \toprule diff --git a/paper/src/chapters/05-discussion.tex b/paper/src/chapters/05-discussion.tex index fa5ad55..ad7ff47 100644 --- a/paper/src/chapters/05-discussion.tex +++ b/paper/src/chapters/05-discussion.tex @@ -6,7 +6,7 @@ Our analysis of interaction dynamics between the platform and non-human actors suggests that static posted-price models are a weak match for an economy in which software agents mediate search and purchase. If one pushes toward direct-revelation or auction-like pricing, volatility rises: prices behave more like traded claims than like sticky retail quotes, though without the fungibility of securities. -E-commerce goods differ from financial assets in a hard way: unit economics and reservation values set a floor. The market might ``want'' an iPhone at \$1; the platform cannot honor that. Pricing therefore needs an anchor $P_{0}$ (cost plus target margin) around which offers may move. In that setting, large language model (LLM) agents resemble institutional liquidity providers: they quote, probe, and clear subsets of flow. As autonomy of agentic systems increases, end users may delegate browsing and checkout to assistants rather than to retailer sites directly, which shifts where demand signals originate. The scenario presumes agents eventually hold delegated payment authority; until then, our results bound a near-term reconnaissance-heavy regime. +E-commerce goods differ from financial assets in a hard way: unit economics and reservation values set a floor. The market might ``want'' an iPhone at \$1, however that is not permissible. Pricing therefore needs an anchor $P_{0}$ (cost plus target margin) around which offers may move. In that setting, large language model (LLM) agents resemble institutional liquidity providers: they quote, probe, and clear subsets of flow. As autonomy of agentic systems increases, end users may delegate browsing and checkout to assistants rather than to retailer sites directly, which shifts where demand signals originate. The scenario presumes agents eventually hold delegated payment authority; until then, our results bound a near-term reconnaissance-heavy regime. \subsection{Risk Assessment and Limitations} \label{sec:limitations_risks} diff --git a/paper/src/main-genpop.tex b/paper/src/main-genpop.tex index 1c8fb1e..4a53f55 100644 --- a/paper/src/main-genpop.tex +++ b/paper/src/main-genpop.tex @@ -91,4 +91,13 @@ The textbook definition $D_{\mathrm{KL}}(P\parallel Q)=\sum_k P(k)\log(P(k)/Q(k) In code we do the boring fix: add a tiny floor $\varepsilon$ to both the numerator and denominator inside the log so nothing is exactly zero, which turns the sum into a finite, smoothed surrogate rather than a literal KL to raw counts. We also skip source states that do not exist at all in the reference kernel, because there is nowhere honest to compare against. This keeps the pipeline running and the divergence scores on a comparable scale, at the cost that the number is regularized KL-ish behavior, not a purist information-theoretic quantity---which is acceptable here because we only use the gap between human-anchored and agent-anchored scores as a weak separability signal, not as a calibrated physical constant. +\section{Why the logarithm appears in the revelation surrogate} +\label{app:revelation_log} + +Recall that $\text{COI}_{\text{leak}}(p,\tau') = f(\tau')\cdot\text{InfoValue}(p,\tau')$. The query-tax surrogate fixes $\text{InfoValue}$ to a positive constant: every suspected reconnaissance quote is penalized equally, which tracks the erosion story where independent query volume drives COI to zero. The revelation surrogate instead sets $\text{InfoValue}(p,\tau') = -\log \pi(p\mid\tau')$, where $\pi(\cdot\mid\tau')$ is the pricing policy's distribution over quoted prices in context $\tau'$ (after whatever discretization or binning the engine uses). + +For an outcome with probability $q$, the quantity $-\log q$ is \emph{surprisal}: likely draws are unsurprising, rare draws are highly surprising. That matches the informal ``surprise'' people talk about in recommender systems when they formalize novelty as low predicted probability---here the model is our own policy. The log is the standard information-theoretic way to turn ``how probable was this draw?'' into a penalty that grows sharply in the tails. In the reconnaissance reading, a price from a thin slice of the policy's support is more identifying than a typical quote. + +So the revelation form is \emph{contamination-weighted surprisal}: $f(\tau')$ scales how agent-like we judge the session, and $-\log\pi(p\mid\tau')$ scales how informative that price is relative to $\pi(\cdot\mid\tau')$. In code you still floor $\pi(p\mid\tau')$ away from zero so tail bins do not explode the penalty, same spirit as Appendix~\ref{app:kl_zeros}. + \end{document} diff --git a/paper/src/main.tex b/paper/src/main.tex index c743f68..ec79886 100644 --- a/paper/src/main.tex +++ b/paper/src/main.tex @@ -123,6 +123,20 @@ The textbook definition $D_{\mathrm{KL}}(P\parallel Q)=\sum_k P(k)\log(P(k)/Q(k) In code we do the basic fix: add a tiny floor $\varepsilon$ to both the numerator and denominator inside the log so nothing is exactly zero, which turns the sum into a finite, smoothed surrogate rather than a literal KL to raw counts. We also skip source states that do not exist at all in the reference kernel, because there is nowhere honest to compare against. This keeps the pipeline running and the divergence scores on a comparable scale, at the cost that the number is regularized KL behavior, not a purist information-theoretic quantity, which is acceptable here because we only use the gap between human-anchored and agent-anchored scores as a weak separability signal. +\section{Why the logarithm appears in the revelation surrogate} +\label{app:revelation_log} + +Recall that $\text{COI}_{\text{leak}}(p,\tau') = f(\tau')\cdot\text{InfoValue}(p,\tau')$. The query-tax surrogate fixes $\text{InfoValue}$ to a positive constant: every suspected reconnaissance quote is penalized equally, which tracks the erosion theorem where independent query volume drives COI to zero. The revelation surrogate instead sets +\begin{equation} +\text{InfoValue}(p,\tau') = -\log \pi(p\mid\tau'), +\end{equation} +where $\pi(\cdot\mid\tau')$ is the pricing policy's distribution over quoted prices in context $\tau'$ (after whatever discretization or binning the engine uses). + +For an outcome that occurs with probability $q$, the quantity $-\log q$ is the usual \emph{surprisal}: likely draws have small surprisal, rare draws have large surprisal. That is the same ``surprise'' people import into recommender systems when they formalize novelty as low predicted probability under a model---here the model is our own policy. The log is not decorative: it is the standard information-theoretic coding of ``how unexpected was this draw under $\pi$?'' In the reconnaissance reading, a quote from a thin slice of the policy's support is more identifying than a modal quote, because it pins down what the rule is willing to do in places where little mass sits. + +Put together, the revelation form is \emph{contamination-weighted surprisal}: $f(\tau')$ scales how agent-like we judge the session, and $-\log\pi(p\mid\tau')$ scales how informative that realized price is relative to $\pi(\cdot\mid\tau')$. In implementation you still floor $\pi(p\mid\tau')$ away from zero so tail bins do not explode the penalty---the same honesty as Appendix~\ref{app:kl_zeros}: we use a regularized surrogate, not a literal infinite penalty. + + % \input{../build/concatenated_code} \end{document} diff --git a/paper/src/mirrors/genpop/03-methodology.tex b/paper/src/mirrors/genpop/03-methodology.tex index 505aa20..d6595e1 100644 --- a/paper/src/mirrors/genpop/03-methodology.tex +++ b/paper/src/mirrors/genpop/03-methodology.tex @@ -130,9 +130,9 @@ To speak to realism, user interviews reported that the platform architecture mir The dynamic pricing mechanism elicited immediate behavioral adjustments. Participants were sensitive to price volatility: sudden boosts triggered urgency and faster booking attempts, while large listing-to-final discrepancies triggered deeper comparison behavior. This is comforting because the controlled setup still produces commercially relevant interaction data. -\subsubsection{Design of Training Factorial Study} +\subsubsection{Design of Training Sweeps} -The simulator has multiple configurable factors. We design a multi-factor study across five axes derived from the sweep configurations: (1) RL algorithm (PPO, A2C, DQN, Q-table; 4 levels), (2) contamination ratio sampled at four representative levels between 0.1 and 0.6, (3) robustness radius (3 levels), (4) COI penalty weight at two reference levels, and (5) pricing action granularity (two discretization settings for action levels); giving a grid of 192 configurations. Behavioral distinguishability is assessed with a two-sample Mann--Whitney test on per-session divergence gap scores at cohort sizes $n_H=13$ and $n_A=16$. +The simulator has multiple configurable factors. Training runs are driven by Weights \& Biases sweep definitions versioned with the codebase, mixing random and grid schedules rather than a single full factorial. For the contamination ratio $\alpha$, exploratory sweeps draw $\alpha$ uniformly on $[0.1,0.6]$; some sweeps use the narrower interval $[0.1,0.5]$. Grid sweeps fix explicit level sets, for example $\alpha\in\{0.1,0.2,0.3,0.4,0.6,0.8\}$ (six levels, including $0.8$ beyond the typical exploratory upper endpoint) or five levels $\{0.1,0.2,0.3,0.4,0.6\}$. Auxiliary schedules also include $\alpha=0$ alongside positive values. Robustness radius $\epsilon_\alpha$, COI penalty $\lambda_\text{coi}$, RL algorithm (\texttt{ppo}, \texttt{a2c}, \texttt{dqn}, \texttt{qtable}), and the discretization of the price action grid vary by sweep. Broad random search may use uniform $\epsilon_\alpha\in[0,0.3]$ and $\lambda_\text{coi}\in[0.05,0.6]$; tighter grids may fix $\epsilon_\alpha=0.2$ and restrict $\lambda_\text{coi}$ to $\{0.15,0.30\}$. Behavioral distinguishability is assessed with a two-sample Mann--Whitney test on per-session divergence gap scores at cohort sizes $n_H=13$ and $n_A=16$. While this scale is generally expensive for reinforcement learning, we execute it on a large TPU cluster to make the sweep tractable. From 835e10d6ef5bed79f69c381e14be6cec338c2708 Mon Sep 17 00:00:00 2001 From: Daniel Rosel Date: Thu, 9 Apr 2026 10:29:38 +0200 Subject: [PATCH 25/44] more on revelatin --- paper/src/auto/main.el | 3 ++- paper/src/chapters/03-methodology.tex | 2 +- paper/src/main-genpop.tex | 6 ++---- paper/src/main.tex | 10 ++-------- 4 files changed, 7 insertions(+), 14 deletions(-) diff --git a/paper/src/auto/main.el b/paper/src/auto/main.el index df170dd..0e92d5d 100644 --- a/paper/src/auto/main.el +++ b/paper/src/auto/main.el @@ -22,6 +22,7 @@ (LaTeX-add-labels "app:compute_budget" "tab:compute_derivation" - "app:kl_zeros")) + "app:kl_zeros" + "app:revelation_log")) :latex) diff --git a/paper/src/chapters/03-methodology.tex b/paper/src/chapters/03-methodology.tex index fce3c71..7c698d7 100644 --- a/paper/src/chapters/03-methodology.tex +++ b/paper/src/chapters/03-methodology.tex @@ -478,7 +478,7 @@ In practice, we parameterize this with a session-level leakage term: \begin{equation} \text{COI}_{\text{leak}}(p,\tau') = f(\tau')\cdot \text{InfoValue}(p,\tau') \end{equation} -where $f(\tau')$ is the weak agent probability and $\text{InfoValue}$ is implemented either as a constant query-tax surrogate or as a revelation surrogate $-\log\pi(p\mid\tau')$. In the latter case, leakage is \emph{contamination-weighted surprisal}: $f(\tau')$ scales how much we treat the session as agentic, and $-\log\pi(p\mid\tau')$ scores how unexpected the realized quote is under the policy's own distribution over prices. Appendix~\ref{app:revelation_log} records why the logarithm is the conventional choice for that second factor. +where $f(\tau')$ is the weak agent probability and $\text{InfoValue}$ is implemented either as a constant query-tax surrogate or as a revelation surrogate $-\log\pi(p\mid\tau')$. This is the surprise of the probability of a certain price-setting probability. Essentially, we proxy the leakage term as a surprise of the price our policy is setting, weighted by the contamination estimate. Appendix~\ref{app:revelation_log} expands on why the logarithm is used in the revelation surrogate. The inner minimization selects the contamination candidate that makes the penalized reward smallest, so the outer policy update faces the worst plausible leakage scenario inside the ambiguity set rather than an average case. diff --git a/paper/src/main-genpop.tex b/paper/src/main-genpop.tex index 4a53f55..a50dd10 100644 --- a/paper/src/main-genpop.tex +++ b/paper/src/main-genpop.tex @@ -94,10 +94,8 @@ In code we do the boring fix: add a tiny floor $\varepsilon$ to both the numerat \section{Why the logarithm appears in the revelation surrogate} \label{app:revelation_log} -Recall that $\text{COI}_{\text{leak}}(p,\tau') = f(\tau')\cdot\text{InfoValue}(p,\tau')$. The query-tax surrogate fixes $\text{InfoValue}$ to a positive constant: every suspected reconnaissance quote is penalized equally, which tracks the erosion story where independent query volume drives COI to zero. The revelation surrogate instead sets $\text{InfoValue}(p,\tau') = -\log \pi(p\mid\tau')$, where $\pi(\cdot\mid\tau')$ is the pricing policy's distribution over quoted prices in context $\tau'$ (after whatever discretization or binning the engine uses). +$\text{COI}_{\text{leak}} = f(\tau')\cdot\text{InfoValue}$. Either $\text{InfoValue}=c>0$ (query-tax) or $\text{InfoValue}=-\log\pi(p\mid\tau')$ (revelation), with $\pi(\cdot\mid\tau')$ the policy over quoted prices in context $\tau'$. -For an outcome with probability $q$, the quantity $-\log q$ is \emph{surprisal}: likely draws are unsurprising, rare draws are highly surprising. That matches the informal ``surprise'' people talk about in recommender systems when they formalize novelty as low predicted probability---here the model is our own policy. The log is the standard information-theoretic way to turn ``how probable was this draw?'' into a penalty that grows sharply in the tails. In the reconnaissance reading, a price from a thin slice of the policy's support is more identifying than a typical quote. - -So the revelation form is \emph{contamination-weighted surprisal}: $f(\tau')$ scales how agent-like we judge the session, and $-\log\pi(p\mid\tau')$ scales how informative that price is relative to $\pi(\cdot\mid\tau')$. In code you still floor $\pi(p\mid\tau')$ away from zero so tail bins do not explode the penalty, same spirit as Appendix~\ref{app:kl_zeros}. +For probability $q$, $-\log q$ is surprisal; for independent events, $-\log\prod_i q_i=\sum_i(-\log q_i)$. The revelation surrogate is that surprisal under $\pi(\cdot\mid\tau')$, scaled by $f(\tau')$. Use $\max\{\pi,\varepsilon\}$ so the term stays finite (cf.\ Appendix~\ref{app:kl_zeros}). \end{document} diff --git a/paper/src/main.tex b/paper/src/main.tex index ec79886..ae79ec0 100644 --- a/paper/src/main.tex +++ b/paper/src/main.tex @@ -126,15 +126,9 @@ In code we do the basic fix: add a tiny floor $\varepsilon$ to both the numerato \section{Why the logarithm appears in the revelation surrogate} \label{app:revelation_log} -Recall that $\text{COI}_{\text{leak}}(p,\tau') = f(\tau')\cdot\text{InfoValue}(p,\tau')$. The query-tax surrogate fixes $\text{InfoValue}$ to a positive constant: every suspected reconnaissance quote is penalized equally, which tracks the erosion theorem where independent query volume drives COI to zero. The revelation surrogate instead sets -\begin{equation} -\text{InfoValue}(p,\tau') = -\log \pi(p\mid\tau'), -\end{equation} -where $\pi(\cdot\mid\tau')$ is the pricing policy's distribution over quoted prices in context $\tau'$ (after whatever discretization or binning the engine uses). +Leakage is $\text{COI}_{\text{leak}} = f(\tau')\cdot\text{InfoValue}$. The query-tax form fixes $\text{InfoValue}=c>0$. The revelation form sets $\text{InfoValue}(p,\tau')=-\log\pi(p\mid\tau')$, with $\pi(\cdot\mid\tau')$ the policy distribution over quoted prices in context $\tau'$ (discretized as in the engine). -For an outcome that occurs with probability $q$, the quantity $-\log q$ is the usual \emph{surprisal}: likely draws have small surprisal, rare draws have large surprisal. That is the same ``surprise'' people import into recommender systems when they formalize novelty as low predicted probability under a model---here the model is our own policy. The log is not decorative: it is the standard information-theoretic coding of ``how unexpected was this draw under $\pi$?'' In the reconnaissance reading, a quote from a thin slice of the policy's support is more identifying than a modal quote, because it pins down what the rule is willing to do in places where little mass sits. - -Put together, the revelation form is \emph{contamination-weighted surprisal}: $f(\tau')$ scales how agent-like we judge the session, and $-\log\pi(p\mid\tau')$ scales how informative that realized price is relative to $\pi(\cdot\mid\tau')$. In implementation you still floor $\pi(p\mid\tau')$ away from zero so tail bins do not explode the penalty---the same honesty as Appendix~\ref{app:kl_zeros}: we use a regularized surrogate, not a literal infinite penalty. +For an outcome with probability $q$, the quantity $-\log q$ is \emph{surprisal}. For independent events, $-\log\prod_i q_i=\sum_i(-\log q_i)$. The revelation term is surprisal under $X\sim\pi(\cdot\mid\tau')$, multiplied by $f(\tau')$. In practice we do $\max\{\pi,\varepsilon\}$ in place of $\pi$ so the log stays finite (same spirit as Appendix~\ref{app:kl_zeros}). % \input{../build/concatenated_code} From 8dac0905fc078a780d003b7747e35595b6634f9c Mon Sep 17 00:00:00 2001 From: Daniel Rosel Date: Thu, 9 Apr 2026 11:49:00 +0200 Subject: [PATCH 26/44] fixing mdp defintino --- paper/src/chapters/03-methodology.tex | 4 ++-- paper/src/chapters/mdp_agent.pdf | Bin 10932 -> 10932 bytes paper/src/chapters/mdp_human.pdf | Bin 11953 -> 11953 bytes paper/src/summary.tex | 4 ++-- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/paper/src/chapters/03-methodology.tex b/paper/src/chapters/03-methodology.tex index 7c698d7..942f0f5 100644 --- a/paper/src/chapters/03-methodology.tex +++ b/paper/src/chapters/03-methodology.tex @@ -341,11 +341,11 @@ With these divergence features we compute a weak agent probability $f(\tau')\in[ \label{sec:tpe} -For both subsets, we model session dynamics as an MDP and estimate transition kernel $\mathcal{T}$. For each actor type we estimate global kernels $\hat{\mathcal{T}}_A$ and $\hat{\mathcal{T}}_H$, then cluster into behavioral sub-kernels $\hat{\mathcal{T}}_y^i$ to avoid collapsing all behavior into one average profile. Transition probabilities are estimated by maximum likelihood: +For both subsets, we model session dynamics as a Markov decision process and estimate transition kernel $\mathcal{T}$. For each actor type we estimate global kernels $\hat{\mathcal{T}}_A$ and $\hat{\mathcal{T}}_H$, then cluster into behavioral sub-kernels $\hat{\mathcal{T}}_y^i$ to avoid collapsing all behavior into one average profile. Transition probabilities are estimated by maximum likelihood: \begin{equation} \hat{P}(s' \mid s) = \frac{N(s, s')}{\sum_{k \in \mathcal{S}} N(s, k)} \end{equation} -where $N(s, s')$ is the observed transition count. This allows us to construct a \textit{Contamination Generator} $\mathcal{G}(\alpha)$. Given a clean trajectory dataset, $\mathcal{G}$ injects synthetic agent trajectories sampled from $\hat{\mathcal{T}}_A$ until the effective mixing ratio reaches $\alpha$. The properties of an MDP such as ... should be preserved by the operation described below. +where $N(s, s')$ is the observed transition count. This allows us to construct a \textit{Contamination Generator} $\mathcal{G}(\alpha)$. Given a clean trajectory dataset, $\mathcal{G}$ injects synthetic agent trajectories sampled from $\hat{\mathcal{T}}_A$ until the effective mixing ratio reaches $\alpha$. The properties of an MDP such as a discrete state space, nonnegative transition mass, and row-stochasticity ($\sum_{s'}\hat{P}(s'\mid s)=1$ for visited states) should be preserved by the operation described below. To scale this to catalog-level pricing, we expand the base event transition matrix from $T\times T$ into product-specific transitions using the current demand condition. In practice, we normalize the demand vector across products and use it to weight how much transition mass each product pair receives. Concretely, each cell of the base matrix becomes an $N\times N$ block (for $N$ products), so the transition matrix grows from $T\times T$ to $(T\cdot N)\times(T\cdot N)$. Finally, we add $C$ generic states (homepage, login, checkout terminal states), which gives the full kernel size $(T\cdot N + C)\times(T\cdot N + C)$. % The validity of this demand-weighted block expansion is still subject to formal proof: it needs to be shown that the resulting matrix retains row-stochasticity (rows summing to 1) and that the weighting by the demand vector preserves the Markov property for the expanded state space. In the engine source this is the target of ongoing validation before the expansion is relied on for behavioral generation at scale. diff --git a/paper/src/chapters/mdp_agent.pdf b/paper/src/chapters/mdp_agent.pdf index a11184db8673c5f5c62f4435f634fbcea6b7adb9..729998b80c4071b401378b84df895ef3acca9521 100644 GIT binary patch delta 291 zcmV+;0o?wyRkT&GeJFp;YQr!PgztWexs=8fY^~xrKbRbnKq#ePlH5uULJ^LkSVodd z^Y)eO*rn*SA3K^EmcSy)v0xDgEhwTT=F2=P3Z$2NtB{tWQZ@pRzSUQY4{$J?@Y$oi z?ivS*w%cA&8h!1gGMGtG8x2Zmm1LORoNMhSO?z4y(cWrl6K#L0I3B(+O1N4#2jLWE zhYe>uXBk@)Wpm;$j6X9Lk0yVy@Z_z7z7$4<93fc`-qp1hJN@J1GXMB0TZ3|1Pq7>4 z1s2()I2zrxD2gzId2s3Jh|ZoG(bzV)$@_?3;Y_E-Y45iV>bsWeble5#QwM%hR(D1! pi}~bv_Co1~@YWXJ;a|bMaNkAD6Jk9vOT7x`sxN3)SBBLUh{jottN delta 291 zcmV+;0o?wyRkT&GeJFoXYr-%Th2Qfl&dXS3&?YslR*DZQ$QT2uzKuPE7;0cNB}vEp z_e-jEj6B_sdveaaKn{uqpCj6u zq4Qv5y&e?@YtDmI7Ks$v8nBctQ;y`~eeYITHqzFL{-UKzjctEtv)eaT39n7J6J8X9g>k p!~EfS3_|H|;awKE!@q)C;l-C}@BP9J?)AdbF9R!YP_vOKBLNUvk2U}R diff --git a/paper/src/chapters/mdp_human.pdf b/paper/src/chapters/mdp_human.pdf index 5723a31e6de0b08b0774c1b41263e667c3454a19..7e7f61a49ec38e3da53b011c994921e57358d8b0 100644 GIT binary patch delta 291 zcmV+;0o?wvU9nxTdn|unYlAQp#ozrD=Vh!i&?Hs+2kAo<$`}K&eH(iSX|TdDlEkro z`z2Z(BTx6oJvrxaDM&ywq$FUVDGlfb>rEEqIpWK`mxwFgND}~v-`cCe2N(!ye2%D4 zLuWyQ!(miZT3rVrHCBSRT7gD%8zorXtnJM{jz`*P-rrhbBBg(uFr2>8lG|2wCASi* za?c3MNJ4g$CTq%G2zw?ZoK5~>;mKKheZ{r(IefAayvu9LkLt(8CGN3Zwgt=5%@Vt_ zPGFEMij!7Dk39E-pC^}*&gkr@=AG%io4C*T6;=;&o(i{B%I{Xl`gHWrrS`m*Mh#j> pgZ1Khc0ysN@WvM1;a|amTmGe*M?ZIiM?JUn3;kVhOtX(IA^|-TkyZcz delta 291 zcmV+;0o?wvU9nxTdn|uZYr-%Th2Qfl&dXS3V3XL^D#ZsY$QT1r-^Lz78fsuPB}vEp z_e-jEj6B_sdvea^#Hdcna20e-Kv195LAl-c_T;*8GIHG(5g4O7J3IK4N!1 z2pqD9VsFjRqpIQ%=bOt&Cv*zbiq7@XOlWkoEfZC p4vUB9DF~&%g?AR=4*v?)!iz7{-us0c-0Ov Date: Thu, 9 Apr 2026 15:53:47 +0200 Subject: [PATCH 27/44] clean abstract and introduction --- paper/src/chapters/01-intro.tex | 12 ++++++------ paper/src/main.tex | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/paper/src/chapters/01-intro.tex b/paper/src/chapters/01-intro.tex index 63f3aef..0fa16c8 100644 --- a/paper/src/chapters/01-intro.tex +++ b/paper/src/chapters/01-intro.tex @@ -8,17 +8,17 @@ \section{Introduction} -In this paper we present an exploration and defense against the presence of new commercial entities in digitally powered platforms, preserving market equilibrium in the age of AI. This research establishes the following contributions: definition and formalization of non-human transactors in e-commerce platforms, development of a testing-ground for capturing the behavioral essence of these transactors across a large variety of digital systems, construction of a discriminative model (to prove distinguishability) as a strong learner for downstream mitigation of contamination by non-human entities, translation of such learned distinguishability into existing dynamic pricing machine learning loops, and finally establishment of a high-level KPI-affecting causal effect and cost-saving framework for the future of internet commerce in the presence of such non-human learners. +In this paper we present an exploration and defense against the presence of new commercial entities in digitally powered platforms, preserving market equilibrium in the age of AI. This research establishes the following contributions: definition and formalization of non-human transactors in e-commerce platforms, development of a testing-ground for capturing the behavioral essence of these transactors across a large variety of digital systems, construction of a discriminative model (to prove distinguishability) as a guiding teacher for downstream mitigation of contamination by non-human entities, translation of such learned distinguishability into existing dynamic pricing machine learning loops, and finally establishment of a high-level KPI-affecting causal effect and cost-saving framework for the future of internet commerce in the presence of such non-human learners. -This research effort touches a large variety of domains, spanning behavioral economics for understanding the rationality of behavior as theorized by the concept of homo economicus, agent-based modeling to translate our learned distinguishability into disjoint dynamic pricing systems, reinforcement learning which serves as the SOTA for price-learners, and dynamic pricing and market equilibrium theory to understand the risks of possible supra-competitive pricing phenomena in cases of adversarial pricing systems driving the market out of equilibrium. \footnote{Given the rapid evolution of the field we acknowledge all developments with a cutoff set at the date of March 1st 2026.} +This research effort touches a large variety of domains, spanning behavioral economics for understanding the rationality of behavior as theorized by the concept of homo economicus, agent-based modeling to translate our learned distinguishability into disjoint dynamic pricing systems, reinforcement learning which serves as the SOTA for price-learners, and dynamic pricing and market equilibrium theory to understand the risks of possible supra-competitive pricing phenomena in cases of adversarial pricing systems driving the market out of equilibrium. \footnote{Given the rapid evolution of the field we acknowledge all developments with a knowledge cutoff set at the date of March 1st 2026.} \subsection{Motivation and Market Context} The current innovation boom in generative artificial intelligence and its applications to knowledge-based work tasks has brought many competing technologies for browser-use automation, with benchmarks and evaluations \parencite{xia_evaluation-driven_2025} motivating the development of capabilities focused on commercial research, understanding, and transaction execution \parencite{xie_osworld_2024}. The ``AI Agent'' market is forecasted to grow from around USD 5-8 billion in 2025 to USD 42-52 billion by 2030. This surge reflects adoption in e-commerce, customer service, and enterprise automation, where agents handle interactions previously done by humans, raising the question of how these systems should be designed for future robustness as well as how to maintain a competitive edge in the analytical components of e-commerce platforms \parencite{markntel_advisors_global_2025}. -The key stakeholders affected by the threat of increasing agent-driven traffic include online businesses and platform operators (especially in bot-heavy sectors like retail, travel, and financial services), their security, fraud, and engineering teams, end users whose accounts and data are exposed and whose experience degrades, regulators and legal stakeholders responding to breaches and fraud, and the attackers or bot operators driving the automation \parencite{imperva_rapid_2025}. +The key stakeholders affected by the threat of increasing agent-driven traffic include online businesses and platform operators (especially in bot-heavy sectors like retail, travel, and financial services), their security, fraud, and engineering teams, end users whose accounts and data are exposed and whose user experience degrades, regulators and legal stakeholders responding to breaches and fraud, and the attackers or bot operators driving the automation \parencite{imperva_rapid_2025}. -The industry has already seen legal action in cases like Amazon against Perplexity \parencite{ghaffary_amazon_2025}, stemming from the difficulty of identifying traffic from hybrid systems like the Comet browser. This paper explores such systems to better understand what the interaction data looks like and what it means for dynamic pricing and recommendation systems downstream. This observed impact indicates a need for prevention of secondary negative effects on the ``legacy'' systems which power modern revenue sources for many companies. Dynamic pricing algorithms rely on directly translating demand features $q$ to new price assignments $\hat{p}$ across a catalogue of products of size $N$. This opens opportunities to design a \textit{tabula rasa} of digital market mechanisms that will shape the future of commerce in the age of artificial intelligence. +The industry has already seen legal action in cases like Amazon against Perplexity \parencite{ghaffary_amazon_2025}, stemming from the difficulty of identifying traffic from hybrid systems like the Comet browser. This paper explores such systems to better understand what the interaction data looks like and what it means for dynamic pricing and recommendation systems downstream. This observed impact indicates a need for prevention of secondary negative effects on the ``legacy'' systems which power modern revenue sources for many companies. Dynamic pricing algorithms rely on directly translating demand features $q$ to new price assignments $\hat{p}$ across a catalogue of products of size $N$. Our exploration of this field opens opportunities to design a \textit{tabula rasa} of digital market mechanisms that will shape the future of commerce in the age of artificial intelligence. \subsection{Solution Space Overview} Dynamic pricing systems, as presented by \textcite{mueller_low-rank_2019}, often deal with sparse low-rank data of demand signals which, combined with contamination from agents, creates complex interactions that impact pricing. To further complicate the problem, certain commercial settings such as the one presented by \textcite{amjad_censored_2017} must address the true demand of products under censored observations. This provides a formulation for handling demand in our case with multiple kinds of commercial mediators: $\hat{q} \gets q_A + q_H$ where $q_A$ represents the distribution of demand generated by agentic mediators and $q_H$ represents that of true human demand, these are two distinct populations with divergent objective functions. @@ -27,7 +27,7 @@ We formally define interaction data as coming from some actor which can either b \subsection{Research Questions} -This dissertation is organized around one main research question and three supporting sub-questions: +This dissertation is organized around one main research question and three supporting pillar questions: \begin{enumerate} \item[\textbf{Main RQ}] How can dynamic pricing systems preserve margin integrity when transaction orchestration is increasingly mediated by non-human agents? \item[\textbf{SQ1}] \textit{Distinguishability}: Can agent and human sessions be reliably distinguished from behavioral interaction signals alone, without relying on network-level or device fingerprinting? @@ -64,4 +64,4 @@ Extract final result $r$ from terminal state\; \end{algorithm} -The previously described goal of distinguishability allows us to formulate a task which entails taking raw interaction data for either actor and creating a composite demand estimate $\hat{q}$. We propose a robust optimization objective defined in our methodology, transforming the pricing problem into a form of distributionally robust optimization \parencite{kuhn_distributionally_2025} in which the learner guards against adversarial contamination in observed demand \emph{distributions}. The decision rule must perform when the data-generating law is not a single known distribution but any member of an ambiguity set described only partially. Here that law is a mixture whose weight and components need not be stationary. +The previously described goal of distinguishability allows us to formulate a task which entails taking raw interaction data for either actor and creating a composite demand estimate $\hat{q}$. We propose a robust optimization objective defined in our methodology, transforming the pricing problem into a form of distributionally robust optimization \parencite{kuhn_distributionally_2025} in which the learner guards against adversarial contamination in observed demand \emph{distributions}. The decision rule (in the policy) must perform when the data-generating mechanism is not a single known distribution but any member of an ambiguity set described only partially. Here that mechanism is a mixture whose weight and components need not be stationary. diff --git a/paper/src/main.tex b/paper/src/main.tex index ae79ec0..a5ce990 100644 --- a/paper/src/main.tex +++ b/paper/src/main.tex @@ -19,11 +19,11 @@ \begin{abstract} \noindent -Large language model (LLM) agents are spreading in e-commerce; one consequence is intermediaries that can separate information gathering from transaction execution. This thesis studies dynamic pricing when agents reconnoitre in isolated sessions and thereby weaken the \emph{Cost of Information} (COI), the premium platforms typically extract once demand signals are expressed. +Large language model (LLM) agents are spreading in e-commerce, one consequence is intermediaries that can separate information gathering from transaction execution. This thesis studies dynamic pricing when agents survey in isolated sessions and thereby weaken the \emph{Cost of Information} (COI), the premium platforms typically extract once demand signals are expressed. -We formalize the phenomenon and prove a Cost of Information theorem: as independent, utility-maximizing agents saturate price queries, the platform's sustainable COI goes to zero, so ordinary dynamic pricing is incentive-incompatible in the limit. +We formalize the phenomenon and prove a Cost of Information theorem: as independent, utility-maximizing agents saturate price queries, the platform's sustainable margin goes to zero, so ordinary dynamic pricing is incentive-incompatible in the limit. -The defensive design combines behavioral signals with distributionally robust optimization (DRO). We implement a controlled storefront on a hybrid Kappa--Lambda architecture and show that human and agent sessions induce different transition kernels. Kullback--Leibler divergence to class prototypes yields session scores that feed a distributionally robust reinforcement learning (DR-RL) policy, posed as a Stackelberg game with a Wasserstein ambiguity set over demand so the learner does not collapse to a single empirical demand curve under shifting contamination. +The defensive design combines behavioral signals with distributionally robust optimization (DRO). We implement a controlled storefront on a hybrid batch-streaming architecture and show that human and agent sessions induce different transition kernels. Kullback--Leibler divergence to class prototypes yields session scores that feed a distributionally robust reinforcement learning (DR-RL) policy, posed as a Stackelberg game with a Wasserstein ambiguity set over demand so the learner does not collapse to a single empirical demand curve under shifting contamination. Factorial training on TPUs shows the expected short-run revenue hit from contamination and that the robust objective recovers COI and equilibrium structure in harder regimes (higher contamination, larger catalogs), accounting for UX to prevent supra-competitive pricing. Code and an interaction dataset are released for work on agent-mediated traffic. \end{abstract} From d5d8ea5870d165d8e17f6e35b966ebae8ce727c8 Mon Sep 17 00:00:00 2001 From: Daniel Rosel Date: Thu, 9 Apr 2026 15:57:35 +0200 Subject: [PATCH 28/44] chore: improve lit reviwe tweaks --- paper/src/chapters/02-literature-review.tex | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/paper/src/chapters/02-literature-review.tex b/paper/src/chapters/02-literature-review.tex index 7e3e63d..576fa09 100644 --- a/paper/src/chapters/02-literature-review.tex +++ b/paper/src/chapters/02-literature-review.tex @@ -1,6 +1,6 @@ \section{Literature Review} -To situate the work we review agents and agentic computer use, web automation, economic reasoning, and strategic interaction, then turn to data-driven dynamic pricing under uncertainty. The main technical risk is not ``agents buying things'' in isolation but agents reshaping the behavioral and demand signals on which downstream pricing depends. Related litigation is already underway---for example \textcite{noauthor_amazoncom_2026} under the Computer Fraud and Abuse Act. Mediating actors also revive classic concerns such as false-name bidding \parencite{yokoo_effect_2004}; pseudonymous re-entry can whitewash reputation and weaken defenses \parencite{feldman_free-riding_2004}. Dynamic pricing assumes demand proxies are behaviorally meaningful, whereas classical bot detection targets security and access control. The gap we target is a principled way to separate non-human reconnaissance from genuine human demand expression and to fold that signal into pricing without degrading legitimate users (we track harm with a user-experience index), for the stakeholders named in the introduction. +To situate the work we review agents and agentic computer use, web automation, economic reasoning, and strategic interaction, then turn to data-driven dynamic pricing under uncertainty. The main technical risk is not ``agents buying things'' in isolation but agents reshaping the behavioral and demand signals on which downstream pricing depends. Related litigation is already underway---for example \textcite{noauthor_amazoncom_2026} under the Computer Fraud and Abuse Act. Mediating actors surface classic concerns such as false-name bidding \parencite{yokoo_effect_2004} or pseudonymous re-entry which can whitewash reputation and weaken defenses \parencite{feldman_free-riding_2004}. Dynamic pricing assumes demand proxies are behaviorally meaningful, whereas classical bot detection targets security and access control. The gap we target is a principled way to separate non-human reconnaissance from genuine human demand expression and to fold that signal into pricing without degrading legitimate users (we track harm with a user-experience index), for the stakeholders named in the introduction. \subsection{Agent Taxonomy and Definitions} @@ -23,7 +23,7 @@ A HAP (HTTP Agent Profile) protocol has been developed as an internet draft by \ Contamination in dynamic pricing systems that observe demand features to update prices appears across several industries. Aviation (about 24\% of observed disruptions in one industry survey) illustrates how malicious or scripted traffic can skew KPIs visible in look-to-book metrics. Excessive reconnaissance traffic inflates search volume without corresponding completed bookings, thereby skewing demand forecasts and disrupting dynamic pricing models. Demand proxies have also been observed to cause significant threat to inventory management by creating artificial scarcity that distorts the demand-supply relationships in the enterprise model. Censored demand as shown by \textcite{amjad_censored_2017} can also be observed in low-bias demand under-estimation caused by a distortion effect coming from non-human traffic data \parencite{imperva_rapid_2025}. -When dynamic pricing algorithms train on highly contaminated or noisy data, mis-inference risk rises. Mis-specified reward and regret signals can push prices down to preserve volume, eroding margins, while misfit to legitimate demand can produce the opposite failure mode; both call for guardrails that preserve commercial intent \parencite{mullapudi_reinforcement_2025}. +When dynamic pricing algorithms train on highly contaminated or noisy data, mis-inference risk rises and revenue is threatened. Mis-specified reward and regret signals can push prices down to preserve volume, eroding margins, while misfit to legitimate demand can produce the opposite failure mode where both call for guardrails that preserve commercial intent \parencite{mullapudi_reinforcement_2025}. %Documented instances of agent-driven market disruptions - Quantitative evidence of pricing manipulation - Case studies from affected industries @@ -35,7 +35,7 @@ When dynamic pricing algorithms train on highly contaminated or noisy data, mis- Like in classical revenue-maximizing auctions \parencite{roughgarden_cs364a_2013} we assume that the human actor in our system has a private valuation $v$ which we formally draw from intrinsically defined distributions. The important note here is that the agent proxy does not have a mechanism to convey this private information into the demand data which directly impacts the pricing systems. -The mediation between agents and commercial platforms turns on transaction costs of information gathering and negotiation. \textcite{shahidi_coasean_2025} argue these costs tend toward zero (we give a complementary formal result in Section~3). \textcite{coase_nature_1937} treats search and participation time as central to Coasean transaction costs, including discovery of relevant prices. Price discovery without AI intermediaries is already costly; we extend the classical Coasean logic to AI-mediated markets. +The mediation between agents and commercial platforms turns on transaction costs of information gathering and negotiation. \textcite{shahidi_coasean_2025} argue these costs tend toward zero (we give a complementary formal result in Section~3). \textcite{coase_nature_1937} treats search and participation time as central to Coasean transaction costs, including discovery of relevant prices. Price discovery without AI intermediaries is already costly. We extend this classical Coasean logic to AI-mediated markets. % Economic foundations: relating the problem to options pricing theory. Cost of Information (COI) concept and its relevance @@ -49,7 +49,7 @@ Our effort to combat contamination stems from research by \textcite{hardt_strate To bridge the gap between detection and robust pricing, we look at work in Distributionally Robust Optimization (DRO). As defined by \textcite{kuhn_wasserstein_2024}, DRO provides a framework for decision-making under ambiguity, where the true data distribution is unknown but lies within a ``Wasserstein ball'' of a target distribution. In our context, the ``ambiguity set'' represents the uncertainty introduced by agentic reconnaissance. By optimizing for the worst-case distribution within this set, pricing mechanisms can become resilient to the distributional shifts such as the ones caused by non-human actors, effectively robustifying the revenue function against the contamination described in our problem statement. -To build an environment where prices face a demand estimate from a behavioral model, we draw on RecSim \parencite{ie_recsim_2019}. Modeling user behavior as partially observable Markov decision processes yields synthetic interaction that generalizes past the usual cold-start limit of per-user data. We translate RecSim-style user choice modeling into per-class transition models (human versus agent). Dynamic Bayesian networks remain a tractability option for the full platform. RecSim's main contribution is a sandbox for recommender learners; we adapt that idea to dynamic pricing under contamination. +To build an environment where prices face a demand estimate from a behavioral model, we draw on RecSim \parencite{ie_recsim_2019}. Modeling user behavior as partially observable Markov decision processes yields synthetic interaction that generalizes past the usual cold-start limit of per-user data. We translate RecSim-style user choice modeling into per-class transition models (human versus agent). Dynamic Bayesian networks remain a tractability option for the full platform. RecSim's main contribution is a sandbox for recommender learners and we adapt that idea to dynamic pricing under contamination into a sort of contaminated pricing simulator. % TODO: mention https://github.com/meta-pytorch/OpenEnv/tree/main/envs/browsergym_env We also acknowledge the difficulty in similarly affected fields such as authorship, where \textcite{ganie_uncertainty_2025} demonstrate the theoretical limits of the distributional divergence between text authored by a human or large language model. Their approach of computing the divergence between two distributions demonstrates purely theoretically that no classifier can outperform random guessing on their particular task. This is yet another factor to take into consideration when exploring the potential mitigation strategies. From e694d38bce3143526de8e0c84058da74daf1fc93 Mon Sep 17 00:00:00 2001 From: Daniel Rosel Date: Thu, 9 Apr 2026 16:32:47 +0200 Subject: [PATCH 29/44] fiture sigmoid added --- .../chapters/figures/sigmoid_softmax_gap.tex | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 paper/src/chapters/figures/sigmoid_softmax_gap.tex diff --git a/paper/src/chapters/figures/sigmoid_softmax_gap.tex b/paper/src/chapters/figures/sigmoid_softmax_gap.tex new file mode 100644 index 0000000..e987ec0 --- /dev/null +++ b/paper/src/chapters/figures/sigmoid_softmax_gap.tex @@ -0,0 +1,38 @@ +\begin{tikzpicture} +\begin{axis}[ + width=8.8cm, + height=5.2cm, + xmin=-4.6, + xmax=4.6, + ymin=-0.02, + ymax=1.06, + axis lines=left, + xlabel={$\Delta_H - \Delta_A$}, + xlabel style={yshift=-1.5pt}, + ylabel={$f(\tau')$}, + xtick={-4,-2,0,2,4}, + ytick={0,0.5,1}, + tick label style={font=\small}, + label style={font=\small}, + line width=0.6pt, + clip=false, + enlarge x limits=false, +] +\addplot[ + thick, + domain=-4.6:4.6, + samples=201, + smooth, +] {1/(1+exp(-x))}; +\draw[dashed, line width=0.45pt, black!38] + (axis cs:-2.15,0) -- (axis cs:-2.15,{1/(1+exp(2.15))}); +\draw[dashed, line width=0.45pt, black!38] + (axis cs:2.15,0) -- (axis cs:2.15,{1/(1+exp(-2.15))}); +\addplot[only marks, mark=*, mark size=2.2pt, forget plot, draw=black!55, fill=black!55] + coordinates {(-2.15, {1/(1+exp(2.15))})}; +\addplot[only marks, mark=*, mark size=2.2pt, forget plot, draw=black, fill=black] + coordinates {(2.15, {1/(1+exp(-2.15))})}; +\node[font=\footnotesize, anchor=south, inner sep=11pt] at (axis cs:-2.15,{1/(1+exp(2.15))}) {$\Delta_H<\Delta_A$}; +\node[font=\footnotesize, anchor=south, inner sep=6pt] at (axis cs:2.15,{1/(1+exp(-2.15))}) {$\Delta_H>\Delta_A$}; +\end{axis} +\end{tikzpicture} From c0c375548caa845c632c5b88964f6d39468fdd23 Mon Sep 17 00:00:00 2001 From: Daniel Rosel Date: Thu, 9 Apr 2026 16:55:46 +0200 Subject: [PATCH 30/44] chore: updatimg emthodoloyg --- paper/src/chapters/03-methodology.tex | 57 ++++++++++++++++----------- paper/src/mirrors/cais2026/main.tex | 4 +- paper/src/summary.tex | 2 +- 3 files changed, 36 insertions(+), 27 deletions(-) diff --git a/paper/src/chapters/03-methodology.tex b/paper/src/chapters/03-methodology.tex index 942f0f5..c0e747c 100644 --- a/paper/src/chapters/03-methodology.tex +++ b/paper/src/chapters/03-methodology.tex @@ -3,7 +3,7 @@ % Extra notes and clarifications: we observed some humans and get their transition probabilities between event types % We modify behavioral profiles of transition matrices with price elasticity matrices generated by sample valuations of a distributing. -This section details the theoretical and practical framework developed to address dynamic pricing under the influence of non-human actors. We begin by formalizing the problem environment and the nature of the actors. We then derive the \textit{Cost of Information} (COI) theorem, proving the erosion of pricing power in the limit of agent saturation. Following this, we outline our generative contamination strategy using GOFAI-driven distinguishability and transition probability learning. Finally, we formulate the robust control problem as a Stackelberg game solved via Distributionally Robust Reinforcement Learning (DR-RL) with constructed ambiguity sets. +This section addresses the theoretical and practical framework developed to address dynamic pricing under the influence of non-human actors. We begin by formalizing the problem environment and the nature of the actors. We then derive the \textit{Cost of Information} (COI) theorem, proving the erosion of pricing power in the limit of agent saturation. Following this, we outline our generative contamination strategy using GOFAI-driven distinguishability and transition probability learning. Finally, we formulate the robust control problem as a Stackelberg game solved via Distributionally Robust Reinforcement Learning (DR-RL) with constructed ambiguity sets. \subsection{Problem Formalization} @@ -25,7 +25,7 @@ The platform does not directly observe the true underlying demand function $d(p) \label{eq:qhat} \hat{q}_{t,i} = \sum_{s \in \mathcal{S}_t} \sum_{k=1}^{L_s} \omega(a_{s,k}) \cdot \mathbf{1}[i_{s,k} = i] \end{equation} -where $\omega: \mathcal{A} \to \mathbb{R}_+$ assigns weights to actions based on their signal strength regarding willingness to pay. +where $\omega: \mathcal{A} \to \mathbb{R}^+$ assigns weights to actions based on their signal strength regarding willingness to pay. In the current engine implementation, we use the normalized variant of this proxy for each step: \begin{equation} @@ -48,7 +48,7 @@ Accounting for behavioral and market variation, we also treat $\epsilon_t$ as ab \subsection{Cost of Information (COI) Framework} The platform's pricing power comes from information asymmetry: users who express strong interest signals pay more than the base price. We quantify this markup as the \textit{Cost of Information} (COI), which represents the average premium extracted above marginal cost. The intuition behind this being a cost comes from the perspective of the user who is interacting with the platform, where the user is the one incurring that ``cost.'' COI measures the revenue at risk when information asymmetry collapses. -A top-level view in the current AI discourse is that sufficiently large productivity gains can induce vertical deflation through cost compression and supply expansion \parencite{rachitsky_marc_2026}. Our contribution is narrower and mechanism-level: even under long-run deflation, platform revenue still depends on short-run information costs to the user. We formalize that rent as the Cost of Information (COI) and study how agentic reconnaissance accelerates its erosion. +A top-level view in the current AI discourse is that sufficiently large productivity gains can induce vertical deflation (vertical supply chain price decrease) through cost compression and supply expansion \parencite{rachitsky_marc_2026}. Our contribution is narrower and mechanism-level: even under long-run deflation, platform revenue still depends on short-run information costs to the user. We formalize that rent as the Cost of Information (COI) and study how agentic reconnaissance accelerates its erosion. \begin{definition}[Cost of Information] Let $\pi(\tau)$ be a pricing policy mapping interaction histories to prices. The COI is defined as: @@ -137,9 +137,9 @@ This result implies that standard pricing policies $\pi$ cannot extract the same \subsection{System Architecture: Hybrid Kappa-Lambda Architecture} -In order for our research to have grounding in interactions we built a robust e-commerce web-platform. We initially conducted a survey of the leading platforms of airlines and hotel booking sites to identify the specific interface patterns that effectively manage complex travel data. Our analysis revealed a clear industry standard: while both sectors rely on tabbed service selection and left-sidebar filtering to streamline navigation, they diverge in result presentation: airlines utilize visual date-price bars and multi-step wizards to optimize for logistical transparency, whereas hotel platforms leverage image-led cards and scarcity triggers to drive emotional engagement and urgency. Our web framework defines a highly agnostic boilerplate which can be seeded with any data-modality with an easy-to-tailor pattern, which we leverage to define a \texttt{hotel} and \texttt{airline} mode. Both modes are then individually deployed via an environment-level argument which adjusts the proxy routing with custom middleware in Next.js to render only the desired mode. The purpose of this was to create a baseline adaptable to any use-case or desired commercial application. +In order for our research to have grounding in interactions we built a robust e-commerce web-platform. In this framing Kappa represents streamed processing and Lambda batch operations as is given by terminology in big-data processing. We initially conducted a survey of the leading platforms of airlines and hotel booking sites to identify the specific interface patterns that effectively manage complex travel data. To better understand the playing field, we collected artifacts on design across various airlines and hotels. While both sectors rely on tabbed service selection and left-sidebar filtering to streamline navigation, they diverge in result presentation: airlines utilize visual date-price bars and multi-step wizards to optimize for logistical transparency, whereas hotel platforms leverage image-led cards and scarcity triggers to drive emotional engagement and urgency. Our web framework defines a highly agnostic boilerplate which can be seeded with any data-modality with an easy-to-tailor pattern, which we leverage to define a \texttt{hotel} and \texttt{airline} mode. Both modes are then individually deployed via an environment-level argument which adjusts the proxy routing with custom middleware in Next.js to render only the desired mode. The purpose of this was to create a baseline adaptable to any use-case or desired commercial application. -The architecture begins with deployed web applications posting interaction data to a backend that stores each record in Apache Kafka. Kafka acts as the reservoir linking sessions to experiments. Behavioral events and, separately, price observations from the pricing-provider microservice (invoked by the frontend) land in Kafka topics. A scheduled Airflow pipeline (with manual triggers) consumes the stream; the final pricing stage writes vectors to Redis for low-latency reads by the provider and display in the client. The pattern is deliberately standard---Kafka for durability and replay, Redis for serving---so the same skeleton applies across e-commerce settings. We invested in this stack to keep runs reproducible and to limit extraneous variance. +The architecture begins with deployed web applications posting interaction data to a backend that stores each record in Apache Kafka. Kafka acts as the reservoir linking sessions to experiments. Behavioral events and, separately, price observations from the pricing-provider microservice (invoked by the frontend) land in Kafka topics. A scheduled Airflow pipeline (with manual triggers) consumes the stream and the final pricing stage writes vectors to Redis for low-latency reads by the provider and display in the client. This design pattern allows us to generalize to other commercial settings, where Kafka is used for durability and replay, Redis for serving and quick queries. We invested in this stack to keep runs reproducible and to limit extraneous variance so the same skeleton applies across e-commerce settings \paragraph{Public Web Artifact} We transition the Kappa like architecture of the data collection to a Lambda architecture for actual learning in a surrogate environment. This allows us to move faster on data which is provided and helps us create a feedback loop for production deployment. To support further research in this intersection of fields we release P4P \footnote{\url{https://github.com/velocitatem/p4p}} as a public repository providing the interaction layer of the PHANTOM framework. This provides a configurable storefront which can be tailored to any commercial setting with a standardized session-level event tracking. We document the API adapters and expected schemas for pricing providers and log ingestion services. The repository is intended for controlled experimentation and method replication rather than production commerce deployment. @@ -167,7 +167,7 @@ The transformation that governs this dynamic pricing is a very simple surge-base \quad \forall i \in \{1, \ldots, N\} \end{equation} -where $p_0 \in \mathbb{R}^N$ is the base price vector (which is seeded into our database distinctly for each mode of the commerce platform), $\varrho_{\text{high}}, \varrho_{\text{low}} \in \mathbb{R}$ are demand thresholds defining surge and discount regions, and $\lambda_{\text{surge}}, \lambda_{\text{disc}} \in \mathbb{R}^+$ are multiplicative factors with typical values $\lambda_{\text{surge}} = 1.2$ and $\lambda_{\text{disc}} = 0.9$. This piecewise function enables rapid price adjustment in response to observed demand without requiring complex elasticity estimation or historical calibration, allowing us to expose actors within our experiments to a system with a dynamic component of pricing. +where $p_0 \in \mathbb{R}^N$ is the base price vector (which is seeded into our database distinctly for each mode of the commerce platform), $\varrho_{\text{high}}, \varrho_{\text{low}} \in \mathbb{R}$ are demand thresholds defining surge and discount regions, and $\lambda_{\text{surge}}, \lambda_{\text{disc}} \in \mathbb{R}^+$ are multiplicative factors with typical values $\lambda_{\text{surge}} = 1.2$ and $\lambda_{\text{disc}} = 0.9$. This piecewise function enables rapid price adjustment in response to observed demand without requiring complex elasticity estimation or historical calibration, allowing us to work with actors within our experiments to a system with a dynamic component of pricing. % For our offline experimental setting, we generalize a master value function that can encompass different demand estimation and pricing strategies. % @@ -179,14 +179,16 @@ where $p_0 \in \mathbb{R}^N$ is the base price vector (which is seeded into our \subsection{Experimental Design} -We start from a practical constraint: we do not have access to proprietary production data. Because of that, we design our own fictional platform that still represents how commercial platforms work in the real world. The design comes from a survey of hotel and airline websites, where we extracted common interface components and used them as a high-level template for dynamic pricing environments. +% We start from a practical constraint: we do not have access to proprietary production data. Because of that, we design our own fictional platform that still represents how commercial platforms work in the real world. The design comes from a survey of hotel and airline websites, where we extracted common interface components and used them as a high-level template for dynamic pricing environments. +In the aforementioned platform we develop for our experiments, we use the surveyed websites and create an \textit{average} representation of what the most expected interfaces would be by extracting common components and designing a high level template for dynamic pricing environments. -The interface is organized as a product catalog where each product belongs to a time-bounded price vector (for example, a daily pricing period). During each period we collect interaction data by instrumenting UI components and predefined action templates that are still customizable. That yields controlled variation while keeping the interface credible. + +The interface is organized as a product catalog where each product belongs to a time-bounded price vector (for example, a daily pricing period). During each period we collect interaction data by instrumenting UI components and predefined action templates that are still customizable. That yields controlled variation while keeping the interface controlled-for. Since users act with motivations, we define a pool of tasks (jobs to be done) and assign tasks randomly to participants. We discuss limitations and choices made in this experimental design in Section~\ref{sec:limitations_risks}. -The task pool is stored as a structured table with fields \texttt{id}, \texttt{created\_at}, \texttt{task\_name}, \texttt{task\_description}, and \texttt{task\_def\_of\_done}. We formulate the tasks as compact jobs-to-be-done rather than as strict click scripts, because the target is to elicit realistic browsing and comparison behavior which can capture nuance of different people. In hotel mode the assigned tasks include \textit{Cheapest Room}, \textit{Cheapest Room w/ View}, \textit{MultiStep Cheapest Room}, \textit{The Digital Nomad (Executive)}, and \textit{The 3-Way Tradeoff (Desk + Quiet + Flexible)}. These prompts deliberately require critical thought in search, inspection of room details, comparison of amenities or images, return visits to the listing page, and a final booking decision which create a degree of cognitive load. In airline mode we use \textit{Last-Minute One-Way Flight}, where the actor must urgently travel to LAX from either SEA or JFK within the next 1--3 days, inspect at least a small set of candidate itineraries, and then book a reasonable earliest departure. -A representative task is to find the cheapest feasible catalog item under explicit constraints while removing strict financial limits so we avoid trivial optimization behavior. Participants are also randomly assigned to one experimental platform mode (hotel or airline). Once assigned, they are dropped into the experiment with an actor ID. Under each experiment ID, we can observe multiple sessions across time and gather long interaction traces for the same actor. +The task pool is stored as a structured table with fields \texttt{id}, \texttt{created\_at}, \texttt{task\_name}, \texttt{task\_description}, and \texttt{task\_def\_of\_done}. We formulate the tasks as compact jobs-to-be-done rather than as rigid instructions, because the target is to elicit realistic browsing and comparison behavior which can capture nuance of different people. In hotel mode the assigned tasks include \textit{Cheapest Room}, \textit{Cheapest Room w/ View}, \textit{MultiStep Cheapest Room}, \textit{The Digital Nomad (Executive)}, and \textit{The 3-Way Tradeoff (Desk + Quiet + Flexible)}. These prompts deliberately require critical thought in search, inspection of room details, comparison of amenities or images, return visits to the listing page, and a final booking decision which create a degree of cognitive load. In airline mode we use \textit{Last-Minute One-Way Flight} or \textit{Family/Work Emergency Travel}, where the actor must urgently travel to LAX from either SEA or JFK within the next 1 to 3 days, inspect at least a small set of candidate itineraries, and then book a reasonable earliest departure. +A representative task is to find the cheapest feasible catalog item under explicit constraints while removing strict financial limits so we avoid trivial optimization behavior. Participants are also randomly assigned to one experimental platform mode (hotel or airline). Once assigned, they are dropped into the experiment with an actor ID. Under each experiment ID, we can observe multiple sessions across time and gather long interaction traces for the same actor. This de-risks our lower sample size of individuals by allowing broad interaction data to come from each one. The human data collection involved 13 participants, all of whom provided explicit informed consent prior to their session. Participants had an average age of 21 years and were recruited from a university population. Alongside the 13 human sessions we ran 16 agent sessions of equivalent task scope, yielding 29 labeled trajectories in total (45\% human, 55\% agent). Each participant was assigned a single platform mode and a single task drawn from the pool, and completed the session independently without guidance on navigation or pricing strategy. @@ -218,15 +220,15 @@ Our web platform (developed in similar spirit to RecSim \parencite{ie_recsim_201 To speak to realism, user interviews reported that the platform architecture mirrored standard booking interfaces and reduced the cognitive load required to learn the system. One participant described the flow as ``intuitive'' and close to a ``normal'' transaction, suggesting observed behavior was primarily driven by pricing treatment rather than interface novelty. -The dynamic pricing mechanism elicited immediate behavioral adjustments. Participants were sensitive to price volatility: sudden boosts triggered urgency and faster booking attempts, while large listing-to-final discrepancies triggered deeper comparison behavior. The responses match what one expects from live commerce: sharp reactions to volatility and to list--checkout gaps, which supports external validity despite the lab setting. +The dynamic pricing mechanism elicited immediate behavioral adjustments. Participants were sensitive to price volatility: sudden boosts triggered urgency and faster booking attempts, while large listing-to-final discrepancies triggered deeper comparison behavior. The responses match what one expects from live e-commerce experiences, such as reactions to volatility, which supports external validity despite the lab setting. \subsubsection{Design of Training Sweeps} -The simulator has multiple configurable factors. Training runs are driven by Weights \& Biases sweep definitions versioned with the codebase, mixing random and grid schedules rather than a single full factorial. For the contamination ratio $\alpha$, exploratory sweeps draw $\alpha$ uniformly on $[0.1,0.6]$; some sweeps use the narrower interval $[0.1,0.5]$. Grid sweeps fix explicit level sets, for example $\alpha\in\{0.1,0.2,0.3,0.4,0.6,0.8\}$ (six levels, including $0.8$ beyond the typical exploratory upper endpoint) or five levels $\{0.1,0.2,0.3,0.4,0.6\}$. Auxiliary schedules also include $\alpha=0$ alongside positive values. Robustness radius $\epsilon_\alpha$, COI penalty $\lambda_\text{coi}$, RL algorithm (\texttt{ppo}, \texttt{a2c}, \texttt{dqn}, \texttt{qtable}), and the discretization of the price action grid vary by sweep. Broad random search may use uniform $\epsilon_\alpha\in[0,0.3]$ and $\lambda_\text{coi}\in[0.05,0.6]$; tighter grids may fix $\epsilon_\alpha=0.2$ and restrict $\lambda_\text{coi}$ to $\{0.15,0.30\}$. Behavioral distinguishability is assessed with a two-sample Mann--Whitney test on per-session divergence gap scores at cohort sizes $n_H=13$ and $n_A=16$. +The simulator has multiple configurable factors. Training runs are driven by Weights \& Biases sweep definitions versioned with the codebase, mixing random and grid schedules rather than a single full factorial. For the contamination ratio $\alpha$, exploratory sweeps draw $\alpha$ uniformly on $[0.1,0.6]$ and then some sweeps use the narrower interval $[0.1,0.5]$. Grid sweeps fix explicit level sets, for example $\alpha\in\{0.1,0.2,0.3,0.4,0.6,0.8\}$ (six levels, including $0.8$ beyond the typical exploratory upper endpoint) or five levels $\{0.1,0.2,0.3,0.4,0.6\}$. Auxiliary schedules also include $\alpha=0$ alongside positive values. Robustness radius $\epsilon_\alpha$, COI penalty $\lambda_\text{coi}$, RL algorithm (\texttt{ppo}, \texttt{a2c}, \texttt{dqn}, \texttt{qtable}), and the discretization of the price action grid vary by sweep. Broad random search may use uniform $\epsilon_\alpha\in[0,0.3]$ and $\lambda_\text{coi}\in[0.05,0.6]$; tighter grids may fix $\epsilon_\alpha=0.2$ and restrict $\lambda_\text{coi}$ to $\{0.15,0.30\}$. Behavioral distinguishability is assessed with a two-sample Mann--Whitney test on per-session divergence gap scores at cohort sizes $n_H=13$ and $n_A=16$. While this scale is generally expensive for reinforcement learning, we execute it on a large TPU cluster to make the sweep tractable. -Our training budget is provisioned through TPU Research Cloud and spans 384 chips across TPU v4, v5e, and v6e generations, with a spot-heavy allocation plus an on-demand reserve. At peak BF16 throughput this corresponds to approximately 160\,PFLOPS of aggregate compute (derivation in Appendix~\ref{app:compute_budget}), which makes repeated seeds, ablations, and sensitivity sweeps feasible within practical wall-clock limits. We allocate v6e capacity to the highest-intensity policy training jobs, use v5e for wider hyperparameter exploration where throughput-per-dollar is favorable, and reserve on-demand v4 capacity for runs that should not be interrupted. +Our training budget is provisioned through TPU Research Cloud and spans 384 chips across TPU v4, v5e, and v6e generations, with a spot-heavy allocation plus an on-demand reserve. At peak BF16 throughput this corresponds to approximately 160\,PFLOPS of aggregate compute (derivation in Appendix~\ref{app:compute_budget}), which makes repeated seeds, ablations, and sensitivity sweeps feasible within practical wall-clock limits. We allocate v6e capacity to the highest-intensity policy training jobs, use v5e for wider hyperparameter exploration, and reserve on-demand v4 capacity for runs that should not be interrupted. \begin{table}[ht] \centering @@ -265,9 +267,9 @@ For connections from Madrid, we prioritize the europe-west4 allocation for the s % TODO: cite this (from bib) -Training images follow Docker layer caching: dependency layers are separate from the copy of application source so routine code edits do not invalidate the entire build; only changes to the training entrypoint or dependencies force a full rebuild. +Training images abide by Docker layer caching principles with maximal caching on the lowest levels. Dependency layers are separate from the copy of application source so code edits or tweaks do not re-boot the entire build such that only changes to the training entrypoint or dependencies force a full rebuild. -TPU capacity is scarce and often preemptible, so we rely primarily on on-demand pods for workloads that must finish without interruption. A typical reservation is a 32-chip pod across four worker VMs; that layout already gives enough parallelism for our sweep driver without adding a separate cluster scheduler. We considered SLURM-style job arrays, but fluctuating provisioning times would have added operational overhead with little benefit for our workload, so orchestration stays in the container and Ray layer described below. +TPU capacity is scarce and often preemptible, so we rely primarily on on-demand pods for workloads that must finish without interruption. A typical reservation is a 32-chip pod across four worker VMs. That layout already gives enough parallelism for our sweep driver without adding a separate cluster scheduler. We considered SLURM-style job arrays, but fluctuating provisioning times would have added operational overhead with little benefit for our workload, so orchestration stays in the container and Ray layer described below. \subsubsection{Interaction Schema} @@ -275,7 +277,7 @@ We extend the basic event tuple $e_{s,k}$ to capture the full observational sign \begin{equation} e_{s,k} = \left( a_{s,k}, \, i_{s,k}, \, t_{s,k}, \, \mu_{s,k}, \, \delta_{s,k} \right) \end{equation} -where $\mu_{s,k} \in \mathcal{M}$ is a metadata record containing action-specific context (e.g., price observed, filter parameters, element text), and $\delta_{s,k} \in \mathbb{R}_+$ is the dwell time in milliseconds for attention-based actions. +where $\mu_{s,k} \in \mathcal{M}$ is a metadata record containing action-specific context (e.g., price observed, filter parameters, element text), and $\delta_{s,k} \in \mathbb{R}^+$ is the dwell time in milliseconds for attention-based actions. A session $s$ is itself a structured record: \begin{equation} @@ -302,7 +304,7 @@ $\mathcal{A}_{\text{filter}}$ & \texttt{search}, \texttt{filter\_date}, \texttt{ \end{table} This partition enables the weight function $\omega$ from Eq.~\ref{eq:qhat} to assign category-specific signal strengths, with $\omega(\mathcal{A}_{\text{cart}}) > \omega(\mathcal{A}_{\text{dwell}}) > \omega(\mathcal{A}_{\text{nav}}) > \omega(\mathcal{A}_{\text{filter}})$ reflecting decreasing commitment. -The ordering cart $>$ dwell $>$ nav $>$ filter is a deliberate simplification: we set it from early data by ranking categories by KL divergence between human and agent transition rows and then spacing weights in powers of two. The simulator encodes cart $=4.0$, dwell $=2.0$, nav $=1.0$, filter $=0.5$; unknown actions map by prefix to the nearest category. +The ordering cart $>$ dwell $>$ nav $>$ filter is a deliberate simplification: we set it from early data by ranking categories by KL divergence between human and agent transition rows and then spacing weights in powers of two. The simulator encodes cart $=4.0$, dwell $=2.0$, nav $=1.0$, filter $=0.5$ and finally unknown actions map by prefix to the nearest category (or are discarded). The metadata record $\mu$ varies by action type. For product views, $\mu$ contains the observed price $p_{\text{obs}}$ and product attributes. For dwell events, $\mu$ includes the element text and accumulated hover duration. This heterogeneous structure is captured via a schema-on-read approach in our Kafka ingestion pipeline, where events are validated against type-specific schemas before storage. @@ -367,7 +369,7 @@ To scale this to catalog-level pricing, we expand the base event transition matr \subsection{Distributionally Robust Reinforcement Learning (DR-RL)} -We formulate pricing as a Stackelberg game: the platform (leader) sets prices $p_t$, and the population (follower) responds through trajectories and demand. A useful intuition is that the platform behaves like a distorted mirror at a 45-degree angle: what it mirrors is population demand into an estimated demand proxy, and that proxy drives revenue. +We formulate pricing as a Stackelberg game in which the platform (leader) sets prices $p_t$, and the population (follower) responds through trajectories and demand. A useful intuition is that the platform behaves like a distorted mirror at a 45-degree angle: what it mirrors is population demand into an estimated demand proxy, and that proxy drives revenue. % TODO: add canonical Stackelberg citation. Because contamination level $\alpha$ and demand shift are non-stationary online, a simple error term is not enough. We therefore use a Distributionally Robust Optimization objective. Let $\tau'$ be a newly observed trajectory generated by an unknown actor profile (sampled from the behavioral models in Section~\ref{sec:tpe}). We need a demand mapping conditioned on price and trajectory, $\hat{Q}(p,\tau')$. For each $\tau'$, we compute $\hat{\mathcal{T}}'$ and compare it with controlled baselines $\bar{\mathcal{T}}_H$ and $\bar{\mathcal{T}}_A$: @@ -379,7 +381,7 @@ Because contamination level $\alpha$ and demand shift are non-stationary online, \Delta_A &= D_{KL}(\hat{\mathcal{T}}^\prime \parallel \bar{\mathcal{T}}_A) \end{align} -From these two divergences we define the gap score: +From these two divergences we define the gap score following previously highlighted intuition of the divergence: \begin{equation} g(\tau') = \Delta_H(\tau') - \Delta_A(\tau'). \end{equation} @@ -394,6 +396,13 @@ The session-level control signal injected into pricing is then \hat{\alpha}(\tau') = f(\tau'). \end{equation} +\begin{figure}[ht] + \centering + \input{chapters/figures/sigmoid_softmax_gap.tex} + \caption{Logistic mapping from the gap $\Delta_H-\Delta_A$ to the weak agent probability $f(\tau')$. Markers indicate the contrasts $\Delta_H<\Delta_A$ and $\Delta_H>\Delta_A$.} + \label{fig:sigmoid_softmax_gap} +\end{figure} + This turns distinguishability into an operational control input in the engine. On a per-customer or use-case basis, a similar data collection and fitting process should be repeated to obtain domain-specific behavior kernels. In implementation we keep an alternating game-history buffer and advance it each epoch with two transitions where the platform publishes a price vector (leader move), then the environment returns trajectory-derived demand (follower move). We call this the \textit{Limbo}. @@ -401,7 +410,7 @@ In implementation we keep an alternating game-history buffer and advance it each To avoid notation drift, we separate two COI objects used for different purposes: \begin{align} \text{COI}_{\text{level}}(\pi) &= \mathbb{E}[P]-\underline{p}\\ -\text{COI}_{\text{leak}}(p,\tau') &= f(\tau')\cdot \text{InfoValue}(p,\tau') \quad \text{(local control penalty)} +\text{COI}_{\text{leak}}(p,\tau') &= f(\tau')\cdot \text{InfoValue}(p,\tau') \end{align} where $\text{COI}_{\text{level}}$ is evaluated at policy level and $\text{COI}_{\text{leak}}$ is evaluated per observed quote during training. Subsequently, when discussing the reward structure, we will better understand the term of the information value. @@ -468,9 +477,9 @@ $}% The robust policy $\pi^*$ is obtained by solving the maximin problem: \begin{equation} \label{eq:robust_policy} -\pi^* = \arg \max_{\pi} \min_{Q \in \mathcal{U}_\epsilon} \mathbb{E}_{d \sim Q} \left[ R(p, d) - \lambda \cdot \text{COI}_{\text{leak}}(p,\tau') \right] +\pi^* = \arg \max_{\pi} \min_{Q \in \mathcal{U}_\epsilon} \mathbb{E}_{d \sim Q} \left[ R(p, d) - \lambda \cdot \text{COI}_{\text{leak}}(p,\tau') - \eta_{\text{ux}} \cdot \text{UX}(\tau', p) \right] \end{equation} -where $R(p, d)$ is the revenue function and $\lambda$ weighs the information-leakage penalty. We note that $p$ is directly dependent on $\pi$, which is the one deciding this as its action. +where $R(p, d)$ is the revenue function, $\lambda$ weighs the information-leakage penalty, $\eta_{\text{ux}}$ weighs the user-experience penalty, and $\text{UX}(\tau', p)\in[0,1]$. We note that $p$ is directly dependent on $\pi$, which is the one deciding this as its action. Looking at the reward structure, note that we are not subtracting COI but rather the leakage of COI, which is as defined below. @@ -484,7 +493,7 @@ The inner minimization selects the contamination candidate that makes the penali For the baseline engine reported here, we intentionally use the constant query-tax surrogate to keep the mechanism minimal: \begin{equation} -r_t = R(p_t,\tilde q_t) - \lambda\,f(\tau_t')\,c_{\text{info}} +r_t = \ldots - \lambda\,f(\tau_t')\,c_{\text{info}} \end{equation} with fixed $c_{\text{info}}>0$. @@ -557,7 +566,7 @@ The baseline achieves approximately 26 steps per second. Enabling the robustness \begin{table}[ht] \centering -\caption{Per-step profiling results (20 steps, $M=10$ sessions, $N=3$ products). Self-time measures time spent inside the function excluding callees; cumulative time includes the full call subtree.} +\caption{Per-step profiling results (20 steps, $M=10$ sessions, $N=3$ products). Self-time measures time spent inside the function excluding callees & cumulative time includes the full call subtree.} \label{tab:profile_results} \begingroup \small diff --git a/paper/src/mirrors/cais2026/main.tex b/paper/src/mirrors/cais2026/main.tex index 3fe8db7..d7e7568 100644 --- a/paper/src/mirrors/cais2026/main.tex +++ b/paper/src/mirrors/cais2026/main.tex @@ -300,9 +300,9 @@ where $W_p$ is the $p$-Wasserstein distance and $\epsilon > 0$ is the ambiguity The platform seeks a policy $\pi^*$ that maximizes worst-case revenue over the ambiguity set while penalizing information leakage to suspected agents: \begin{equation} \label{eq:robust_policy} -\pi^* = \arg \max_{\pi} \min_{Q \in \mathcal{U}_\epsilon} \; \mathbb{E}_{d \sim Q} \left[ R(p, d) - \lambda \cdot \text{COI}_{\text{leak}}(p, \tau') - \eta \cdot \text{UX}(\tau', p) \right] +\pi^* = \arg \max_{\pi} \min_{Q \in \mathcal{U}_\epsilon} \; \mathbb{E}_{d \sim Q} \left[ R(p, d) - \lambda \cdot \text{COI}_{\text{leak}}(p, \tau') - \eta_{\text{ux}} \cdot \text{UX}(\tau', p) \right] \end{equation} -where $R(p, d) = p \cdot d$ is the revenue function. +where $R(p, d) = p \cdot d$ is the revenue function, $\lambda$ scales COI leakage, and $\eta_{\text{ux}}$ scales the UX penalty with $\text{UX}(\tau', p)\in[0,1]$. \begin{definition}[COI Leakage] The per-query information leakage cost is: diff --git a/paper/src/summary.tex b/paper/src/summary.tex index cc2813e..17151eb 100644 --- a/paper/src/summary.tex +++ b/paper/src/summary.tex @@ -80,7 +80,7 @@ Because contamination level $\alpha$ and demand shift are non-stationary online, We therefore use a Distributionally Robust Optimization objective. We define an ambiguity set $\mathcal{U}_\epsilon(\hat{P}_N)$ centered around our empirical reference distribution $\hat{P}_N$ (derived from the generator $\mathcal{G}$). We utilize the Wasserstein distance metric to define the set of plausible demand distributions the agent might face. -The robust policy $\pi^*$ is obtained by solving the maximin problem $\pi^* = \arg \max_{\pi} \min_{Q \in \mathcal{U}_\epsilon} \mathbb{E}_{d \sim Q} \left[ R(p, d) - \lambda \cdot \text{COI}_{\text{leak}}(p,\tau') \right]$ where $R(p, d)$ is the revenue function and $\lambda$ weighs the information-leakage penalty. +The robust policy $\pi^*$ is obtained by solving the maximin problem $\pi^* = \arg \max_{\pi} \min_{Q \in \mathcal{U}_\epsilon} \mathbb{E}_{d \sim Q} \left[ R(p, d) - \lambda \cdot \text{COI}_{\text{leak}}(p,\tau') - \eta_{\text{ux}} \cdot \text{UX}(\tau', p) \right]$ where $R(p, d)$ is the revenue function, $\lambda$ weighs the information-leakage penalty, and $\eta_{\text{ux}}$ weighs the UX term. In practice, we parameterize this with a session-level leakage term $\text{COI}_{\text{leak}}(p,\tau') = f(\tau')\cdot \text{InfoValue}(p,\tau')$ where $f(\tau')$ is the weak agent probability. As part of reward engineering, we keep a UX factor ($UX\in[0,1]$) as an auxiliary evaluation axis. Our training budget is provisioned through TPU Research Cloud and spans 384 chips across TPU v4, v5e, and v6e generations, with a spot-heavy allocation plus an on-demand reserve. From 2ba5d6d6af4f0085cb3466cfaa7af0a66a702008 Mon Sep 17 00:00:00 2001 From: Daniel Rosel Date: Thu, 9 Apr 2026 17:00:34 +0200 Subject: [PATCH 31/44] fixing results --- paper/src/chapters/04-results.tex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/paper/src/chapters/04-results.tex b/paper/src/chapters/04-results.tex index 1dc220d..2c26b1e 100644 --- a/paper/src/chapters/04-results.tex +++ b/paper/src/chapters/04-results.tex @@ -99,7 +99,7 @@ The Mann-Whitney result ($p<0.001$) confirms that per-session divergence gaps di The first calibration and paired benchmark runs additionally confirm three practical points aligned with the thesis. First, the control loop is reproducible end-to-end (training, evaluation, artifact generation) across algorithms and contamination levels. Second, policy class materially changes price trajectories and resulting COI/revenue profiles under identical environment settings. Third, objective improvements from robustness are regime-dependent in the current baseline, which is consistent with the thesis claim that contamination-aware pricing needs explicit calibration rather than a one-size-fits-all penalty. -We also note that maximizing revenue in isolation can favor aggressive high-price behavior; even in these early runs, the non-robust aggregate shows slightly higher mean COI and margin. For this reason, all subsequent reporting in this thesis is interpreted on a multi-metric basis (objective, revenue, COI, and stability), and not by revenue alone. +We also note that maximizing revenue in isolation can favor aggressive high-price behavior, even in our early runs, the non-robust aggregate shows slightly higher mean COI and margin. For this reason, all subsequent reporting in this thesis is interpreted on a multi-metric basis (objective, revenue, COI, and stability), and not by revenue alone. \subsection{Anomalies} From 0ff0c0432c23819a87ae1f77657733b9294c5f26 Mon Sep 17 00:00:00 2001 From: Daniel Rosel Date: Thu, 9 Apr 2026 17:04:23 +0200 Subject: [PATCH 32/44] fixing methodoloy I hope --- paper/src/chapters/03-methodology.tex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/paper/src/chapters/03-methodology.tex b/paper/src/chapters/03-methodology.tex index c0e747c..bb879d0 100644 --- a/paper/src/chapters/03-methodology.tex +++ b/paper/src/chapters/03-methodology.tex @@ -566,7 +566,7 @@ The baseline achieves approximately 26 steps per second. Enabling the robustness \begin{table}[ht] \centering -\caption{Per-step profiling results (20 steps, $M=10$ sessions, $N=3$ products). Self-time measures time spent inside the function excluding callees & cumulative time includes the full call subtree.} +\caption{Per-step profiling results (20 steps, $M=10$ sessions, $N=3$ products). Self-time measures time spent inside the function excluding callees and cumulative time includes the full call subtree.} \label{tab:profile_results} \begingroup \small From 895a00480713c5ab7638ff3a528666a93e5b478b Mon Sep 17 00:00:00 2001 From: Daniel Rosel Date: Thu, 9 Apr 2026 18:19:55 +0200 Subject: [PATCH 33/44] feat: improved discussion --- paper/src/chapters/03-methodology.tex | 12 +++++++- paper/src/chapters/05-discussion.tex | 24 ++++++--------- .../figures/experiment_design_tree.tex | 29 +++++++++++++++--- paper/src/chapters/mdp_agent.pdf | Bin 10932 -> 10932 bytes paper/src/chapters/mdp_human.pdf | Bin 11953 -> 11953 bytes paper/src/mirrors/genpop/05-discussion.tex | 6 ++-- 6 files changed, 48 insertions(+), 23 deletions(-) diff --git a/paper/src/chapters/03-methodology.tex b/paper/src/chapters/03-methodology.tex index bb879d0..fb21293 100644 --- a/paper/src/chapters/03-methodology.tex +++ b/paper/src/chapters/03-methodology.tex @@ -187,7 +187,17 @@ The interface is organized as a product catalog where each product belongs to a Since users act with motivations, we define a pool of tasks (jobs to be done) and assign tasks randomly to participants. We discuss limitations and choices made in this experimental design in Section~\ref{sec:limitations_risks}. -The task pool is stored as a structured table with fields \texttt{id}, \texttt{created\_at}, \texttt{task\_name}, \texttt{task\_description}, and \texttt{task\_def\_of\_done}. We formulate the tasks as compact jobs-to-be-done rather than as rigid instructions, because the target is to elicit realistic browsing and comparison behavior which can capture nuance of different people. In hotel mode the assigned tasks include \textit{Cheapest Room}, \textit{Cheapest Room w/ View}, \textit{MultiStep Cheapest Room}, \textit{The Digital Nomad (Executive)}, and \textit{The 3-Way Tradeoff (Desk + Quiet + Flexible)}. These prompts deliberately require critical thought in search, inspection of room details, comparison of amenities or images, return visits to the listing page, and a final booking decision which create a degree of cognitive load. In airline mode we use \textit{Last-Minute One-Way Flight} or \textit{Family/Work Emergency Travel}, where the actor must urgently travel to LAX from either SEA or JFK within the next 1 to 3 days, inspect at least a small set of candidate itineraries, and then book a reasonable earliest departure. +The task pool is stored as a structured table with fields \texttt{id}, \texttt{created\_at}, \texttt{task\_name}, \texttt{task\_description}, and \texttt{task\_def\_of\_done}. We formulate the tasks as compact jobs-to-be-done rather than as rigid instructions, because the target is to elicit realistic browsing and comparison behavior which can capture nuance of different people. In hotel mode the assigned tasks include \textit{Cheapest Room}, \textit{Cheapest Room w/ View}, \textit{MultiStep Cheapest Room}, \textit{The Digital Nomad (Executive)}, and \textit{The 3-Way Tradeoff (Desk + Quiet + Flexible)}. These prompts deliberately require critical thought in search, inspection of room details, comparison of amenities or images, return visits to the listing page, and a final booking decision which create a degree of cognitive load. In airline mode we use \textit{Last-Minute One-Way Flight} or \textit{Family/Work Emergency Travel}, where the actor must urgently travel to LAX from either SEA or JFK within the next 1 to 3 days, inspect at least a small set of candidate itineraries, and then book a reasonable earliest departure. Figure~\ref{fig:exp_design_tree} summarizes the assignment tree. + +\begin{figure}[ht] + \centering + \resizebox{0.88\columnwidth}{!}{% + \input{chapters/figures/experiment_design_tree.tex} + } + \caption{Experimental design decision tree for participant assignment.} + \label{fig:exp_design_tree} +\end{figure} + A representative task is to find the cheapest feasible catalog item under explicit constraints while removing strict financial limits so we avoid trivial optimization behavior. Participants are also randomly assigned to one experimental platform mode (hotel or airline). Once assigned, they are dropped into the experiment with an actor ID. Under each experiment ID, we can observe multiple sessions across time and gather long interaction traces for the same actor. This de-risks our lower sample size of individuals by allowing broad interaction data to come from each one. The human data collection involved 13 participants, all of whom provided explicit informed consent prior to their session. Participants had an average age of 21 years and were recruited from a university population. Alongside the 13 human sessions we ran 16 agent sessions of equivalent task scope, yielding 29 labeled trajectories in total (45\% human, 55\% agent). Each participant was assigned a single platform mode and a single task drawn from the pool, and completed the session independently without guidance on navigation or pricing strategy. diff --git a/paper/src/chapters/05-discussion.tex b/paper/src/chapters/05-discussion.tex index ad7ff47..4301eb4 100644 --- a/paper/src/chapters/05-discussion.tex +++ b/paper/src/chapters/05-discussion.tex @@ -1,31 +1,25 @@ \section{Discussion} +% TODO: Gpdr here \subsection{Transition to Agentic Market Microstructure} -Our analysis of interaction dynamics between the platform and non-human actors suggests that static posted-price models are a weak match for an economy in which software agents mediate search and purchase. If one pushes toward direct-revelation or auction-like pricing, volatility rises: prices behave more like traded claims than like sticky retail quotes, though without the fungibility of securities. +Our analysis of the interaction dynamics between the platform and non-human actors suggests that the current static pricing models are insufficient for an agent-mediated economy. If we assume a transition toward a direct revelation mechanism, where actors must reveal their true valuation of a good through bidding dynamics, we inevitably introduce significant stochasticity into the pricing system. Unlike traditional e-commerce where prices are relatively sticky, such a mechanism implies a high volatility characteristic of financial equity markets (without the fungibility however). -E-commerce goods differ from financial assets in a hard way: unit economics and reservation values set a floor. The market might ``want'' an iPhone at \$1, however that is not permissible. Pricing therefore needs an anchor $P_{0}$ (cost plus target margin) around which offers may move. In that setting, large language model (LLM) agents resemble institutional liquidity providers: they quote, probe, and clear subsets of flow. As autonomy of agentic systems increases, end users may delegate browsing and checkout to assistants rather than to retailer sites directly, which shifts where demand signals originate. The scenario presumes agents eventually hold delegated payment authority; until then, our results bound a near-term reconnaissance-heavy regime. +However, e-commerce commodities differ fundamentally from financial securities: they possess a hard floor defined by unit economics and reservation prices. The market might react enthusiastically to an iPhone priced at \$1. Such a transaction is not permissible. The platform must establish an initial valuation anchor ($P_0$) defined by the marginal cost plus a target margin, around which the market price is permitted to fluctuate. + +We float the introduction of GenAI Agents as Institutional Market Makers. As the arms race for greater autonomy of agentic systems grows, the commercial viability of AI agents has the potential to disseminate into everyday users directly interacting with them rather than e-commerce platforms. This is also under the assumption of expected transactional capabilities being given to AI Agents. \subsection{Risk Assessment and Limitations} \label{sec:limitations_risks} -Behavior-based pricing raises predictable ethics questions when models are opaque: a behavioral profile can become a basis for price discrimination or exclusion if deployed without governance. Universal behavioral profile modeling (UBPM) in recommendation already shows how fine-grained traces enable strong personalization; the same machinery applied to prices needs guardrails. +Behavior-based pricing raises predictable ethics questions when models are opaque: a behavioral profile can become a basis for price discrimination or exclusion if deployed without governance. Universal behavioral profile modeling (UBPM) in recommendation already shows how fine-grained traces enable strong personalization. The same machinery applied to prices needs guardrails. -In our experiments participants are randomized to platform mode and task. Figure~\ref{fig:exp_design_tree} summarizes the assignment tree. -\begin{figure}[ht] - \centering - \resizebox{0.92\columnwidth}{!}{% - \input{chapters/figures/experiment_design_tree.tex} - } - \caption{Experimental design decision tree for participant assignment.} - \label{fig:exp_design_tree} -\end{figure} +We balance human and agent sessions near one-to-one so cohorts are comparable despite different population sizes. The row-level dataset still contains thousands of events. -The human sample is small but each session is long-form; we balance human and agent sessions one-to-one so cohorts are comparable despite different population sizes. The row-level dataset still contains thousands of events. - -Rapid change in agent capabilities and user expectations induces model drift; the UX term in reward shaping was included partly to penalize policies that sacrifice legitimate users for short-run revenue. Reinforcement learning adds its own risks---reward hacking and limited interpretability---which matter when policies touch live revenue; deployment would require monitoring and constraints beyond what we exercised here. +% Rapid change in agent capabilities and user expectations induces model drift; the UX term in reward shaping was included partly to penalize policies that sacrifice legitimate users for short-run revenue. Reinforcement learning adds its own risks---reward hacking and limited interpretability---which matter when policies touch live revenue; deployment would require monitoring and constraints beyond what we exercised here. +With the exponential growth in capability of agents aswell as user expectations, a degree of model drift is expected in this setting. The computational requirements for continuous extraction of margin as demonstrated by our work are required by the persistent speed of the market. Reinforcement learning that sacrifices legitimate user experience for short run revenue does not hold up in the long run. Reward hacking, to which pricing algorithms are not impervious due to their limited interpretability is a significant risk for a company if live revenue is in play. Deployment requires consistent monitoring and constraints beyond what was done as exercise in this work. % \subsection{Implications of Findings} Interpretation of results and altenrative scenarios with broader market implications. diff --git a/paper/src/chapters/figures/experiment_design_tree.tex b/paper/src/chapters/figures/experiment_design_tree.tex index d37379d..9f0e906 100644 --- a/paper/src/chapters/figures/experiment_design_tree.tex +++ b/paper/src/chapters/figures/experiment_design_tree.tex @@ -1,9 +1,28 @@ +% Horizontal tree: level distance must exceed ~half parent + half child width or nodes overlap (resizebox does not fix that). \begin{tikzpicture}[ - level distance=14mm, - sibling distance=36mm, - decision/.style={rectangle, draw, rounded corners=2pt, align=center, minimum width=26mm, minimum height=8mm, font=\small}, - leaf/.style={rectangle, draw, align=center, minimum width=30mm, minimum height=8mm, font=\small}, - edge from parent/.style={draw, -{Latex[length=2mm]}} + grow=right, + level distance=30mm, + sibling distance=23mm, + decision/.style={ + rectangle, + draw, + rounded corners=1.5pt, + align=center, + inner sep=1.2pt, + minimum width=14mm, + minimum height=4.8mm, + font=\scriptsize, + }, + leaf/.style={ + rectangle, + draw, + align=center, + inner sep=1.2pt, + text width=19mm, + minimum height=4mm, + font=\scriptsize, + }, + edge from parent/.style={draw, -{Latex[length=1.2mm]}}, ] \node[decision] {Participant} child { diff --git a/paper/src/chapters/mdp_agent.pdf b/paper/src/chapters/mdp_agent.pdf index 729998b80c4071b401378b84df895ef3acca9521..d06b268a806d678feb84f41fa9862c84fc58acec 100644 GIT binary patch delta 284 zcmV+%0ptF(RkT&GgeZS{in)}=6l_UxoF7aMu_2UFFiCEu2cZbZP%Ik^O#9 zR7PKWsSFlUG)9BPY!jzg+??y|E=dMj8`0ewX=82bC>p;pO1OVUx213ji*m;)&uB`w zY|SX;FO)x18cimDvGC-rgT4|*g&ZN-0N&NL5eNO_<5K_lCf$N%Y&*wpoflZ7v*KuU z-yzS#5a!Wkpc6WKYD8U2B==u-!NQdajyDvRapdGqkF-gGW8T^b7b~Z$`6`DI)=UeTqi_ delta 284 zcmV+%0ptF(RkT&GgeZT0in)}=6l|^HI6s&il0Yb>V3OQQ4?+=+p;$(eOY`=X?AWE~ zv>!W~8J55z%CTS(2Q4U~CFaXKDhi~Rd#jL^qEa>jkiOMdiw|%xobcJBz3v(ZiniNc zQ5t>iq%xREQ5y|PXq9A`-JEOfCQW-<8PVQqX%lU#I3B(+O1OVoHwWPqW`_-DJZBkO z6J>MaFN{Ak7LO)>vGC-rgT53-g&ZMS58l4?sr8qwG`xXJs7U*Sxr#%b@j4(hv>>U7)%=u-!NQdW0HDvSB#dGIPl_=B diff --git a/paper/src/chapters/mdp_human.pdf b/paper/src/chapters/mdp_human.pdf index 7e7f61a49ec38e3da53b011c994921e57358d8b0..a04e427b9c7587013a73e7e01ba883f6022417ba 100644 GIT binary patch delta 291 zcmV+;0o?wvU9nxTdn|uZYr-%Th2Q%t&dXS3&?Zq^mEwaHWQ>8RZ(|Q3z0|^JN|KKG z@0YaJG4gaj?#Vfa%fTX^VaXx^zT^?lG0(H8D3D%m9U(o+nrs9heQPR<4+t=v@HwDT z_pJkuw%b8*G`i{)8N^C9MuR7Ol`Ifno$Kr-O$S;V* pi}}Oz?1jpW;jI?G!@q*1bn;8r_kR8c_j-Qm7yMIiOS6wHA^|2ikt+ZI delta 291 zcmV+;0o?wvU9nxTdn|unYlAQp#ozrD=Vh!i&?Hs+2kAo<$`}K&eH(iSX|TdDlEkro z`z2Z(BTx6oJvrxaDM&ywq$FUVDGlfb>rEEqIpWK`mxwFgND}~v-`cCe2N(!ye2%D4 zLuWyQ!(miZT3rVrHCBSRT7gD%8zorXtnJM{jz`*P-rrhbBBg(uFr2>8lG|2wCASi* za?c3MNJ4g$CTq%G2zw?ZoK5~>;mKKheZ{r(IefAayvu9LkLt(8CGN3Zwgt=5%@Vt_ zPGFEMij!7Dk39E-pC^}*&gkr@=AG%io4C*T6;=;&o(i{B%I{Xl`gHWrrS`m*Mh#j> pgZ1Khc0ysN@WvM1;a|amTmGe*M?ZIiM?JUn3;kVhOtX(IA^|-TkyZcz diff --git a/paper/src/mirrors/genpop/05-discussion.tex b/paper/src/mirrors/genpop/05-discussion.tex index 5137ed0..e85589e 100644 --- a/paper/src/mirrors/genpop/05-discussion.tex +++ b/paper/src/mirrors/genpop/05-discussion.tex @@ -2,9 +2,11 @@ \subsection{Transition to Agentic Market Microstructure} -Our analysis of the interaction dynamics between the platform and non-human actors suggests that the current static pricing models are insufficient for an agent-mediated economy. If we assume a transition toward a direct revelation mechanism, where actors must reveal their true valuation of a good through bidding dynamics, we inevitably introduce significant stochasticity into the pricing system. Unlike traditional e-commerce where prices are relatively sticky, such a mechanism implies a high volatility characteristic of financial equity markets (without the fungability however). +Our analysis of the interaction dynamics between the platform and non-human actors suggests that the current static pricing models are insufficient for an agent-mediated economy. If we assume a transition toward a direct revelation mechanism, where actors must reveal their true valuation of a good through bidding dynamics, we inevitably introduce significant stochasticity into the pricing system. Unlike traditional e-commerce where prices are relatively sticky, such a mechanism implies a high volatility characteristic of financial equity markets (without the fungibility however). -However, ecommerce commodities differ fundamentally from financial securities: they possess a hard floor defined by unit economics and reservation prices. The market might react enthusiastically to an iPhone priced at \$1, such a transaction is not permissible. The platform must establish an initial valuation anchor defined by the marginal cost plus a target margin, around which the market price is permitted to fluctuate. We float the introduction of GenAI Agents as Institutional Market Makers. As the arms race for greater autonomy of agnetic systems grows, the commercial viability of AI agents has the potential to disseminate into every-day users directly interacting with them rather than e-commerce platforms. This is also under the assumption of expected transactional capabilities being given to AI Agents. +However, e-commerce commodities differ fundamentally from financial securities: they possess a hard floor defined by unit economics and reservation prices. The market might react enthusiastically to an iPhone priced at \$1. Such a transaction is not permissible. The platform must establish an initial valuation anchor ($P_0$) defined by the marginal cost plus a target margin, around which the market price is permitted to fluctuate. + +We float the introduction of GenAI Agents as Institutional Market Makers. As the arms race for greater autonomy of agentic systems grows, the commercial viability of AI agents has the potential to disseminate into everyday users directly interacting with them rather than e-commerce platforms. This is also under the assumption of expected transactional capabilities being given to AI Agents. \subsection{Risk Assessment and Limitations} From 51de0cbdc5ce721472aeb57b5651255ee00bea34 Mon Sep 17 00:00:00 2001 From: Daniel Rosel Date: Thu, 9 Apr 2026 19:58:08 +0200 Subject: [PATCH 34/44] work summary and notes --- paper/src/chapters/01-intro.tex | 11 ++++++++--- paper/src/chapters/02-literature-review.tex | 1 + paper/src/chapters/03-methodology.tex | 1 + paper/src/chapters/04-results.tex | 12 +++++++++--- paper/src/chapters/05-discussion.tex | 1 + paper/src/chapters/06-conclusion.tex | 3 +++ 6 files changed, 23 insertions(+), 6 deletions(-) diff --git a/paper/src/chapters/01-intro.tex b/paper/src/chapters/01-intro.tex index 0fa16c8..f4b7a4d 100644 --- a/paper/src/chapters/01-intro.tex +++ b/paper/src/chapters/01-intro.tex @@ -7,6 +7,7 @@ %% \end{figure} \section{Introduction} +\label{sec:introduction} In this paper we present an exploration and defense against the presence of new commercial entities in digitally powered platforms, preserving market equilibrium in the age of AI. This research establishes the following contributions: definition and formalization of non-human transactors in e-commerce platforms, development of a testing-ground for capturing the behavioral essence of these transactors across a large variety of digital systems, construction of a discriminative model (to prove distinguishability) as a guiding teacher for downstream mitigation of contamination by non-human entities, translation of such learned distinguishability into existing dynamic pricing machine learning loops, and finally establishment of a high-level KPI-affecting causal effect and cost-saving framework for the future of internet commerce in the presence of such non-human learners. @@ -26,13 +27,14 @@ Dynamic pricing systems, as presented by \textcite{mueller_low-rank_2019}, often We formally define interaction data as coming from some actor which can either be an agent ($A$) or human ($H$). For purposes of this research, an agent is an algorithmic loop with the ability to access a web platform and perform actions such as clicks, scrolls, and input field fills. The loop terminates when the internal large language model judges the provided task definition as complete. A detailed breakdown can be found in \cref{algagent-loop}. \subsection{Research Questions} +\label{sec:research_questions} This dissertation is organized around one main research question and three supporting pillar questions: \begin{enumerate} \item[\textbf{Main RQ}] How can dynamic pricing systems preserve margin integrity when transaction orchestration is increasingly mediated by non-human agents? - \item[\textbf{SQ1}] \textit{Distinguishability}: Can agent and human sessions be reliably distinguished from behavioral interaction signals alone, without relying on network-level or device fingerprinting? - \item[\textbf{SQ2}] \textit{Theoretical Impact}: What is the formal relationship between agent contamination levels and the erosion of pricing power in dynamic pricing systems? - \item[\textbf{SQ3}] \textit{Robust Mitigation}: How can pricing policies be constructed to maintain margin integrity under unknown and non-stationary levels of agent contamination? + \item[\textbf{SQ1}] \hypertarget{sq1}{}\textit{Distinguishability}: Can agent and human sessions be reliably distinguished from behavioral interaction signals alone, without relying on network-level or device fingerprinting? + \item[\textbf{SQ2}] \hypertarget{sq2}{}\textit{Theoretical Impact}: What is the formal relationship between agent contamination levels and the erosion of pricing power in dynamic pricing systems? + \item[\textbf{SQ3}] \hypertarget{sq3}{}\textit{Robust Mitigation}: How can pricing policies be constructed to maintain margin integrity under unknown and non-stationary levels of agent contamination? \end{enumerate} @@ -65,3 +67,6 @@ Extract final result $r$ from terminal state\; The previously described goal of distinguishability allows us to formulate a task which entails taking raw interaction data for either actor and creating a composite demand estimate $\hat{q}$. We propose a robust optimization objective defined in our methodology, transforming the pricing problem into a form of distributionally robust optimization \parencite{kuhn_distributionally_2025} in which the learner guards against adversarial contamination in observed demand \emph{distributions}. The decision rule (in the policy) must perform when the data-generating mechanism is not a single known distribution but any member of an ambiguity set described only partially. Here that mechanism is a mixture whose weight and components need not be stationary. + +% The contributions of this thesis are best understood as a dependency chain centered on dynamic pricing under agent-mediated contamination. The work begins with a formal account of why non-human reconnaissance threatens pricing power, then constructs a controlled platform capable of generating the interaction data needed to study that threat empirically. On top of that substrate, session behavior is modeled to determine whether human and agent traffic can be separated from behavioral traces alone. The resulting contamination estimate is then translated into the pricing loop itself, where it serves as an operational signal for robust control under distributional uncertainty. The breadth of the thesis is therefore a consequence of the problem structure: the theoretical, behavioral, systems, and control components are not separate projects, but successive requirements of a single argument. +Our work's contributions are best understoo as a dependency chain centered around dynamic pricing. The work begins with a formal account of why a non human mediator threatens pricing power, then we construct a platform capable of generating the intraction data needed for our study of that threat. On to of that \textit{substrate} we build behaioral models to determine whether human and agent traffic can be separated. The resulting contamination estimate is then translated into the pricing core itself, where it serves as key signal for robust control under distributionall uncertainty. The breadth of the thesis is therefore a consequence of the problem structure: the theoretical, behavioral, systems, and control components are not separate projects, but successive requirements of a single argument. diff --git a/paper/src/chapters/02-literature-review.tex b/paper/src/chapters/02-literature-review.tex index 576fa09..aa618e6 100644 --- a/paper/src/chapters/02-literature-review.tex +++ b/paper/src/chapters/02-literature-review.tex @@ -1,4 +1,5 @@ \section{Literature Review} +\label{sec:literature_review} To situate the work we review agents and agentic computer use, web automation, economic reasoning, and strategic interaction, then turn to data-driven dynamic pricing under uncertainty. The main technical risk is not ``agents buying things'' in isolation but agents reshaping the behavioral and demand signals on which downstream pricing depends. Related litigation is already underway---for example \textcite{noauthor_amazoncom_2026} under the Computer Fraud and Abuse Act. Mediating actors surface classic concerns such as false-name bidding \parencite{yokoo_effect_2004} or pseudonymous re-entry which can whitewash reputation and weaken defenses \parencite{feldman_free-riding_2004}. Dynamic pricing assumes demand proxies are behaviorally meaningful, whereas classical bot detection targets security and access control. The gap we target is a principled way to separate non-human reconnaissance from genuine human demand expression and to fold that signal into pricing without degrading legitimate users (we track harm with a user-experience index), for the stakeholders named in the introduction. diff --git a/paper/src/chapters/03-methodology.tex b/paper/src/chapters/03-methodology.tex index fb21293..194cc00 100644 --- a/paper/src/chapters/03-methodology.tex +++ b/paper/src/chapters/03-methodology.tex @@ -1,4 +1,5 @@ \section{Methodology} +\label{sec:methodology} % Extra notes and clarifications: we observed some humans and get their transition probabilities between event types % We modify behavioral profiles of transition matrices with price elasticity matrices generated by sample valuations of a distributing. diff --git a/paper/src/chapters/04-results.tex b/paper/src/chapters/04-results.tex index 2c26b1e..db3edcc 100644 --- a/paper/src/chapters/04-results.tex +++ b/paper/src/chapters/04-results.tex @@ -1,4 +1,10 @@ \section{Results} +\label{sec:results} + +% The gap we target is not detection for its own sake but whether behavioral signals can support pricing decisions once agent traffic is present. This section follows the supporting questions in \cref{sec:research_questions}: we first establish session-level distinguishability (behavioral evidence and a rank test), then estimate how contamination shifts revenue in a controlled sweep, and finally compare robust and baseline policies under factorial training with COI and revenue readouts. The ordering is deliberate---each stage feeds the next so that separability, contamination effects, and policy outcomes form one connected line of evidence. + +In our work, the gap we target is not the detection for its own sake. Our aim is to understand behavioral signals which can support pricing decisions once agent traffic is present. Now we set to conclude and piece together the path we laid out in \cref{sec:research_questions}. We established distinguishability (behavioral evidence and test) that estimate how contamination shifts revenue in an adversarial environment and finally we compare robust and baseline pricing under factorial training. + \begin{figure}[ht] \centering \input{chapters/figures/supra/supra.tex} @@ -40,7 +46,7 @@ We report two preliminary stages before the full factorial interpretation. First \subsubsection{The Impact of Contamination on Revenue} -The contamination--revenue slope is estimated on a controlled cohort (single sweep, baseline policy, $n_{\text{products}}=100$, $n=95$). In this setting, contamination $\alpha$ is set exogenously by the experiment, so the slope identifies the within-sweep causal effect of contamination on revenue under fixed policy and environment settings. +The contamination--revenue slope is estimated on a controlled cohort (single sweep, baseline policy, $n_{\text{products}}=100$, $n=95$). In this setting, contamination $\alpha$ is set exogenously by the experiment, so the slope identifies the within-sweep causal effect of contamination on revenue under fixed policy and environment settings. These results are in favor of our second research question \hyperlink{sq2}{\textbf{SQ2}} (\textit{Theoretical Impact}) from \cref{sec:research_questions}. \begin{table}[ht] \centering @@ -95,11 +101,11 @@ In our complete training runs we logged $\approx 180$ days of net compute time. \subsection{Interpretation and Insights} -The Mann-Whitney result ($p<0.001$) confirms that per-session divergence gaps distinguish the two actor classes with near-zero overlap in rank ordering. This is the condition required for distinguishability to act as a useful control signal in the pricing loop rather than just an auxiliary classifier score. +The Mann-Whitney result ($p<0.001$) confirms that per-session divergence gaps distinguish the two actor classes with near-zero overlap in rank ordering. This is the condition required for distinguishability to act as a useful control signal in the pricing loop rather than just an auxiliary classifier score. This is a direct result relevant to our first pillar \hyperlink{sq1}{\textbf{SQ1}} (\textit{Distinguishability}) from \cref{sec:research_questions}. The first calibration and paired benchmark runs additionally confirm three practical points aligned with the thesis. First, the control loop is reproducible end-to-end (training, evaluation, artifact generation) across algorithms and contamination levels. Second, policy class materially changes price trajectories and resulting COI/revenue profiles under identical environment settings. Third, objective improvements from robustness are regime-dependent in the current baseline, which is consistent with the thesis claim that contamination-aware pricing needs explicit calibration rather than a one-size-fits-all penalty. -We also note that maximizing revenue in isolation can favor aggressive high-price behavior, even in our early runs, the non-robust aggregate shows slightly higher mean COI and margin. For this reason, all subsequent reporting in this thesis is interpreted on a multi-metric basis (objective, revenue, COI, and stability), and not by revenue alone. +We also note that maximizing revenue in isolation can favor aggressive high-price behavior, even in our early runs, the non-robust aggregate shows slightly higher mean COI and margin. For this reason, all subsequent reporting in this thesis is interpreted on a multi-metric basis (objective, revenue, COI, and stability), and not by revenue alone. This is another direct answer to our third pillar \hyperlink{sq3}{\textbf{SQ3}} (\textit{Robust Mitigation}) from \cref{sec:research_questions}. \subsection{Anomalies} diff --git a/paper/src/chapters/05-discussion.tex b/paper/src/chapters/05-discussion.tex index 4301eb4..b13486c 100644 --- a/paper/src/chapters/05-discussion.tex +++ b/paper/src/chapters/05-discussion.tex @@ -1,4 +1,5 @@ \section{Discussion} +\label{sec:discussion} % TODO: Gpdr here diff --git a/paper/src/chapters/06-conclusion.tex b/paper/src/chapters/06-conclusion.tex index cb0d174..1e6977a 100644 --- a/paper/src/chapters/06-conclusion.tex +++ b/paper/src/chapters/06-conclusion.tex @@ -1,8 +1,11 @@ \section{Conclusion} +\label{sec:conclusion} This thesis examined reinforcement-learning policies for dynamic pricing when a fraction of traffic is orchestrated by non-human agents intent on extracting information before purchase. We introduced COI-oriented metrics, a behavioral distinguishability layer, and a distributionally robust training loop; empirical runs show where robustness helps and where it must be tuned. \subsection{Summary of contributions} +Our work has yielded a broad set of dependencies which we carefully orchestrated to give us measurable results. To give a clear picture we outline the specific contributions of each stage of our work. The theoretical component formalizes why agent-mediated reconnaissance erodes pricing power, the behavioral component establishes that such contamination is detectable from interaction traces alone, the control component translates that distinguishability into a robust pricing mechanism, and the systems component provides the controlled experimental environment required to observe, test, and reproduce these effects. + \begin{itemize} \item TPU-accelerated parallelization of the behavioral simulation and reinforcement learning pipeline, making large factorial sweeps tractable. \item Formalization of non-human transaction orchestration in e-commerce as a distinct source of contamination in dynamic pricing systems. From 3eea137f49417f12930d55aba7b8d239b3c3c917 Mon Sep 17 00:00:00 2001 From: Daniel Rosel Date: Thu, 9 Apr 2026 20:19:39 +0200 Subject: [PATCH 35/44] fixing grammar --- paper/src/chapters/01-intro.tex | 3 +-- paper/src/chapters/03-methodology.tex | 5 +++-- paper/src/chapters/06-conclusion.tex | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/paper/src/chapters/01-intro.tex b/paper/src/chapters/01-intro.tex index f4b7a4d..5e3d50d 100644 --- a/paper/src/chapters/01-intro.tex +++ b/paper/src/chapters/01-intro.tex @@ -68,5 +68,4 @@ Extract final result $r$ from terminal state\; The previously described goal of distinguishability allows us to formulate a task which entails taking raw interaction data for either actor and creating a composite demand estimate $\hat{q}$. We propose a robust optimization objective defined in our methodology, transforming the pricing problem into a form of distributionally robust optimization \parencite{kuhn_distributionally_2025} in which the learner guards against adversarial contamination in observed demand \emph{distributions}. The decision rule (in the policy) must perform when the data-generating mechanism is not a single known distribution but any member of an ambiguity set described only partially. Here that mechanism is a mixture whose weight and components need not be stationary. -% The contributions of this thesis are best understood as a dependency chain centered on dynamic pricing under agent-mediated contamination. The work begins with a formal account of why non-human reconnaissance threatens pricing power, then constructs a controlled platform capable of generating the interaction data needed to study that threat empirically. On top of that substrate, session behavior is modeled to determine whether human and agent traffic can be separated from behavioral traces alone. The resulting contamination estimate is then translated into the pricing loop itself, where it serves as an operational signal for robust control under distributional uncertainty. The breadth of the thesis is therefore a consequence of the problem structure: the theoretical, behavioral, systems, and control components are not separate projects, but successive requirements of a single argument. -Our work's contributions are best understoo as a dependency chain centered around dynamic pricing. The work begins with a formal account of why a non human mediator threatens pricing power, then we construct a platform capable of generating the intraction data needed for our study of that threat. On to of that \textit{substrate} we build behaioral models to determine whether human and agent traffic can be separated. The resulting contamination estimate is then translated into the pricing core itself, where it serves as key signal for robust control under distributionall uncertainty. The breadth of the thesis is therefore a consequence of the problem structure: the theoretical, behavioral, systems, and control components are not separate projects, but successive requirements of a single argument. +Our work's contributions are best understood as a dependency chain centered around dynamic pricing. The work begins with a formal account of why a non-human mediator threatens pricing power, then we construct a platform capable of generating the interaction data needed for our study of that threat. On top of that \textit{substrate} we build behavioral models to determine whether human and agent traffic can be separated. The resulting contamination estimate is then translated into the pricing core itself, where it serves as a key signal for robust control under distributional uncertainty. The breadth of the thesis is therefore a consequence of the problem structure: the theoretical, behavioral, systems, and control components are not separate projects, but successive requirements of a single argument. diff --git a/paper/src/chapters/03-methodology.tex b/paper/src/chapters/03-methodology.tex index 194cc00..17b0071 100644 --- a/paper/src/chapters/03-methodology.tex +++ b/paper/src/chapters/03-methodology.tex @@ -504,9 +504,10 @@ The inner minimization selects the contamination candidate that makes the penali For the baseline engine reported here, we intentionally use the constant query-tax surrogate to keep the mechanism minimal: \begin{equation} -r_t = \ldots - \lambda\,f(\tau_t')\,c_{\text{info}} +\label{eq:baseline_step_reward} +r_t = R\!\left(p_t,\hat{Q}_t\right) - \lambda\,f(\tau_t')\,c_{\text{info}} - \eta_{\text{ux}}\,\text{UX}(\tau_t', p_t) \end{equation} -with fixed $c_{\text{info}}>0$. +with fixed $c_{\text{info}}>0$, matching the leakage term $\text{COI}_{\text{leak}}=f(\tau_t')\,c_{\text{info}}$ and the user-experience penalty already introduced in~\eqref{eq:robust_policy}. Another possible extension is to adapt the ambiguity radius online, e.g., $\epsilon(\Delta_H)$, so the Wasserstein ball changes with live divergence. We keep this as future work and retain a fixed-radius setup because Wasserstein ambiguity already handles heavy-tail and ``black swan'' behavior without absolute continuity assumptions \parencite{kuhn_wasserstein_2024}. diff --git a/paper/src/chapters/06-conclusion.tex b/paper/src/chapters/06-conclusion.tex index 1e6977a..76b5153 100644 --- a/paper/src/chapters/06-conclusion.tex +++ b/paper/src/chapters/06-conclusion.tex @@ -22,4 +22,4 @@ Our work has yielded a broad set of dependencies which we carefully orchestrated Several constraints are intentional and could be relaxed later. Action weights in the demand proxy are hand-set; learning them from data is an obvious next step. The Stackelberg interface assumes a clean alternation between platform move and market response; richer histories (multi-agent, multi-platform) would need a less rigid state definition. Non-perishable catalog supply in the simulator widens the sim-to-real gap for inventory-constrained domains. Within-session contamination is modeled as stable; time-varying $\alpha$ inside a session would better match some attack patterns. -Before any deployment, human baselines should grow beyond the convenience sample used here, and catalog scaling laws should be re-checked when transition matrices grow with SKU count. For the deployment of this methodology presented in our work. +Before any deployment, human baselines should grow beyond the convenience sample used here, catalog scaling laws should be re-checked when transition matrices grow with SKU count, and the full pipeline should be re-validated under production traffic volumes, governance constraints, and product mixes. From a3dc5125df447602abcd9b50d40a96fb86f876c5 Mon Sep 17 00:00:00 2001 From: Daniel Rosel Date: Thu, 9 Apr 2026 20:25:37 +0200 Subject: [PATCH 36/44] fix: typos and flow --- paper/src/chapters/03-methodology.tex | 6 +++--- paper/src/chapters/05-discussion.tex | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/paper/src/chapters/03-methodology.tex b/paper/src/chapters/03-methodology.tex index 17b0071..11dc265 100644 --- a/paper/src/chapters/03-methodology.tex +++ b/paper/src/chapters/03-methodology.tex @@ -140,9 +140,9 @@ This result implies that standard pricing policies $\pi$ cannot extract the same In order for our research to have grounding in interactions we built a robust e-commerce web-platform. In this framing Kappa represents streamed processing and Lambda batch operations as is given by terminology in big-data processing. We initially conducted a survey of the leading platforms of airlines and hotel booking sites to identify the specific interface patterns that effectively manage complex travel data. To better understand the playing field, we collected artifacts on design across various airlines and hotels. While both sectors rely on tabbed service selection and left-sidebar filtering to streamline navigation, they diverge in result presentation: airlines utilize visual date-price bars and multi-step wizards to optimize for logistical transparency, whereas hotel platforms leverage image-led cards and scarcity triggers to drive emotional engagement and urgency. Our web framework defines a highly agnostic boilerplate which can be seeded with any data-modality with an easy-to-tailor pattern, which we leverage to define a \texttt{hotel} and \texttt{airline} mode. Both modes are then individually deployed via an environment-level argument which adjusts the proxy routing with custom middleware in Next.js to render only the desired mode. The purpose of this was to create a baseline adaptable to any use-case or desired commercial application. -The architecture begins with deployed web applications posting interaction data to a backend that stores each record in Apache Kafka. Kafka acts as the reservoir linking sessions to experiments. Behavioral events and, separately, price observations from the pricing-provider microservice (invoked by the frontend) land in Kafka topics. A scheduled Airflow pipeline (with manual triggers) consumes the stream and the final pricing stage writes vectors to Redis for low-latency reads by the provider and display in the client. This design pattern allows us to generalize to other commercial settings, where Kafka is used for durability and replay, Redis for serving and quick queries. We invested in this stack to keep runs reproducible and to limit extraneous variance so the same skeleton applies across e-commerce settings +The architecture begins with deployed web applications posting interaction data to a backend that stores each record in Apache Kafka. Kafka acts as the reservoir linking sessions to experiments. Behavioral events and, separately, price observations from the pricing-provider microservice (invoked by the frontend) land in Kafka topics. A scheduled Airflow pipeline (with manual triggers) consumes the stream and the final pricing stage writes vectors to Redis for low-latency reads by the provider and display in the client. This design pattern allows us to generalize to other commercial settings, where Kafka is used for durability and replay, Redis for serving and quick queries. We invested in this stack to keep runs reproducible and to limit extraneous variance so the same skeleton applies across e-commerce settings. -\paragraph{Public Web Artifact} We transition the Kappa like architecture of the data collection to a Lambda architecture for actual learning in a surrogate environment. This allows us to move faster on data which is provided and helps us create a feedback loop for production deployment. To support further research in this intersection of fields we release P4P \footnote{\url{https://github.com/velocitatem/p4p}} as a public repository providing the interaction layer of the PHANTOM framework. This provides a configurable storefront which can be tailored to any commercial setting with a standardized session-level event tracking. We document the API adapters and expected schemas for pricing providers and log ingestion services. The repository is intended for controlled experimentation and method replication rather than production commerce deployment. +\paragraph{Public Web Artifact} We transition the Kappa-like architecture of the data collection to a Lambda architecture for actual learning in a surrogate environment. This allows us to move faster on data which is provided and helps us create a feedback loop for production deployment. To support further research in this intersection of fields we release P4P \footnote{\url{https://github.com/velocitatem/p4p}} as a public repository providing the interaction layer of the PHANTOM framework. This provides a configurable storefront which can be tailored to any commercial setting with a standardized session-level event tracking. We document the API adapters and expected schemas for pricing providers and log ingestion services. The repository is intended for controlled experimentation and method replication rather than production commerce deployment. \paragraph{Public Dataset} For reproducibility of the behavioral analysis and distinguishability experiments, we also release the interaction dataset used in this thesis as \textit{WhoClickedIt}. The dataset is hosted on Hugging Face \footnote{\url{https://huggingface.co/datasets/velocitatem/whoclickedit}} and is distributed as one flattened event sheet (\texttt{whoclicked.csv}) with explicit labels (\texttt{actor\_type}, \texttt{is\_agent}, and \texttt{record\_type}). The dataset card on that page documents the schema, collection process, and known limitations. @@ -498,7 +498,7 @@ In practice, we parameterize this with a session-level leakage term: \begin{equation} \text{COI}_{\text{leak}}(p,\tau') = f(\tau')\cdot \text{InfoValue}(p,\tau') \end{equation} -where $f(\tau')$ is the weak agent probability and $\text{InfoValue}$ is implemented either as a constant query-tax surrogate or as a revelation surrogate $-\log\pi(p\mid\tau')$. This is the surprise of the probability of a certain price-setting probability. Essentially, we proxy the leakage term as a surprise of the price our policy is setting, weighted by the contamination estimate. Appendix~\ref{app:revelation_log} expands on why the logarithm is used in the revelation surrogate. +where $f(\tau')$ is the weak agent probability and $\text{InfoValue}$ is implemented either as a constant query-tax surrogate or as a revelation surrogate $-\log\pi(p\mid\tau')$. This is the surprise of a certain price-setting probability. Essentially, we proxy the leakage term as a surprise of the price our policy is setting, weighted by the contamination estimate. Appendix~\ref{app:revelation_log} expands on why the logarithm is used in the revelation surrogate. The inner minimization selects the contamination candidate that makes the penalized reward smallest, so the outer policy update faces the worst plausible leakage scenario inside the ambiguity set rather than an average case. diff --git a/paper/src/chapters/05-discussion.tex b/paper/src/chapters/05-discussion.tex index b13486c..7e7cb5f 100644 --- a/paper/src/chapters/05-discussion.tex +++ b/paper/src/chapters/05-discussion.tex @@ -21,6 +21,6 @@ Behavior-based pricing raises predictable ethics questions when models are opaqu We balance human and agent sessions near one-to-one so cohorts are comparable despite different population sizes. The row-level dataset still contains thousands of events. % Rapid change in agent capabilities and user expectations induces model drift; the UX term in reward shaping was included partly to penalize policies that sacrifice legitimate users for short-run revenue. Reinforcement learning adds its own risks---reward hacking and limited interpretability---which matter when policies touch live revenue; deployment would require monitoring and constraints beyond what we exercised here. -With the exponential growth in capability of agents aswell as user expectations, a degree of model drift is expected in this setting. The computational requirements for continuous extraction of margin as demonstrated by our work are required by the persistent speed of the market. Reinforcement learning that sacrifices legitimate user experience for short run revenue does not hold up in the long run. Reward hacking, to which pricing algorithms are not impervious due to their limited interpretability is a significant risk for a company if live revenue is in play. Deployment requires consistent monitoring and constraints beyond what was done as exercise in this work. +With the exponential growth in capability of agents aswell as user expectations, a degree of model drift is expected in this setting. The computational requirements for continuous extraction of margin as demonstrated by our work are required by the persistent speed of the market. Reinforcement learning that sacrifices legitimate user experience for short run revenue does not hold up in the long run. Reward hacking, to which pricing algorithms are not impervious due to their limited interpretability, is a significant risk for a company if live revenue is in play. Deployment requires consistent monitoring and constraints beyond what was done as an exercise in this work. % \subsection{Implications of Findings} Interpretation of results and altenrative scenarios with broader market implications. From 0fd145901149abbd90e56a550da0af1868b7e73c Mon Sep 17 00:00:00 2001 From: Daniel Rosel Date: Thu, 9 Apr 2026 22:03:53 +0200 Subject: [PATCH 37/44] fixing summary top --- paper/src/summary.tex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/paper/src/summary.tex b/paper/src/summary.tex index 17151eb..f400853 100644 --- a/paper/src/summary.tex +++ b/paper/src/summary.tex @@ -21,7 +21,7 @@ \vspace{0.75em} -Large language model (LLM) agents are spreading in e-commerce; one consequence is intermediaries that can separate information gathering from transaction execution. +Large language model (LLM) agents are spreading in e-commerce, one consequence is intermediaries that can separate information gathering from transaction execution. This thesis studies dynamic pricing when agents reconnoitre in isolated sessions and thereby weaken the \emph{Cost of Information} (COI), the premium platforms typically extract once demand signals are expressed. The key technical risk is not ``agents buying things'' per se, but agents shaping the behavioral and demand signals that downstream pricing systems consume and depend on \parencite{xia_evaluation-driven_2025}. Dynamic pricing assumes demand proxies are behaviorally meaningful, while bot detection aims at security and access control. From f027ac173603ecb55e0631bf671f46ed8a7cc079 Mon Sep 17 00:00:00 2001 From: Daniel Rosel Date: Thu, 9 Apr 2026 23:07:56 +0200 Subject: [PATCH 38/44] updating with TOC --- paper/src/chapters/mdp_agent.pdf | Bin 10932 -> 10932 bytes paper/src/chapters/mdp_human.pdf | Bin 11953 -> 11953 bytes paper/src/main.tex | 2 ++ scripts/nx_paper.sh | 20 +++++++++++++++++--- 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/paper/src/chapters/mdp_agent.pdf b/paper/src/chapters/mdp_agent.pdf index d06b268a806d678feb84f41fa9862c84fc58acec..23cb6e410d59733ee28fbc90b1222f23d31e729e 100644 GIT binary patch delta 291 zcmV+;0o?wyRkT&GeJFoXYr-%Th2Q%t&dZoGXp>m0mEwaf$QT1r-^Lz7dZ~rclq4PV z-!G}wG4gaj?#Vfa%fTX^VaXx^zT^>KVX?}hqCj%K_Jrgh8?q6Ao~>i;#_YxNix#L$o|qQ8*6{l%;&dn3`y7Ou9A*0 zt2Tm(jHN6WVv%zG!o)LU^U35d7M{Fy&>y6skRv1;!8_Hh-02@5m-#2wX%3$8{1Llz zUSN?v6nmqG9z_v`FyCB8I-!%NMs~IjZt6PWS2#1!G?jj{4{vj~&en%RmXsu%d*6tr%fzEY}&yb0n90r;t>lR+a!r-kPh$2RJBB_#9B7 z`__Sx{eDnXMqhiW3>H!}MuWv{6Q@|*oa^i^Nd{UQ(cKzpV{L!xC>p;pO1MV1rEm(1 za>pspXiB$i%_!wBls{7%O(uV_@Z_z7z7j@-93j~N-qp1c2mRyYQvdiS-GXIoJI8LF z7g(gT;%Ic=AbsHZbUXy;QwM%hR`*6K pi{d7D##cEQQyWMLK`gZgAK;)k;&Vh>Gjtw|tkt4Hk42Z2NJQ0%Q4 zden6s;(T)%>4Z*!TG6>ay2#%9?BhAbo2piw_7eobWlIQunO`kG9)E zaWuN>6&b`zHb#Rde3dK^U!CjhCQS!g8`)hOWfN`c*=+pAkaT~IZuim=;{8T2k+B6^ z^PDp#UYK}hY&Mzv#ln-f4*H`s6mo=Q19+#Zkvsk4<1+umI$J?x%lr|$bzWexcqk4= z_Z^BN3}GH!20Ed$r$)B63vTK*;a51*(=?TSYfs;eqSIj)pidq6iLCC8B8&OM^X!Gn ijp41o!@q*1bn*{N*Y|$@2KRb?=@&2 + latexmk -g "${common[@]}" "$tex" + } +} + case "$cmd" in build) mkdir -p paper/build @@ -34,7 +48,7 @@ case "$cmd" in bash paper/concat_code.sh cd paper/src link_build_bib - latexmk -pdf -jobname=main -f -interaction=nonstopmode -file-line-error -r ../.latexmkrc -outdir=../build main.tex + latexmk_paper main main.tex ;; watch) mkdir -p paper/build @@ -63,7 +77,7 @@ case "$cmd" in sync_mdp_figures cd paper/src link_build_bib - latexmk -pdf -jobname=main-genpop -f -interaction=nonstopmode -file-line-error -r ../.latexmkrc -outdir=../build main-genpop.tex + latexmk_paper main-genpop main-genpop.tex ;; watch-genpop) mkdir -p paper/build @@ -85,7 +99,7 @@ case "$cmd" in mkdir -p paper/build cd paper/src link_build_bib - latexmk -pdf -jobname=summary -f -interaction=nonstopmode -file-line-error -r ../.latexmkrc -outdir=../build summary.tex + latexmk_paper summary summary.tex ;; watch-summary) mkdir -p paper/build From ece06a5a50c2d699b07dcb2883708dd50b6ee537 Mon Sep 17 00:00:00 2001 From: Daniel Rosel Date: Fri, 10 Apr 2026 08:19:37 +0200 Subject: [PATCH 39/44] chore: fixing kafka in summary --- paper/src/summary.tex | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/paper/src/summary.tex b/paper/src/summary.tex index f400853..aa14fcc 100644 --- a/paper/src/summary.tex +++ b/paper/src/summary.tex @@ -53,10 +53,10 @@ As the number of independent competitive agents $N$ querying the system grows, t \vspace{0.5em} In order for our research to have grounding in interactions we built a robust e-commerce web-platform. -The architecture of this platform begins with the deployed web-apps posting interaction data to our backend which processes them and stores each ingested interaction into a kafka cluster. +The architecture of this platform begins with the deployed web-apps posting interaction data to our backend which processes them and stores each ingested interaction into a Kafka cluster. This serves as our data reservoir tracking and associating each interaction with its session and importantly with which experiment it belongs to. -Not only do we track the behavioral interactions, but our pricing provider micro-service, once called by the frontend reports the observed/queried price-product into kafka. -This kafka cluster is subscribed to by our pipeline which is configured on a schedule in Airflow, with the possibility of manual trigger. +Not only do we track the behavioral interactions, but our pricing provider micro-service, once called by the frontend reports the observed/queried price-product into Kafka. +This Kafka cluster is subscribed to by our pipeline which is configured on a schedule in Airflow, with the possibility of manual trigger. The final stage of the pricing pipeline, submits computed dynamic pricing results into a redis database for quick updates which is then read by the pricing provider and displayed on the webapp. This is a very generic end-to-end mechanism which is applicable to a variety of different e-commerce tasks. We intentionally put emphasis on the development of this infrastructure to establish a reproducible framework for interaction and to minimize any noise. From 5460f34426c6380d9a68c564be40f74d61d37699 Mon Sep 17 00:00:00 2001 From: Daniel Rosel Date: Fri, 10 Apr 2026 08:24:03 +0200 Subject: [PATCH 40/44] feat;i mprocving setup --- SETUP.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/SETUP.md b/SETUP.md index d2c37e0..0fc72a9 100644 --- a/SETUP.md +++ b/SETUP.md @@ -129,7 +129,7 @@ The platform **observes** behavioral proxies and quoted prices, not the latent d **Paths for data and runs:** Override with `PHANTOM_DATA_DIR`, `PHANTOM_SIM_RUNS_DIR`, `PHANTOM_MODEL_REGISTRY_DIR`, `PHANTOM_COLLECTED_DATA_DIR`, etc. (`[lib/config.py](lib/config.py)`). -**Honest scope:** A new vertical (custom product ontology, checkout rules, pricing rules) means **new UI, events, and possibly new reward features** in the engine. Budget engineering time; the repo is a research platform, not a turnkey SaaS skin for arbitrary catalogs without code changes. +**Scope:** A new vertical (custom product ontology, checkout rules, pricing rules) means **new UI, events, and possibly new reward features** in the engine. Budget engineering time; the repo is a research platform, not a turnkey SaaS skin for arbitrary catalogs without code changes. ### Theoretical implications @@ -250,11 +250,11 @@ Non-stationary noise $\epsilon_t$ and drifting $\alpha$ confound benchmark inter --- -## 14. Roadmap / gaps (honesty) +## 14. Roadmap and gaps -**Relatively turnkey:** Local dockerized stack, demo verticals, engine benchmarks, documented env and paths. +**In repo:** Local dockerized stack, demo verticals, engine benchmarks, documented env and paths. -**Typically custom:** Production catalog without Supabase, identity/fraud layers, legal review of logging, Kafka/Airflow SLAs, hardening the pricing provider for real money. +**Usually custom:** Production catalog without Supabase, identity/fraud layers, legal review of logging, Kafka/Airflow SLAs, hardening the pricing provider for real money. **Thesis vs code:** The PDF is the **spec**; not every robustness term or large-catalog kernel construction is production-verified—see caveats in **Chapter 3**. @@ -295,4 +295,4 @@ Use the **PDF table of contents** with these anchors: | `[tpu_orchestration/](tpu_orchestration/)` | TPU configs and helpers. | -You do **not** need a running storefront for many **offline** benchmarks if the research Python environment is installed; you **do** need aligned instrumentation to connect production trajectories to kernel estimation. \ No newline at end of file +Many offline benchmarks run without a storefront once the research Python environment is installed; connecting production trajectories to kernel estimation still requires aligned instrumentation. \ No newline at end of file From 6427ae63ec4416006898e1577e8c81462b490d4a Mon Sep 17 00:00:00 2001 From: Daniel Rosel Date: Fri, 10 Apr 2026 08:30:48 +0200 Subject: [PATCH 41/44] fixing paper build --- Makefile | 4 ++-- paper/.latexmkrc | 18 ++++++++++++++++ paper/src/chapters/mdp_agent.pdf | Bin 10932 -> 10931 bytes paper/src/chapters/mdp_human.pdf | Bin 11953 -> 11953 bytes scripts/nx_paper.sh | 35 ++++++++++++++++++------------- 5 files changed, 40 insertions(+), 17 deletions(-) diff --git a/Makefile b/Makefile index 315651e..cc9711f 100644 --- a/Makefile +++ b/Makefile @@ -253,8 +253,8 @@ test: count-lines: @$(NX) run research:stats -all: - @$(NX) run paper:build +# Default artifact set for this repo: thesis PDF (same as pdf). +all: pdf .PHONY: manim.defense manim.defense.hq manim.render manim.render.full manim.render.poster manim.render.appendix manim.render.all # Main defense reel (paper/defense/manim/render_defense); uses paper/defense/.venv when present diff --git a/paper/.latexmkrc b/paper/.latexmkrc index e1b5f1f..6ee0b36 100644 --- a/paper/.latexmkrc +++ b/paper/.latexmkrc @@ -4,5 +4,23 @@ $bibtex_use = 2; # run biber when biblatex .bcf changes # biber cwd is paper/build; scripts/nx_paper.sh symlinks ../build/bib -> ../src/bib so # datasources log as bib/references.bib and latexmk's -e check works from paper/src $biber = 'biber %O %S'; + +# Stale latexmk db: biblatex uses biber + .bcf, but the fdb can keep a "bibtex" rule after a bad +# run. Then biber never runs and citations stay undefined. Read whole fdb (small) so the rule +# line is never missed after a long dependency list. +for my $job (qw(main main-genpop summary)) { + my $bcf = "../build/$job.bcf"; + my $bbl = "../build/$job.bbl"; + my $fdb = "../build/$job.fdb_latexmk"; + next unless -e $fdb && -e $bcf; + my $drop = !-e $bbl; + if ( !$drop && open my $fh, '<', $fdb ) { + local $/; + my $body = <$fh>; + close $fh; + $drop = 1 if defined $body && $body =~ /\["bibtex $job"\]/; + } + unlink $fdb if $drop; +} $pdf_previewer = 'zathura %O %S'; $clean_ext = 'synctex.gz bbl bcf run.xml fls fdb_latexmk glg glo gls ist blg lof lot out toc'; diff --git a/paper/src/chapters/mdp_agent.pdf b/paper/src/chapters/mdp_agent.pdf index 23cb6e410d59733ee28fbc90b1222f23d31e729e..21460dfb20d52faa2eb8283c3f01db8a7948e373 100644 GIT binary patch delta 320 zcmV-G0l)sVRkKyFe<**0FcgK~{T1hBOc`jR*4iR{s6rWIAZ_2q9zqOO7)Fvf)_=c5 zt7GKpe%zCD4wr&>G{c5?G3bVRbc&Hg zT@@!K#{os*1~<TxcY# z=fw)SnZs*)hkp|VOJ>-YY#;sX4IcIE(l7M zf{Bc!EEi&ta{j`^Gh_3~@whwOVI^kD1Gte}ZeygJIR?%_43(%(y{76<0Mv=wh z;d$~xp^{s#AYe(4wbe{V>WkSQMmI53m3Db5VVKrzi-3=9C} TxCuOyiz*-pG72RnMNdWw1C5>E diff --git a/paper/src/chapters/mdp_human.pdf b/paper/src/chapters/mdp_human.pdf index 3aa8ceb78da934f89db4f5eff70f5d330f5e6750..e3247c8c7f361640ee1b6dcb1e61a52b10c60122 100644 GIT binary patch delta 291 zcmV+;0o?wvU9nxTdn|v!YQr!PMDO~Fxs*B-Y)N)(H<%ofKq#dUlHN)WLKcpoSVodd z^Y@jUIHl;cj~&en%RmV$v8059Eg4}I@~R|FgY5k>C}aoGDMtWguex>k00+eppA*`e zvG-tPy`B^YYuZsNi$n@-4Oq(Rv_Nw9esHTSn`mdnaMseL#&&;;#q}Gjgx99u39pds zR-E#Z7L3w5r>uCQ{E^bdZ1NWi55YR>55lULBPN@`n`*V#n4b`rhR2`F8oXRq_t?D; z0*B(R*jqCWXqq_0`RX#!8Jz;PqIW}dlb0F4z}r#H(>84F)VG%ExZgw=Qb&GN&Wu(n phy3n&3PS0x;awKE!@q)C;l-EfZvDayZuP>_FZlFtNVAVEA^{pfl&$~( delta 291 zcmV+;0o?wvU9nxTdn|uZYr-%Th2Qfl&dXS3&?aeZRf-QP$QT1r-^Lz78fsuPB}vEp z_e-jEj6B_sdveaRU^7+;1WbsUtrsX9g>k pL;mnQ1)=n}@GchI;a|b6@Z!s~_kQ69_j+OJ7yfr|O|y?JA^~Xfkyro# diff --git a/scripts/nx_paper.sh b/scripts/nx_paper.sh index 44bce3c..9a864c9 100644 --- a/scripts/nx_paper.sh +++ b/scripts/nx_paper.sh @@ -27,18 +27,17 @@ link_build_bib() { ln -sfn ../src/bib ../build/bib } -# latexmk can exit non-zero with "biber ... gave an error in previous invocation" while the PDF is -# already fine: incremental runs skip biber but keep a stale failure bit in main.fdb_latexmk. -# One forced cycle (-g) re-runs biber and clears that state. -latexmk_paper() { - local job="$1" - local tex="$2" - local -a common - common=( -pdf -jobname="$job" -f -interaction=nonstopmode -file-line-error -r ../.latexmkrc -outdir=../build ) - latexmk "${common[@]}" "$tex" || { - printf '%s\n' "latexmk failed; retrying once with -g (clear stale biber/latexmk state)" >&2 - latexmk -g "${common[@]}" "$tex" - } +# Biblatex uses biber; a stale latexmk fdb can still record ["bibtex "], so latexmk skips +# biber, main.bbl is missing or wrong, and every citation stays undefined. Drop only that case. +drop_stale_latexmk_bibtex_fdb() { + local job fdb tag + for job in main main-genpop summary; do + fdb="../build/${job}.fdb_latexmk" + tag=$(printf '["bibtex %s"]' "$job") + if [[ -f "$fdb" ]] && grep -Fq "$tag" "$fdb"; then + rm -f "$fdb" + fi + done } case "$cmd" in @@ -48,13 +47,15 @@ case "$cmd" in bash paper/concat_code.sh cd paper/src link_build_bib - latexmk_paper main main.tex + drop_stale_latexmk_bibtex_fdb + latexmk -pdf -jobname=main -f -interaction=nonstopmode -file-line-error -r ../.latexmkrc -outdir=../build main.tex ;; watch) mkdir -p paper/build sync_mdp_figures cd paper/src link_build_bib + drop_stale_latexmk_bibtex_fdb latexmk -pvc -pdf -jobname=main -f -interaction=nonstopmode -file-line-error -r ../.latexmkrc -outdir=../build main.tex ;; clean) @@ -77,13 +78,15 @@ case "$cmd" in sync_mdp_figures cd paper/src link_build_bib - latexmk_paper main-genpop main-genpop.tex + drop_stale_latexmk_bibtex_fdb + latexmk -pdf -jobname=main-genpop -f -interaction=nonstopmode -file-line-error -r ../.latexmkrc -outdir=../build main-genpop.tex ;; watch-genpop) mkdir -p paper/build sync_mdp_figures cd paper/src link_build_bib + drop_stale_latexmk_bibtex_fdb latexmk -pvc -pdf -jobname=main-genpop -f -interaction=nonstopmode -file-line-error -r ../.latexmkrc -outdir=../build main-genpop.tex ;; build-arxiv) @@ -99,12 +102,14 @@ case "$cmd" in mkdir -p paper/build cd paper/src link_build_bib - latexmk_paper summary summary.tex + drop_stale_latexmk_bibtex_fdb + latexmk -pdf -jobname=summary -f -interaction=nonstopmode -file-line-error -r ../.latexmkrc -outdir=../build summary.tex ;; watch-summary) mkdir -p paper/build cd paper/src link_build_bib + drop_stale_latexmk_bibtex_fdb latexmk -pvc -pdf -jobname=summary -f -interaction=nonstopmode -file-line-error -r ../.latexmkrc -outdir=../build summary.tex ;; *) From b69c3a87fd446714f90cd5fcbde0de05e567c670 Mon Sep 17 00:00:00 2001 From: Daniel Rosel Date: Fri, 10 Apr 2026 10:48:33 +0200 Subject: [PATCH 42/44] fixing typos and inconsistencies --- paper/src/mirrors/genpop/03-methodology.tex | 2 +- paper/src/summary.tex | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/paper/src/mirrors/genpop/03-methodology.tex b/paper/src/mirrors/genpop/03-methodology.tex index d6595e1..cd1f93f 100644 --- a/paper/src/mirrors/genpop/03-methodology.tex +++ b/paper/src/mirrors/genpop/03-methodology.tex @@ -83,7 +83,7 @@ In order for our research to have grounding in interactions we built a robust e- The architecture of this platform begins with the deployed web-apps posting interaction data to our backend which processes them and stores each ingested interaction into a kafka cluster. This serves as our data reservoir tracking and associating each interaction with its session and importantly with which experiment it belongs to. Not only do we track the behavioral interactions, but our pricing provider micro-service, once called by the frontend reports the observed/queried price-product into kafka. This kafka cluster is subscribed to by our pipeline which is configured on a schedule in Airflow, with the possibility of manual trigger. The final stage of the pricing pipeline, submits computed dynamic pricing results into a redis database for quick updates which is then read by the pricing provider and displayed on the webapp. This is a very generic end-to-end mechanism which is applicable to a variety of different e-commerce tasks. We intentionally put emphasis on the development of this infrastructure to establish a reproducible framework for interaction and to minimize any noise. -\paragraph{Public Web Artifact} We transition the Kappa like architecture of the data collection to a Lambda architecture for actual learning in a surrogate environment. This allows us to move faster on data which is provided and helps us create a feedback loop for production deployment. To support further research in this intersection of fields we release P4P \footnote{\url{https://github.com/velocitatem/p4p}} as a public repository providing the interaction layer of the PHANTOM framework. This provides a configurable storefront which can be tailored to any commercial setting with a standardized session-level event tracking. We document the API adapters or what the framework expects in terms of schemas for pricing providers and log ingestion servicse. The repository is intended for controlled experimentation and method replication rather than production commerce deployment. +\paragraph{Public Web Artifact} We transition the Kappa-like architecture of the data collection to a Lambda architecture for actual learning in a surrogate environment. This allows us to move faster on data which is provided and helps us create a feedback loop for production deployment. To support further research in this intersection of fields we release P4P \footnote{\url{https://github.com/velocitatem/p4p}} as a public repository providing the interaction layer of the PHANTOM framework. This provides a configurable storefront which can be tailored to any commercial setting with a standardized session-level event tracking. We document the API adapters or what the framework expects in terms of schemas for pricing providers and log ingestion servicse. The repository is intended for controlled experimentation and method replication rather than production commerce deployment. \subsubsection{DevOps Principles} diff --git a/paper/src/summary.tex b/paper/src/summary.tex index aa14fcc..8824abd 100644 --- a/paper/src/summary.tex +++ b/paper/src/summary.tex @@ -39,7 +39,7 @@ In this paper we present an exploration and defense against the presence of new We formally define interaction data as coming from some actor which can either be an agent ($A$) or human ($H$). Dynamic pricing algorithms rely on directly translating demand features $q$ to new price assignments $\hat{p}$ across a catalogue of products of size $N$. This opens opportunities to design a \textit{tabula rasa} of digital market mechanisms that will shape the future of commerce in the age of artificial intelligence. -We propose a robust optimization objective defined in our methodology, transforming the pricing problem into a form of Distributionally Robust Optimization \parencite{kuhn_distributionally_2025} where the learner must guard against adversarial contamination in observed demand distributors. +We propose a robust optimization objective defined in our methodology, transforming the pricing problem into a form of Distributionally Robust Optimization \parencite{kuhn_distributionally_2025} where the learner must guard against adversarial contamination in observed demand distributions. For purposes of this research, an agent is an algorithmic loop with the ability to access a web platform and perform actions such as clicks, scrolls, and input field fills. \vspace{0.5em} @@ -63,7 +63,7 @@ We intentionally put emphasis on the development of this infrastructure to estab In addition to behavioral events, the platform logs price observations to a separate Kafka topic. Each price query generates a record $(i, p, \text{sid}, \phi, t)$ associating the product, displayed price, requesting session, platform mode, and timestamp. This dual-stream architecture enables joint analysis of price exposure and behavioral response. -We transition the Kappa like architecture of the data collection to a Lambda architecture for actual learning in a surrogate environment. +We transition the Kappa-like architecture of the data collection to a Lambda architecture for actual learning in a surrogate environment. This allows us to move faster on data which is provided and helps us create a feedback loop for production deployment. Operationally, goals and experiment runs are tracked in PostgreSQL (goal table, run table, and assignment mapping). This data-acquisition phase is the first half of the methodology and is intentionally a disconnected component that feeds the later contributions. @@ -83,7 +83,7 @@ We utilize the Wasserstein distance metric to define the set of plausible demand The robust policy $\pi^*$ is obtained by solving the maximin problem $\pi^* = \arg \max_{\pi} \min_{Q \in \mathcal{U}_\epsilon} \mathbb{E}_{d \sim Q} \left[ R(p, d) - \lambda \cdot \text{COI}_{\text{leak}}(p,\tau') - \eta_{\text{ux}} \cdot \text{UX}(\tau', p) \right]$ where $R(p, d)$ is the revenue function, $\lambda$ weighs the information-leakage penalty, and $\eta_{\text{ux}}$ weighs the UX term. In practice, we parameterize this with a session-level leakage term $\text{COI}_{\text{leak}}(p,\tau') = f(\tau')\cdot \text{InfoValue}(p,\tau')$ where $f(\tau')$ is the weak agent probability. As part of reward engineering, we keep a UX factor ($UX\in[0,1]$) as an auxiliary evaluation axis. -Our training budget is provisioned through TPU Research Cloud and spans 384 chips across TPU v4, v5e, and v6e generations, with a spot-heavy allocation plus an on-demand reserve. +Our training budget is provisioned through TPU Research Cloud and spans 320 chips across TPU v4, v5e, and v6e generations, with a spot-heavy allocation plus an on-demand reserve. At peak BF16 throughput this corresponds to approximately $160$\,PFLOPS of aggregate compute. \vspace{0.5em} From 03b4996bea715f5876170c31f75c28329a4184fe Mon Sep 17 00:00:00 2001 From: Daniel Rosel Date: Fri, 10 Apr 2026 11:27:15 +0200 Subject: [PATCH 43/44] moer on future work --- paper/src/chapters/06-conclusion.tex | 6 ++++-- paper/src/chapters/mdp_agent.pdf | Bin 10931 -> 10932 bytes paper/src/chapters/mdp_human.pdf | Bin 11953 -> 11953 bytes 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/paper/src/chapters/06-conclusion.tex b/paper/src/chapters/06-conclusion.tex index 76b5153..1c87465 100644 --- a/paper/src/chapters/06-conclusion.tex +++ b/paper/src/chapters/06-conclusion.tex @@ -1,7 +1,7 @@ \section{Conclusion} \label{sec:conclusion} -This thesis examined reinforcement-learning policies for dynamic pricing when a fraction of traffic is orchestrated by non-human agents intent on extracting information before purchase. We introduced COI-oriented metrics, a behavioral distinguishability layer, and a distributionally robust training loop; empirical runs show where robustness helps and where it must be tuned. +This thesis examined reinforcement-learning policies for dynamic pricing when a fraction of traffic is orchestrated by non-human agents intent on extracting information before purchase. We introduced COI-oriented metrics, a behavioral distinguishability layer, and a distributionally robust training loop, empirical runs show where robustness helps and where it must be tuned. \subsection{Summary of contributions} Our work has yielded a broad set of dependencies which we carefully orchestrated to give us measurable results. To give a clear picture we outline the specific contributions of each stage of our work. The theoretical component formalizes why agent-mediated reconnaissance erodes pricing power, the behavioral component establishes that such contamination is detectable from interaction traces alone, the control component translates that distinguishability into a robust pricing mechanism, and the systems component provides the controlled experimental environment required to observe, test, and reproduce these effects. @@ -20,6 +20,8 @@ Our work has yielded a broad set of dependencies which we carefully orchestrated \subsection{Limitations and future work} -Several constraints are intentional and could be relaxed later. Action weights in the demand proxy are hand-set; learning them from data is an obvious next step. The Stackelberg interface assumes a clean alternation between platform move and market response; richer histories (multi-agent, multi-platform) would need a less rigid state definition. Non-perishable catalog supply in the simulator widens the sim-to-real gap for inventory-constrained domains. Within-session contamination is modeled as stable; time-varying $\alpha$ inside a session would better match some attack patterns. +Several constraints are intentional and could be relaxed later. Action weights in the demand proxy are currently derived from simple divergence rankings, learning them from data is an obvious next step. We propose a jointly learn the demand proxy, policy, and simulator parameters instead of treating them modularly. Another avenue we could not cover in this work is incorporating Bayesian methods better capture demand uncertainty and propagation of that uncertainty into reward systems. +The Stackelberg interface assumes a clean alternation between platform move and market response. Richer histories (multi-agent, multi-platform) would need a less rigid state definition. Non-perishable catalog supply in the simulator widens the sim-to-real gap for inventory-constrained domains. Within-session contamination is modeled as stable, time-varying $\alpha$ inside a session would better match some attack patterns. Before any deployment, human baselines should grow beyond the convenience sample used here, catalog scaling laws should be re-checked when transition matrices grow with SKU count, and the full pipeline should be re-validated under production traffic volumes, governance constraints, and product mixes. +We conclude our work with enthusiasm for future developments in the field of agent mediated commerce, we are excited to provide the foundations for these developments and hope to see future work in similar spirit. diff --git a/paper/src/chapters/mdp_agent.pdf b/paper/src/chapters/mdp_agent.pdf index 21460dfb20d52faa2eb8283c3f01db8a7948e373..83e10d3e525abf81b3b745ea82e4aff20b7db528 100644 GIT binary patch delta 323 zcmV-J0lfaRRkT&GeJFp;YQr!PgztWexs)apY^~xrKbRb1Lnx(SlH5uULJ^LkSVodd z^Y)eOIHl;cA3K^EmcSy)uw)SjEh(ZE7OO1Eb0p_mr;t>lRyG2Vyfqh#4{$IX@j0ME z_pJj(yWOCujK1_z8O)?;j0VNDj#JF8&UJR1Bm=FD=&p^lu{MA8d_I0-lyHr1OW_n| z<(4y^v6L7QU(w=)@n^>7lgVE!JbCM&uY^${M@Tk+cXesRUjO*G%s;+PH=v9*kJzpA z0*myaI2he`$n!9Sd2|`*gif9s(b_J!$=ifq;Y_clsqkB+`fj8;9`*tH)PWzB)xD9* zV)5`id7*S;cy`C7Y281)sA{NzjnyVG93d%%JyCG|^VE;nr~-O4WY`!TgO9%yhD^m=W-+ zeN5tvq?C|6AvAp<@iQU8V)7RYPuAM$Yo>(D;gU_@T{Im#${!n-*ca#N4m6{?HFj^T zKqFlhCnd)LMd1cF&n^>P(8W^4dObKdeqZn_j2gu@=7hl0Yb>5R%?X4?OtIX&*b98CHNZ zkz>i3gjfnD3e1a~l_j$GYe&eAswT$(WUo!-@Bs;q6FvvDGkxnJ*k&^*j@DGYCW}O? z#u|uJtkMONi}#&dXW2k&tGY|0U21GSn~mRCQob?mUU@>YUrT>3bG{H<2w8~v6PJ&i z&nA<0B=+^YHNN%Tppf$&R3B6vbe`?eGoV-?uvsoeTTA)L!3vK zfllZgs8y}&qMN=>_yykfG)=p(wWse!)9J8{Fr<$BM9%b9lf(S(c@9GD$M7!P;a|a> a^6Jafw|?OUw|WO*=@<9bZ%4C_Eg}KMXNZpg delta 276 zcmV+v0qg#;U9nxTiY$M2Y&V!3l0Yb>5R%?X4?-4>pjbwdOY`@YoH(WEw2vLl49h?X zE3u@6gDn|h74oViO@r+HGALvR(J4m&WUsn)_y7mR5uX#a;*|_I_}yESqR&#cJP%Im?I{ez?*8d*qEOXmxjlm%No2~R`=Mw4+4kcuGm{M4rrP<#QEwn z(HWfrwW4=Jbd#4Ezrfp3&C@n)?bNrH>bT!T7*a=mRL+c6Du?{;c?v@5ui;&|!@q)C a;l-EfZvDayZuJMk(l7Y*Z%DI`Eg}IfNR4>_ From d36a34ead97b1c61960be68b9e30ad5559c663cb Mon Sep 17 00:00:00 2001 From: Daniel Rosel Date: Fri, 10 Apr 2026 11:29:21 +0200 Subject: [PATCH 44/44] updating appendix --- paper/src/chapters/mdp_agent.pdf | Bin 10932 -> 10932 bytes paper/src/chapters/mdp_human.pdf | Bin 11953 -> 11953 bytes paper/src/main.tex | 2 +- 3 files changed, 1 insertion(+), 1 deletion(-) diff --git a/paper/src/chapters/mdp_agent.pdf b/paper/src/chapters/mdp_agent.pdf index 83e10d3e525abf81b3b745ea82e4aff20b7db528..37df0f5cfd8f533a526b80a5be8c54a7c38386d4 100644 GIT binary patch delta 284 zcmV+%0ptF(RkT&GgeZS{in)|J6l|^HI6s&i;y@^+5R%+V4?+=+p;$(eOY`=XoH(WE zv>!W~8J55q6_{}*Kr>=A$8=t>vP5>eb{g4U*4i-u*;{jV_<#V%0iOe^Oy7DCTdxPj z-kNi-v_+z1V+|;!MVceIc;C5ImJPJFvb!|OrN-8i$?Y4frEh;syOmxe*{%c^1<#2S zi6!MPTs(6=8BP9T;VD>0{a#ufbHro=c-QAfZp=@J%fl0kd6AaKYZik&rm zhq8=AoNq1z9nmpRD_hq^H+3EHE4=OXI8|Y5tG^qi54%l-A$8;j?M!c#c9=dqk3p#X iExZeN_*bx!UVaam`ra?x;9f5*{Q~zGZ$-0_DI)<3`-J`g delta 284 zcmV+%0ptF(RkT&GgeZT0in){~6l|^HI6s&iVnZmUV3OQQ4?+=+p;$(eOY`=X>^P<9 zv>!W~8J55z%CKY+2Q4Y06&9;3%5x;=Tc?mzqEVkrXy{P>@Tgdi8g=DYSPCWz%!{0rC9?NxN63zOSFi$tr&8i-V^(gl)>_nlj3*+6Tnx=W*7YHWW!n~mRCQob?mUU@>Y zUrR1?z7SjpS%~=)myev!CX>HdcnH=}e^i!Yj+kr!Z&WpEYkopp9-e&8SCIL#xW{gN z5I8LEii0(Mhq8=AoJW^|PUsw{Rjuoyo4!u?1>W{FO}ntQr|(A7>9CD3q>lVV&h%E3 p!~E`f4npn6@GchI;a|a>^6Jafw|?OUw|Zge7x&h0N3)MDA_20>kHi1~ diff --git a/paper/src/main.tex b/paper/src/main.tex index 2959d04..9bf7f99 100644 --- a/paper/src/main.tex +++ b/paper/src/main.tex @@ -125,7 +125,7 @@ The textbook definition $D_{\mathrm{KL}}(P\parallel Q)=\sum_k P(k)\log(P(k)/Q(k) In code we do the basic fix: add a tiny floor $\varepsilon$ to both the numerator and denominator inside the log so nothing is exactly zero, which turns the sum into a finite, smoothed surrogate rather than a literal KL to raw counts. We also skip source states that do not exist at all in the reference kernel, because there is nowhere honest to compare against. This keeps the pipeline running and the divergence scores on a comparable scale, at the cost that the number is regularized KL behavior, not a purist information-theoretic quantity, which is acceptable here because we only use the gap between human-anchored and agent-anchored scores as a weak separability signal. -\section{Why the logarithm appears in the revelation surrogate} +\section{Expanding the Intuition of Information Value in the Reward} \label{app:revelation_log} Leakage is $\text{COI}_{\text{leak}} = f(\tau')\cdot\text{InfoValue}$. The query-tax form fixes $\text{InfoValue}=c>0$. The revelation form sets $\text{InfoValue}(p,\tau')=-\log\pi(p\mid\tau')$, with $\pi(\cdot\mid\tau')$ the policy distribution over quoted prices in context $\tau'$ (discretized as in the engine).