forked from EHV/sejeteralo
modification calcul mediane
This commit is contained in:
@@ -5,12 +5,22 @@ Computes the element-wise median of (vinf, a, b, c, d, e) across all active vote
|
||||
This parametric median is chosen over geometric median because:
|
||||
- It's transparent and politically explainable
|
||||
- The result is itself a valid set of Bézier parameters
|
||||
|
||||
Post-processing: the median is sanitized to guarantee monotonicity of tier 2.
|
||||
The condition for tier 2 to be non-decreasing is e <= 1/3.
|
||||
If the raw median violates this, e is clamped to E_MAX_MONOTONE.
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
# Maximum value of e that guarantees tier-2 price monotonicity.
|
||||
# prix_m3_2 = p0 + (pmax-p0) * ((1-3e)t³ + 3e t²)
|
||||
# The cubic is monotone non-decreasing on [0,1] iff (1-3e) >= 0, i.e. e <= 1/3.
|
||||
E_MAX_MONOTONE = 1.0 / 3.0
|
||||
|
||||
|
||||
@dataclass
|
||||
class VoteParams:
|
||||
"""The 6 citizen-adjustable parameters."""
|
||||
@@ -22,23 +32,49 @@ class VoteParams:
|
||||
e: float
|
||||
|
||||
|
||||
def _is_tier2_monotone(e: float) -> bool:
|
||||
"""Check if tier-2 price curve is monotone non-decreasing."""
|
||||
return e <= E_MAX_MONOTONE
|
||||
|
||||
|
||||
def sanitize_median(params: "VoteParams") -> "VoteParams":
|
||||
"""
|
||||
Ensure the median curve is physically valid:
|
||||
- Tier 2 must be monotone non-decreasing (penalizes high consumption)
|
||||
- If e violates monotonicity, clamp it to E_MAX_MONOTONE
|
||||
"""
|
||||
e = params.e
|
||||
if not _is_tier2_monotone(e):
|
||||
e = E_MAX_MONOTONE
|
||||
|
||||
return VoteParams(
|
||||
vinf=params.vinf,
|
||||
a=params.a,
|
||||
b=params.b,
|
||||
c=params.c,
|
||||
d=params.d,
|
||||
e=e,
|
||||
)
|
||||
|
||||
|
||||
def compute_median(votes: list[VoteParams]) -> VoteParams | None:
|
||||
"""
|
||||
Compute element-wise median of vote parameters.
|
||||
|
||||
Returns None if no votes provided.
|
||||
The result is sanitized for tier-2 monotonicity.
|
||||
"""
|
||||
if not votes:
|
||||
return None
|
||||
|
||||
vinfs = [v.vinf for v in votes]
|
||||
a_s = [v.a for v in votes]
|
||||
b_s = [v.b for v in votes]
|
||||
c_s = [v.c for v in votes]
|
||||
d_s = [v.d for v in votes]
|
||||
e_s = [v.e for v in votes]
|
||||
a_s = [v.a for v in votes]
|
||||
b_s = [v.b for v in votes]
|
||||
c_s = [v.c for v in votes]
|
||||
d_s = [v.d for v in votes]
|
||||
e_s = [v.e for v in votes]
|
||||
|
||||
return VoteParams(
|
||||
raw = VoteParams(
|
||||
vinf=float(np.median(vinfs)),
|
||||
a=float(np.median(a_s)),
|
||||
b=float(np.median(b_s)),
|
||||
@@ -46,3 +82,5 @@ def compute_median(votes: list[VoteParams]) -> VoteParams | None:
|
||||
d=float(np.median(d_s)),
|
||||
e=float(np.median(e_s)),
|
||||
)
|
||||
|
||||
return sanitize_median(raw)
|
||||
Reference in New Issue
Block a user