"""記事51の追試スクリプト(合同会社SuzuLabo・2026-09-10) https://suzulabo.co.jp/ai/834/ 公開している simulate.py の写しに、記事のために書き足した3つの追試を入れたもの。 1. 固定閾値との比較(残量がその%を割ったら通常の仕事を止める方式) 2. 見積もり誤差(Bだけを間違えたとき) 3. 勤務時間(9-19時に仕事が集中する場合)と、Bの積み方の違い profile='uniform'(24時間均一)かつ policy2_workday_aware=False のとき、 simulate.py と同じ値を返す(codex/予定日が動く/判断式=319.6)。 使い方: events_public.json と同じフォルダに置いて python3 verify_workday.py 必要環境: Python 3.9以上(標準ライブラリのみ) """ """Synthetic workload replay. Quota points, not tokens or money. No future reset lookahead by policies.""" from pathlib import Path from datetime import datetime,timedelta from zoneinfo import ZoneInfo import json,statistics P=Path(__file__).resolve().parent _d=P/'events_public.json' if not _d.exists(): _d=Path.cwd()/'events_public.json' if not _d.exists(): raise SystemExit('events_public.json が見つかりません。simulate.py と同じフォルダに置いて実行してください。') E=json.loads(_d.read_text()) Z=ZoneInfo('Asia/Tokyo'); START=datetime(2026,6,15,tzinfo=Z) CAP=100.0; H=28.; DT=1/24 PROFILES={ 'uniform': ([(0,24,1.0)], 1.0), 'workday': ([(9,19,1.0)], 24/10), # 日本の日中9-19時に集中 'workday_tail': ([(9,19,1.0),(20,24,0.35)], 24/(10+4*0.35)), # 日中主体+夜も少し } def wint(t0,t1,profile): """[t0,t1](単位=日)に含まれる仕事量を、時間帯の重みで正確に積分する。 1日あたりの総量はprofileによらず等しくなるようnormで正規化する。""" wins,norm=PROFILES[profile] total=0.0 for d in range(int(t0), int(t1)+1): for a,b,wt in wins: s=max(t0, d+a/24.0); e=min(t1, d+b/24.0) if e>s: total+=(e-s)*wt return total*norm def simulate(events,policy,essential=10,demand=30,phase=7,timer='fixed',tickets=0,profile='uniform',policy2_workday_aware=False,thr=70,est=None): est=essential if est is None else est # Tickets are initially held, expire after day 26/27; no future gifts assumed. ticket_expiries=[26.+i for i in range(tickets)] r=CAP;next_regular=float(phase);e_done=d_done=e_missed=0.;backlog=0.;auto_waste=regular_waste=0.;used=0;expired=0;regulars=0;ticket_waste=0.;resets=0; constrained=0;mode_changes=0;prev=False boundaries=sorted(set([0.,H]+[i*DT for i in range(1,int(H/DT))]+[x for x in events if 0t+1e-9] if abs(t-next_regular)<1e-8: regular_waste+=r;regulars+=1;r=CAP;next_regular=t+7 if t in event_set: auto_waste+=r;r=CAP;resets+=1 if timer=='restart':next_regular=t+7 while k=next_regular for x in ticket_expiries)) allowed=max(0,r-reserve) else:raise ValueError(policy) d=min(r,backlog,allowed);r-=d;backlog-=d;d_done+=d restricted=d+1e-7<(demand-essential)*wdt if restricted:constrained+=dt if restricted!=prev:mode_changes+=1 prev=restricted;t=end assert r>=-1e-7 assert abs(CAP*(1+regulars+resets+used)-(e_done+d_done+r+auto_waste+regular_waste+ticket_waste))<1e-5 assert abs(e_done+e_missed-essential*H)<1e-4 assert abs(d_done+backlog-(demand-essential)*H)<1e-4 return dict(tickets_expired=expired,ticket_unused= ticket_waste,essential_done=e_done,essential_missed=e_missed,optional_done=d_done,total_done=e_done+d_done,optional_backlog=backlog,auto_unused=auto_waste,regular_unused=regular_waste,terminal_balance=r,tickets_used=used,tickets_unspent=len(ticket_expiries),constrained_days=constrained,mode_changes=mode_changes) def resets(provider,start): out=[] for e in E: if e['provider']!=provider or not e['count_main']:continue t=(datetime.fromisoformat(e['announcement_jst'])-start).total_seconds()/86400 if 01e-6 for r in rs)} def _runs(prov,pol,timer,prof='uniform',aware=False,thr=70,est=None,tk=0,ess=10,dem=30): return [simulate(resets(prov,START+timedelta(weeks=w)),pol,ess,dem,ph,timer,tk,prof,aware,thr,est) for w in range(9) for ph in range(1,8)] def _s(rs): return (statistics.mean(x['optional_done'] for x in rs), statistics.mean(x['essential_missed'] for x in rs), sum(x['essential_missed']>1e-6 for x in rs)) def main(): print('== 0. 同一性の確認(公開 simulate.py と同じ値を返すか)==') o,m,n=_s(_runs('codex','reserve','restart')) print(f' codex/動く/判断式 = {o:.1f}(公開値 319.6 と一致するはず) 未処理 {m:.1f}') print() print('== 1. 固定閾値との比較(記事の表)==') for timer,label in [('restart','予定日が動く'),('fixed','予定日が固定')]: o,m,n=_s(_runs('codex','reserve',timer)); print(f' {label}: 判断式 処理{o:6.1f} 未処理{m:5.1f} ({n:2d}/63回)') for t in (60,70,80): o,m,n=_s(_runs('codex','threshold',timer,thr=t)); print(f' {label}: 固定閾値{t}% 処理{o:6.1f} 未処理{m:5.1f} ({n:2d}/63回)') print() print('== 2. 見積もり誤差(Bだけを間違えたとき)==') for timer,label in [('restart','予定日が動く'),('fixed','予定日が固定')]: for e in (8,9,10,12): o,m,n=_s(_runs('codex','reserve',timer,est=e)) print(f' {label}: 実際10を{e:2d}と見積もる 処理{o:6.1f} 未処理{m:5.1f} ({n:2d}/63回)') print() print('== 3. 勤務時間を入れる(9-19時)とBの積み方 ==') for prov in ('codex','claude'): for timer,label in [('restart','動く'),('fixed','固定')]: a=_s(_runs(prov,'reserve',timer,'workday',False)) b=_s(_runs(prov,'reserve',timer,'workday',True)) print(f' {prov}/{label}: B=残り日数 処理{a[0]:6.1f} 未処理{a[1]:5.1f}({a[2]:2d}回) → B=実発生量 処理{b[0]:6.1f} 未処理{b[1]:5.1f}({b[2]:2d}回) 減少{(a[0]-b[0])/a[0]*100:.1f}%') print() print('== 4. 臨時リセットが一度も来ない28日 ==') for prof in ('uniform','workday'): for tk in (0,2): pol='reserve_bank' if tk else 'reserve' r=simulate([],pol,10,30,7,'restart',tk,prof,True) print(f' {prof:8s} 券{tk}枚 通常{r["optional_done"]:6.1f} 未処理{r["essential_missed"]:5.1f}') if __name__=='__main__':main()