import {
  ValidationError
} from "./chunk-3BFEG2F6.js";

// src/rl/off-policy.ts
function inverseProbabilityWeighting(trajectories, opts = {}) {
  const cap = opts.weightCap ?? Infinity;
  const clip = opts.rewardClip ?? { low: 0, high: 1 };
  if (trajectories.length === 0) {
    return zeroEstimate();
  }
  const weights = [];
  const weightedRewards = [];
  let maxW = 0;
  for (const t of trajectories) {
    if (t.behaviorProb <= 0) {
      throw new ValidationError(
        `inverseProbabilityWeighting: behaviorProb must be > 0 (runId=${t.runId})`
      );
    }
    const w = Math.min(cap, t.targetProb / t.behaviorProb);
    const r = clamp(t.reward, clip.low, clip.high);
    weights.push(w);
    weightedRewards.push(w * r);
    if (w > maxW) maxW = w;
  }
  const n = weights.length;
  const value = weightedRewards.reduce((s, x) => s + x, 0) / n;
  const variance = weightedRewards.reduce((s, x) => s + (x - value) ** 2, 0) / Math.max(1, n - 1);
  const sumW = weights.reduce((s, w) => s + w, 0);
  const sumW2 = weights.reduce((s, w) => s + w * w, 0);
  const effN = sumW === 0 ? 0 : sumW * sumW / sumW2;
  return {
    value,
    standardError: Math.sqrt(variance / n),
    effectiveSampleSize: effN,
    n,
    maxImportanceWeight: maxW
  };
}
function selfNormalizedImportanceWeighting(trajectories, opts = {}) {
  const cap = opts.weightCap ?? Infinity;
  const clip = opts.rewardClip ?? { low: 0, high: 1 };
  if (trajectories.length === 0) return zeroEstimate();
  const weights = [];
  const rewards = [];
  let maxW = 0;
  for (const t of trajectories) {
    if (t.behaviorProb <= 0) {
      throw new ValidationError(
        `selfNormalizedImportanceWeighting: behaviorProb must be > 0 (runId=${t.runId})`
      );
    }
    const w = Math.min(cap, t.targetProb / t.behaviorProb);
    weights.push(w);
    rewards.push(clamp(t.reward, clip.low, clip.high));
    if (w > maxW) maxW = w;
  }
  const sumW = weights.reduce((s, w) => s + w, 0);
  const sumWR = weights.reduce((s, w, i) => s + w * rewards[i], 0);
  const value = sumW === 0 ? 0 : sumWR / sumW;
  const sumW2 = weights.reduce((s, w) => s + w * w, 0);
  const effN = sumW === 0 ? 0 : sumW * sumW / sumW2;
  const phi = weights.map((w, i) => w * (rewards[i] - value));
  const variance = phi.reduce((s, x) => s + x * x, 0) / Math.max(1, sumW * sumW);
  return {
    value,
    standardError: Math.sqrt(variance),
    effectiveSampleSize: effN,
    n: trajectories.length,
    maxImportanceWeight: maxW
  };
}
function doublyRobust(trajectories, opts = {}) {
  const cap = opts.weightCap ?? Infinity;
  const clip = opts.rewardClip ?? { low: 0, high: 1 };
  if (trajectories.length === 0) return zeroEstimate();
  const contributions = [];
  let maxW = 0;
  let sumW = 0;
  let sumW2 = 0;
  for (const t of trajectories) {
    if (t.behaviorProb <= 0) {
      throw new ValidationError(`doublyRobust: behaviorProb must be > 0 (runId=${t.runId})`);
    }
    const w = Math.min(cap, t.targetProb / t.behaviorProb);
    const r = clamp(t.reward, clip.low, clip.high);
    const q = typeof t.qHat === "number" && Number.isFinite(t.qHat) ? clamp(t.qHat, clip.low, clip.high) : null;
    if (q === null) {
      contributions.push(w * r);
    } else {
      contributions.push(q + w * (r - q));
    }
    if (w > maxW) maxW = w;
    sumW += w;
    sumW2 += w * w;
  }
  const n = contributions.length;
  const value = contributions.reduce((s, x) => s + x, 0) / n;
  const variance = contributions.reduce((s, x) => s + (x - value) ** 2, 0) / Math.max(1, n - 1);
  const effN = sumW === 0 ? 0 : sumW * sumW / sumW2;
  return {
    value,
    standardError: Math.sqrt(variance / n),
    effectiveSampleSize: effN,
    n,
    maxImportanceWeight: maxW
  };
}
function offPolicyEstimateAll(trajectories, opts = {}) {
  return {
    ips: inverseProbabilityWeighting(trajectories, opts),
    snips: selfNormalizedImportanceWeighting(trajectories, opts),
    dr: doublyRobust(trajectories, opts)
  };
}
function zeroEstimate() {
  return { value: 0, standardError: 0, effectiveSampleSize: 0, n: 0, maxImportanceWeight: 0 };
}
function clamp(x, lo, hi) {
  if (!Number.isFinite(x)) return lo;
  return Math.max(lo, Math.min(hi, x));
}

export {
  inverseProbabilityWeighting,
  selfNormalizedImportanceWeighting,
  doublyRobust,
  offPolicyEstimateAll
};
//# sourceMappingURL=chunk-4DIJWVUT.js.map