Complete POLYCODE-WP-0002 simulator v0.1 scaffold
This commit is contained in:
parent
6499e38504
commit
a8e4f03172
9 changed files with 268 additions and 5 deletions
132
simulator.py
Normal file
132
simulator.py
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
#!/usr/bin/env python3
|
||||
"""PolyCode Simulator v0.1 — single-run demo."""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import random
|
||||
from dataclasses import asdict, dataclass
|
||||
from datetime import UTC, datetime
|
||||
from pathlib import Path
|
||||
|
||||
from strategies import (
|
||||
LenientVoter,
|
||||
Proposal,
|
||||
RandomInvestor,
|
||||
ThresholdInvestor,
|
||||
ValueVoter,
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class RoundResult:
|
||||
round: int
|
||||
proposal_id: int
|
||||
funded: float
|
||||
approved: bool
|
||||
effort: float
|
||||
value: float
|
||||
|
||||
|
||||
@dataclass
|
||||
class RunSummary:
|
||||
accepted: int
|
||||
rejected: int
|
||||
total_funded: float
|
||||
acceptance_rate: float
|
||||
rounds: list[RoundResult]
|
||||
|
||||
|
||||
def load_params(path: Path | None = None) -> dict:
|
||||
params_path = path or Path(__file__).with_name("params_default.json")
|
||||
return json.loads(params_path.read_text(encoding="utf-8"))
|
||||
|
||||
|
||||
def make_proposal(round_idx: int, rng: random.Random) -> Proposal:
|
||||
effort = rng.uniform(30, 120)
|
||||
value = effort * rng.uniform(0.7, 1.4)
|
||||
return Proposal(id=round_idx, effort=round(effort, 2), value=round(value, 2))
|
||||
|
||||
|
||||
def run_simulation(params: dict | None = None) -> RunSummary:
|
||||
params = params or load_params()
|
||||
rng = random.Random(params.get("random_seed", 42))
|
||||
investors = [ThresholdInvestor(), RandomInvestor(), RandomInvestor()]
|
||||
voters = [ValueVoter(), ValueVoter(), LenientVoter(), LenientVoter(), ValueVoter()]
|
||||
|
||||
capital = float(params["initial_capital"])
|
||||
threshold = float(params["funding_threshold"])
|
||||
rounds: list[RoundResult] = []
|
||||
accepted = rejected = 0
|
||||
total_funded = 0.0
|
||||
|
||||
for i in range(int(params["rounds"])):
|
||||
proposal = make_proposal(i + 1, rng)
|
||||
funded = 0.0
|
||||
for investor in investors:
|
||||
stake = min(capital, capital / len(investors))
|
||||
amount = investor.allocate(proposal, funded, stake, rng)
|
||||
amount = min(amount, capital)
|
||||
funded += amount
|
||||
capital -= amount
|
||||
if funded >= threshold:
|
||||
break
|
||||
|
||||
votes = sum(1 for voter in voters if voter.approve(proposal, funded, rng))
|
||||
approved = votes > len(voters) / 2 and funded >= proposal.effort
|
||||
if approved:
|
||||
accepted += 1
|
||||
else:
|
||||
rejected += 1
|
||||
capital += funded * 0.5
|
||||
total_funded += funded
|
||||
rounds.append(
|
||||
RoundResult(
|
||||
round=i + 1,
|
||||
proposal_id=proposal.id,
|
||||
funded=round(funded, 2),
|
||||
approved=approved,
|
||||
effort=proposal.effort,
|
||||
value=proposal.value,
|
||||
)
|
||||
)
|
||||
|
||||
total = accepted + rejected
|
||||
return RunSummary(
|
||||
accepted=accepted,
|
||||
rejected=rejected,
|
||||
total_funded=round(total_funded, 2),
|
||||
acceptance_rate=round(accepted / total, 3) if total else 0.0,
|
||||
rounds=rounds,
|
||||
)
|
||||
|
||||
|
||||
def write_run(summary: RunSummary, params: dict, out_dir: Path | None = None) -> Path:
|
||||
out_dir = out_dir or Path("runs")
|
||||
out_dir.mkdir(exist_ok=True)
|
||||
stamp = datetime.now(UTC).strftime("%Y%m%dT%H%M%SZ")
|
||||
path = out_dir / f"run-{stamp}.json"
|
||||
payload = {
|
||||
"params": params,
|
||||
"summary": {
|
||||
"accepted": summary.accepted,
|
||||
"rejected": summary.rejected,
|
||||
"total_funded": summary.total_funded,
|
||||
"acceptance_rate": summary.acceptance_rate,
|
||||
},
|
||||
"rounds": [asdict(r) for r in summary.rounds],
|
||||
}
|
||||
path.write_text(json.dumps(payload, indent=2), encoding="utf-8")
|
||||
return path
|
||||
|
||||
|
||||
def main() -> None:
|
||||
params = load_params()
|
||||
summary = run_simulation(params)
|
||||
out = write_run(summary, params)
|
||||
print(f"PolyCode demo run complete: {summary.accepted} accepted, {summary.rejected} rejected")
|
||||
print(f"acceptance_rate={summary.acceptance_rate} total_funded={summary.total_funded}")
|
||||
print(f"output: {out}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Loading…
Add table
Add a link
Reference in a new issue