Skip to content

Commit 21d740a

Browse files
authored
Refactor: use several Bandit models (#52)
2 parents 1f78651 + a260892 commit 21d740a

29 files changed

Lines changed: 4575 additions & 1563 deletions

‎LeanBandits.lean‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,24 @@
11
import LeanBandits.Bandit.Bandit
22
import LeanBandits.Bandit.Regret
3+
import LeanBandits.Bandit.RewardByCountMeasure
4+
import LeanBandits.Bandit.SumRewards
5+
import LeanBandits.BanditAlgorithms.AuxSums
36
import LeanBandits.BanditAlgorithms.ETC
47
import LeanBandits.BanditAlgorithms.UCB
58
import LeanBandits.ForMathlib.CondDistrib
9+
import LeanBandits.ForMathlib.CondIndepFun
10+
import LeanBandits.ForMathlib.HasCondDistrib
611
import LeanBandits.ForMathlib.IndepFun
712
import LeanBandits.ForMathlib.IndepInfinitePi
13+
import LeanBandits.ForMathlib.KernelRepresentation
814
import LeanBandits.ForMathlib.KernelSub
915
import LeanBandits.ForMathlib.Measurable
1016
import LeanBandits.ForMathlib.MeasurableArgMax
17+
import LeanBandits.ForMathlib.StandardBorel
1118
import LeanBandits.ForMathlib.SubGaussian
1219
import LeanBandits.ForMathlib.Traj
13-
import LeanBandits.RewardByCountMeasure
1420
import LeanBandits.SequentialLearning.Algorithm
1521
import LeanBandits.SequentialLearning.Deterministic
1622
import LeanBandits.SequentialLearning.FiniteActions
23+
import LeanBandits.SequentialLearning.IonescuTulceaSpace
1724
import LeanBandits.SequentialLearning.StationaryEnv

‎LeanBandits/Bandit/Bandit.lean‎

Lines changed: 1079 additions & 85 deletions
Large diffs are not rendered by default.

‎LeanBandits/Bandit/Regret.lean‎

Lines changed: 69 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,10 @@ Copyright (c) 2025 Rémy Degenne. All rights reserved.
33
Released under Apache 2.0 license as described in the file LICENSE.
44
Authors: Rémy Degenne, Paulo Rauber
55
-/
6-
import LeanBandits.Bandit.Bandit
76
import LeanBandits.SequentialLearning.FiniteActions
87

98
/-!
10-
# Regret
9+
# Regret, gap, best arm
1110
1211
-/
1312

@@ -17,17 +16,12 @@ open scoped ENNReal NNReal
1716

1817
namespace Bandits
1918

20-
variable {α : Type*} [DecidableEq α] {mα : MeasurableSpace α} {ν : Kernel α ℝ}
21-
{h : ℕ → α × ℝ} {m n t : ℕ} {a : α}
19+
variable {α Ω : Type*} [DecidableEq α] {mα : MeasurableSpace α} {mΩ : MeasurableSpace Ω}
20+
{ν : Kernel α ℝ}
21+
{A : ℕ → Ω → α} {R : ℕ → Ω → ℝ}
22+
{ω : Ω} {m n t : ℕ} {a : α}
2223

23-
/-! ### Definitions of regret, gaps, pull counts -/
24-
25-
/-- Regret of a sequence of pulls `k : ℕ → α` at time `t` for the reward kernel `ν ; Kernel α ℝ`. -/
26-
noncomputable
27-
def regret (ν : Kernel α ℝ) (t : ℕ) (h : ℕ → α × ℝ) : ℝ :=
28-
t * (⨆ a, (ν a)[id]) - ∑ s ∈ range t, (ν (arm s h))[id]
29-
30-
/-- Gap of an arm `a`: difference between the highest mean of the arms and the mean of `a`. -/
24+
/-- Gap of an action `a`: difference between the highest mean of the actions and the mean of `a`. -/
3125
noncomputable
3226
def gap (ν : Kernel α ℝ) (a : α) : ℝ := (⨆ i, (ν i)[id]) - (ν a)[id]
3327

@@ -36,28 +30,35 @@ lemma gap_nonneg [Fintype α] : 0 ≤ gap ν a := by
3630
rw [gap, sub_nonneg]
3731
exact le_ciSup (f := fun i ↦ (ν i)[id]) (by simp) a
3832

39-
lemma arm_stepsUntil (hm : m ≠ 0) (h_exists : ∃ s, pullCount a (s + 1) h = m) :
40-
arm (stepsUntil a m h).toNat h = a := by
41-
exact action_stepsUntil hm h_exists
33+
/-- Regret of a sequence of pulls `k : ℕ → α` at time `t` for the reward kernel `ν ; Kernel α ℝ`. -/
34+
noncomputable
35+
def regret (ν : Kernel α ℝ) (A : ℕ → Ω → α) (t : ℕ) (ω : Ω) : ℝ :=
36+
t * (⨆ a, (ν a)[id]) - ∑ s ∈ range t, (ν (A s ω))[id]
37+
38+
omit [DecidableEq α] in
39+
lemma regret_eq_sum_gap : regret ν A t ω = ∑ s ∈ range t, gap ν (A s ω) := by
40+
simp [regret, gap]
4241

43-
lemma arm_eq_of_stepsUntil_eq_coe {ω : ℕ → α × ℝ} (hm : m ≠ 0)
44-
(h : stepsUntil a m ω = n) :
45-
arm n ω = a := by
46-
exact action_eq_of_stepsUntil_eq_coe hm h
42+
omit [DecidableEq α] in
43+
lemma regret_nonneg [Fintype α] : 0 ≤ regret ν A t ω := by
44+
rw [regret_eq_sum_gap]
45+
exact sum_nonneg (fun _ _ ↦ gap_nonneg)
4746

48-
section RewardByCount
47+
omit [DecidableEq α] in
48+
lemma gap_eq_zero_of_regret_eq_zero [Fintype α] (hr : regret ν A t ω = 0) {s : ℕ} (hs : s < t) :
49+
gap ν (A s ω) = 0 := by
50+
rw [regret_eq_sum_gap] at hr
51+
exact (sum_eq_zero_iff_of_nonneg fun _ _ ↦ gap_nonneg).1 hr s (mem_range.2 hs)
4952

5053
lemma regret_eq_sum_pullCount_mul_gap [Fintype α] :
51-
regret ν t h = ∑ a, pullCount a t h * gap ν a := by
52-
simp [sum_pullCount_mul, regret, gap, sum_sub_distrib, arm, action]
54+
regret ν A t ω = ∑ a, pullCount A a t ω * gap ν a := by
55+
simp_rw [regret_eq_sum_gap, sum_pullCount_mul]
5356

54-
end RewardByCount
55-
56-
section BestArm
57+
section bestArm
5758

5859
variable [Fintype α] [Nonempty α]
5960

60-
/-- Arm with the highest mean. -/
61+
/-- action with the highest mean. -/
6162
noncomputable def bestArm (ν : Kernel α ℝ) : α :=
6263
(exists_max_image univ (fun a ↦ (ν a)[id]) (univ_nonempty_iff.mpr inferInstance)).choose
6364

@@ -78,6 +79,47 @@ omit [DecidableEq α] in
7879
lemma gap_bestArm : gap ν (bestArm ν) = 0 := by
7980
rw [gap_eq_bestArm_sub, sub_self]
8081

81-
end BestArm
82+
omit [DecidableEq α] in
83+
lemma integral_eq_of_gap_eq_zero (hg : gap ν a = 0) : (ν (bestArm ν))[id] = (ν a)[id] := by
84+
rwa [← sub_eq_zero, ← gap_eq_bestArm_sub]
85+
86+
end bestArm
87+
88+
section Asymptotics
89+
90+
omit [DecidableEq α] in
91+
/-- If the regret is sublinear, the average mean reward tends to the highest mean of the arms. -/
92+
lemma avg_mean_reward_tendsto_of_sublinear_regret
93+
(hr : (regret ν A · ω) =o[atTop] fun t ↦ (t : ℝ)) :
94+
Tendsto (fun t ↦ (∑ s ∈ range t, (ν (A s ω))[id]) / (t : ℝ))
95+
atTop (nhds (⨆ a, (ν a)[id])) := by
96+
have ht : Tendsto (fun t ↦ (⨆ a, (ν a)[id]) - regret ν A t ω / t)
97+
atTop (nhds (⨆ a, (ν a)[id])) := by
98+
simpa using tendsto_const_nhds.sub hr.tendsto_div_nhds_zero
99+
apply ht.congr'
100+
filter_upwards [eventually_ne_atTop 0] with t ht
101+
rw [regret]
102+
field_simp
103+
ring
104+
105+
/-- If the regret is sublinear, the rate of suboptimal arm pulls tends to zero. -/
106+
lemma pullCount_rate_tendsto_of_sublinear_regret [Fintype α]
107+
(hr : (regret ν A · ω) =o[atTop] fun t ↦ (t : ℝ)) (hg : 0 < gap ν a) :
108+
Tendsto (fun t ↦ (pullCount A a t ω : ℝ) / t) atTop (nhds 0) := by
109+
have hb (t : ℕ) : (pullCount A a t ω : ℝ) * gap ν a ≤ regret ν A t ω := by
110+
rw [regret_eq_sum_pullCount_mul_gap]
111+
exact single_le_sum (f := fun a ↦ pullCount A a t ω * gap ν a)
112+
(fun _ _ ↦ mul_nonneg (Nat.cast_nonneg _) gap_nonneg) (mem_univ a)
113+
have hb' (t : ℕ) : (pullCount A a t ω : ℝ) / t ≤ regret ν A t ω / t / gap ν a := by
114+
obtain ht | ht := eq_or_ne t 0
115+
· simp [ht]
116+
· calc (pullCount A a t ω : ℝ) / t
117+
= pullCount A a t ω * gap ν a / gap ν a / t := by field_simp
118+
_ ≤ regret ν A t ω / gap ν a / t := by gcongr; exact hb t
119+
_ = regret ν A t ω / t / gap ν a := by ring
120+
apply squeeze_zero' (Eventually.of_forall fun _ ↦ by positivity) (Eventually.of_forall hb')
121+
simpa using hr.tendsto_div_nhds_zero.div_const (gap ν a)
122+
123+
end Asymptotics
82124

83125
end Bandits

0 commit comments

Comments
 (0)