|
| 1 | +/- |
| 2 | +Copyright (c) 2026 Lean FRO, LLC. All rights reserved. |
| 3 | +Released under Apache 2.0 license as described in the file LICENSE. |
| 4 | +Authors: Kim Morrison, Sebastian Graf |
| 5 | +-/ |
| 6 | +module |
| 7 | + |
| 8 | +public import Mathlib.Data.Nat.Log |
| 9 | +public import Mathlib.Data.Fintype.Card |
| 10 | + |
| 11 | +/-! # QueryTree: Decision Trees for Query Complexity Lower Bounds |
| 12 | +
|
| 13 | +`QueryTree Q R α` is a free monad specialized to a single query type: queries take |
| 14 | +input `Q` and return `R`, with final results of type `α`. It reifies an algorithm's |
| 15 | +query pattern as an explicit decision tree. |
| 16 | +
|
| 17 | +The key advantage over `Prog`/`FreeM` for lower bound proofs is that `R` is a fixed type |
| 18 | +parameter (not existentially quantified per query), making structural induction with |
| 19 | +pigeonhole arguments straightforward. |
| 20 | +
|
| 21 | +## Main Definitions |
| 22 | +
|
| 23 | +- `QueryTree Q R α` — the decision tree type |
| 24 | +- `QueryTree.ask` — the canonical single-query tree |
| 25 | +- `QueryTree.eval` — evaluate with a specific oracle |
| 26 | +- `QueryTree.queriesOn` — count queries along an oracle-determined path |
| 27 | +-/ |
| 28 | + |
| 29 | +public section |
| 30 | + |
| 31 | +namespace Cslib.Query |
| 32 | + |
| 33 | +/-- A decision tree over queries of type `Q → R`, with results of type `α`. |
| 34 | +
|
| 35 | +This is the free monad specialized to a single fixed-type operation, used to reify |
| 36 | +algorithms as explicit trees for query complexity lower bounds. -/ |
| 37 | +inductive QueryTree (Q : Type) (R : Type) (α : Type) where |
| 38 | + /-- A completed computation returning value `a`. -/ |
| 39 | + | pure (a : α) : QueryTree Q R α |
| 40 | + /-- A query node: asks query `q`, then continues based on the response. -/ |
| 41 | + | query (q : Q) (cont : R → QueryTree Q R α) : QueryTree Q R α |
| 42 | + |
| 43 | +namespace QueryTree |
| 44 | + |
| 45 | +variable {Q R α β γ : Type} |
| 46 | + |
| 47 | +/-- Lift a single query into the tree. -/ |
| 48 | +@[expose] def ask (q : Q) : QueryTree Q R R := .query q .pure |
| 49 | + |
| 50 | +/-- Monadic bind for query trees. -/ |
| 51 | +@[expose] protected def bind : QueryTree Q R α → (α → QueryTree Q R β) → QueryTree Q R β |
| 52 | + | .pure a, f => f a |
| 53 | + | .query q cont, f => .query q (fun r => (cont r).bind f) |
| 54 | + |
| 55 | +/-- Functorial map for query trees. -/ |
| 56 | +@[expose] protected def map (f : α → β) : QueryTree Q R α → QueryTree Q R β |
| 57 | + | .pure a => .pure (f a) |
| 58 | + | .query q cont => .query q (fun r => (cont r).map f) |
| 59 | + |
| 60 | +protected theorem bind_pure : ∀ (x : QueryTree Q R α), x.bind .pure = x |
| 61 | + | .pure _ => rfl |
| 62 | + | .query _ cont => by simp [QueryTree.bind, QueryTree.bind_pure] |
| 63 | + |
| 64 | +protected theorem bind_assoc : |
| 65 | + ∀ (x : QueryTree Q R α) (f : α → QueryTree Q R β) (g : β → QueryTree Q R γ), |
| 66 | + (x.bind f).bind g = x.bind (fun a => (f a).bind g) |
| 67 | + | .pure _, _, _ => rfl |
| 68 | + | .query _ cont, f, g => by simp [QueryTree.bind, QueryTree.bind_assoc] |
| 69 | + |
| 70 | +protected theorem bind_pure_comp (f : α → β) : |
| 71 | + ∀ (x : QueryTree Q R α), x.bind (.pure ∘ f) = x.map f |
| 72 | + | .pure _ => rfl |
| 73 | + | .query _ cont => by simp [QueryTree.bind, QueryTree.map, QueryTree.bind_pure_comp] |
| 74 | + |
| 75 | +protected theorem id_map : ∀ (x : QueryTree Q R α), x.map id = x |
| 76 | + | .pure _ => rfl |
| 77 | + | .query _ cont => by simp [QueryTree.map, QueryTree.id_map] |
| 78 | + |
| 79 | +instance : Monad (QueryTree Q R) where |
| 80 | + pure := .pure |
| 81 | + bind := .bind |
| 82 | + |
| 83 | +instance : LawfulMonad (QueryTree Q R) := LawfulMonad.mk' |
| 84 | + (bind_pure_comp := fun _ _ => rfl) |
| 85 | + (id_map := QueryTree.bind_pure) |
| 86 | + (pure_bind := fun _ _ => rfl) |
| 87 | + (bind_assoc := QueryTree.bind_assoc) |
| 88 | + |
| 89 | +-- Core operations |
| 90 | + |
| 91 | +/-- Evaluate a query tree with a specific oracle, returning the final result. -/ |
| 92 | +@[expose] def eval (oracle : Q → R) : QueryTree Q R α → α |
| 93 | + | .pure a => a |
| 94 | + | .query q cont => eval oracle (cont (oracle q)) |
| 95 | + |
| 96 | +/-- Count the number of queries along the path determined by `oracle`. -/ |
| 97 | +@[expose] def queriesOn (oracle : Q → R) : QueryTree Q R α → Nat |
| 98 | + | .pure _ => 0 |
| 99 | + | .query q cont => 1 + queriesOn oracle (cont (oracle q)) |
| 100 | + |
| 101 | +-- Simp lemmas |
| 102 | + |
| 103 | +@[simp] theorem eval_pure' (oracle : Q → R) (a : α) : |
| 104 | + (QueryTree.pure a : QueryTree Q R α).eval oracle = a := rfl |
| 105 | + |
| 106 | +@[simp] theorem eval_query (oracle : Q → R) (q : Q) (cont : R → QueryTree Q R α) : |
| 107 | + (QueryTree.query q cont).eval oracle = (cont (oracle q)).eval oracle := rfl |
| 108 | + |
| 109 | +@[simp] theorem eval_bind (oracle : Q → R) (t : QueryTree Q R α) (f : α → QueryTree Q R β) : |
| 110 | + (t.bind f).eval oracle = (f (t.eval oracle)).eval oracle := by |
| 111 | + induction t with |
| 112 | + | pure a => rfl |
| 113 | + | query q cont ih => exact ih (oracle q) |
| 114 | + |
| 115 | +@[simp] theorem queriesOn_pure' (oracle : Q → R) (a : α) : |
| 116 | + (QueryTree.pure a : QueryTree Q R α).queriesOn oracle = 0 := rfl |
| 117 | + |
| 118 | +@[simp] theorem queriesOn_query (oracle : Q → R) (q : Q) (cont : R → QueryTree Q R α) : |
| 119 | + (QueryTree.query q cont).queriesOn oracle = 1 + (cont (oracle q)).queriesOn oracle := rfl |
| 120 | + |
| 121 | +/-- Queries of `t.bind f` = queries of `t` + queries of the continuation. -/ |
| 122 | +@[simp] theorem queriesOn_bind (oracle : Q → R) (t : QueryTree Q R α) (f : α → QueryTree Q R β) : |
| 123 | + (t.bind f).queriesOn oracle = |
| 124 | + t.queriesOn oracle + (f (t.eval oracle)).queriesOn oracle := by |
| 125 | + induction t with |
| 126 | + | pure a => simp [QueryTree.bind, queriesOn, eval] |
| 127 | + | query q cont ih => simp only [QueryTree.bind, queriesOn_query, eval_query, ih (oracle q)]; omega |
| 128 | + |
| 129 | +@[simp] theorem queriesOn_ask (oracle : Q → R) (q : Q) : |
| 130 | + (ask q : QueryTree Q R R).queriesOn oracle = 1 := rfl |
| 131 | + |
| 132 | +@[simp] theorem eval_ask (oracle : Q → R) (q : Q) : |
| 133 | + (ask q : QueryTree Q R R).eval oracle = oracle q := rfl |
| 134 | + |
| 135 | +end QueryTree |
| 136 | + |
| 137 | +end Cslib.Query |
| 138 | + |
| 139 | +end -- public section |
0 commit comments