Skip to content

Commit f220269

Browse files
feat: Support extensional equality in FinFun (#118)
This PR changes `FinFun` to support extensional equality, making it essentially equivalent to `Finsupp` in Mathlib, with the important difference that definitions and theories about `FinFun` are computable. It also makes FinFun adopt the same notation as Finsupp, so to facilitate a future replacement with the latter in the future if it ever changes in Mathlib to have computable defs/theorems (this is relegated to the future since FinFun is used downstream in at least one big project I know of). --------- Co-authored-by: Chris Henson <[email protected]>
1 parent 7c752ca commit f220269

File tree

1 file changed

+114
-96
lines changed

1 file changed

+114
-96
lines changed

Cslib/Foundations/Data/FinFun.lean

Lines changed: 114 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -6,108 +6,126 @@ Authors: Fabrizio Montesi, Xueying Qin
66

77
import Mathlib.Data.Finset.Basic
88

9-
/-! # Finite Functions
9+
/-! # Finite functions
1010
11-
*Note:* the API and notation of `FinFun` is still unstable.
12-
13-
A `FinFun α β` is a function from `α` to `β` with a finite domain of definition.
11+
Given types `α` and `β`, and assuming that `β` has a `Zero` element,
12+
a `FinFun α β` is a function from `α` to `β` where only a finite number of elements
13+
in `α` are mapped to non-zero elements.
1414
-/
1515

1616
namespace Cslib
1717

18-
/-- A finite function FinFun is a function `f` equipped with a domain of definition `dom`. -/
19-
structure FinFun (α : Type u) (β : Type v) where
20-
f : α → β
21-
dom : Finset α
22-
23-
notation:50 α " ⇀ " β => FinFun α β
24-
notation:50 f "↾" dom => FinFun.mk f dom
25-
26-
abbrev CompleteDom [Zero β] (f : α ⇀ β) := ∀ x, x ∉ f.dom → f.f x = 0
27-
28-
def FinFun.defined (f : α ⇀ β) (x : α) : Prop := x ∈ f.dom
29-
30-
@[simp]
31-
abbrev FinFun.apply (f : α ⇀ β) (x : α) : β := f.f x
32-
33-
/- Conversion from FinFun to a function. -/
34-
@[coe] def FinFun.toFun [DecidableEq α] [Zero β] (f : α ⇀ β) : (α → β) :=
35-
fun x => if x ∈ f.dom then f.f x else Zero.zero
36-
37-
instance [DecidableEq α] [Zero β] : Coe (α ⇀ β) (α → β) where
38-
coe := FinFun.toFun
39-
40-
theorem FinFun.toFun_char [DecidableEq α] [Zero β]
41-
{f g : α ⇀ β} (h : (f : α → β) = (g : α → β)) (x) :
42-
(x ∈ (f.dom ∩ g.dom) →
43-
f.apply x = g.apply x) ∧ (x ∈ (f.dom \ g.dom) →
44-
f.apply x = Zero.zero) ∧ (x ∈ (g.dom \ f.dom) → g.apply x = Zero.zero) := by
45-
have happlyx : f.toFun x = g.toFun x := by simp [h]
46-
grind [FinFun.toFun]
47-
48-
theorem FinFun.toFun_dom [DecidableEq α] [Zero β] {f : α ⇀ β}
49-
(h : ∀ x, x ∉ f.dom → f.apply x = Zero.zero) : (f : α → β) = f.f := by
50-
grind [FinFun.toFun]
51-
52-
def FinFun.mapBin [DecidableEq α] (f g : α ⇀ β) (op : Option β → Option β → Option β) :
53-
Option (α ⇀ β) :=
54-
if f.dom = g.dom ∧ ∀ x ∈ f.dom, (op (some (f.f x)) (some (g.f x))).isSome then
55-
some {
56-
f := fun x =>
57-
match op (some (f.f x)) (some (g.f x)) with
58-
| some y => y
59-
| none => f.f x
60-
dom := f.dom
61-
}
62-
else
63-
none
64-
65-
theorem FinFun.mapBin_dom [DecidableEq α] (f g : α ⇀ β)
66-
(op : Option β → Option β → Option β) (h : FinFun.mapBin f g op = some fg) :
67-
fg.dom = f.dom ∧ fg.dom = g.dom := by grind [mapBin]
68-
69-
theorem FinFun.mapBin_char₁ [DecidableEq α] (f g : α ⇀ β)
70-
(op : Option β → Option β → Option β) (h : FinFun.mapBin f g op = some fg) :
71-
∀ x ∈ fg.dom, fg.apply x = y ↔ (op (some (f.f x)) (some (g.f x))) = some y := by
72-
intro x hxdom
73-
constructor
74-
<;> simp only [mapBin, Option.ite_none_right_eq_some] at h
75-
<;> rcases h with ⟨_, _, _, _⟩
76-
<;> grind
77-
78-
theorem FinFun.mapBin_char₂ [DecidableEq α] (f g : α ⇀ β)
79-
(op : Option β → Option β → Option β) (hdom : f.dom = g.dom)
80-
(hop : ∀ x ∈ f.dom, (op (some (f.f x)) (some (g.f x))).isSome)
81-
: (FinFun.mapBin f g op).isSome := by grind [mapBin]
82-
83-
-- Fun to FinFun
84-
def Function.toFinFun [DecidableEq α] (f : α → β) (dom : Finset α) : α ⇀ β := FinFun.mk f dom
85-
86-
lemma Function.toFinFun_eq [DecidableEq α] [Zero β] (f : α → β) (dom : Finset α)
87-
(h : ∀ x, x ∉ dom → f x = 0) : f = (Function.toFinFun f dom) := by
88-
funext p
89-
by_cases hp : p ∈ dom <;> simp only [toFinFun, FinFun.toFun, hp, reduceIte]
90-
exact h p hp
91-
92-
@[coe] def FinFun.toDomFun (f : α ⇀ β) : {x // x ∈ f.dom} → β :=
93-
fun x => f.f x
94-
95-
theorem FinFun.toDomFun_char (f : α ⇀ β) (h : x ∈ f.dom) : f.toDomFun ⟨ x, h ⟩ = f.f x := by
96-
simp [FinFun.toDomFun]
97-
98-
theorem FinFun.congrFinFun [DecidableEq α] [Zero β] {f g : α ⇀ β} (h : f = g) (a : α) :
99-
f.apply a = g.apply a := congrFun (congrArg apply h) a
100-
101-
theorem FinFun.eq_char₁ [DecidableEq α] [Zero β] {f g : α ⇀ β} (h : f = g) :
102-
f.f = g.f ∧ f.dom = g.dom := ⟨congrArg FinFun.f h, congrArg dom h⟩
103-
104-
theorem FinFun.eq_char₂ [DecidableEq α] [Zero β] {f g : α ⇀ β} (heq : f.f = g.f ∧ f.dom = g.dom) :
105-
f = g := by
106-
cases f
107-
cases g
18+
/-- A `FinFun` is a function `fn` with a finite `support`.
19+
20+
This is similar to `Finsupp` in Mathlib, but definitions are computable. -/
21+
structure FinFun (α : Type _) (β : Type _) [Zero β] where
22+
/-- The underlying function. -/
23+
fn : α → β
24+
/-- The finite support of the function. -/
25+
support : Finset α
26+
/-- Proof that `support` is the support of the underlying function. -/
27+
mem_support_fn {a : α} : a ∈ support ↔ fn a ≠ 0
28+
29+
attribute [scoped grind _=_] FinFun.mem_support_fn
30+
31+
namespace FinFun
32+
33+
@[inherit_doc]
34+
scoped infixr:25 " →₀ " => FinFun
35+
36+
/-- Constructs a `FinFun` by restricting a function to a given support, filtering out all elements
37+
not mapped to 0 in the support. -/
38+
@[scoped grind .]
39+
private def fromFun {α β : Type _} [Zero β] [DecidableEq α]
40+
[∀ y : β, Decidable (y = 0)] (fn : α → β) (support : Finset α) : α →₀ β where
41+
fn := (fun a => if a ∈ support then fn a else 0)
42+
support := support.filter (fn · ≠ 0)
43+
mem_support_fn := by grind
44+
45+
@[inherit_doc]
46+
scoped notation f:25 "↾₀" support:51 => FinFun.fromFun f support
47+
48+
instance instFunLike [Zero β] : FunLike (α →₀ β) α β where
49+
coe f := f.fn
50+
coe_injective' := by
51+
rintro ⟨_, _⟩ ⟨_, _⟩
52+
simp_all [Finset.ext_iff]
53+
54+
@[scoped grind =]
55+
theorem coe_fn [Zero β] {f : α →₀ β} : (f : α → β) = f.fn := by simp [DFunLike.coe]
56+
57+
@[scoped grind =]
58+
theorem coe_eq_fn [Zero β] {f : α →₀ β} : f a = f.fn a := by
59+
simp [DFunLike.coe]
60+
61+
/-- Extensional equality for `FinFun`. -/
62+
@[scoped grind ←=]
63+
theorem ext [Zero β] {f g : α →₀ β} (h : ∀ (a : α), f a = g a) : f = g :=
64+
DFunLike.ext (f := f) (g := g) h
65+
66+
@[scoped grind _=_]
67+
theorem mem_support_not_zero [Zero β] {f : α →₀ β} : a ∈ f.support ↔ f a ≠ 0 := by
68+
grind
69+
70+
@[scoped grind _=_]
71+
theorem not_mem_support_zero [Zero β] {f : α →₀ β} : a ∉ f.support ↔ f a = 0 := by
72+
grind
73+
74+
/-- If two `FinFun`s are equal, their underlying functions and supports are equal. -/
75+
@[scoped grind .]
76+
theorem eq_fields_eq [DecidableEq α] [Zero β] {f g : α →₀ β} :
77+
f = g → (f.fn = g.fn ∧ f.support = g.support) := by grind
78+
79+
/-- If two functions are equal, two `FinFun`s respectively using them as underlying functions
80+
are equal. -/
81+
@[scoped grind .]
82+
theorem fn_eq_eq [DecidableEq α] [Zero β] {f g : α →₀ β} (h : f.fn = g.fn) : f = g :=
83+
ext (congrFun h)
84+
85+
@[scoped grind =>]
86+
theorem congrFinFun [DecidableEq α] [Zero β] {f g : α →₀ β} (h : f = g) (a : α) :
87+
f a = g a := by grind
88+
89+
@[scoped grind =]
90+
theorem fromFun_eq [Zero β] [DecidableEq α] [∀ y : β, Decidable (y = 0)]
91+
(f : α → β) (support : Finset α) (h : ∀ a, a ∉ support → f a = 0) :
92+
(f ↾₀ support) = f := by grind
93+
94+
@[scoped grind =]
95+
theorem fromFun_fn [Zero β] [DecidableEq α] [∀ y : β, Decidable (y = 0)]
96+
(f : α → β) (support : Finset α) :
97+
(f ↾₀ support).fn = (fun a => if a ∈ support then f a else 0) := by
98+
grind
99+
100+
@[scoped grind =]
101+
theorem fromFun_support [Zero β] [DecidableEq α] [∀ y : β, Decidable (y = 0)]
102+
(f : α → β) (support : Finset α) :
103+
(f ↾₀ support).support = support.filter (f · ≠ 0) := by
108104
grind
109105

110-
theorem FinFun.eq_char [DecidableEq α] [Zero β] {f g : α ⇀ β} :
111-
f = g ↔ f.f = g.f ∧ f.dom = g.dom := by grind [FinFun.eq_char₁, FinFun.eq_char₂]
106+
/-- Restricting a function twice to the same support is idempotent. -/
107+
@[scoped grind =]
108+
theorem fromFun_idem [Zero β] [DecidableEq α]
109+
[∀ y : β, Decidable (y = 0)] {f : α → β} {support : Finset α} :
110+
(f ↾₀ support) ↾₀ support = f ↾₀ support := by grind
111+
112+
/-- Restricting a `FinFun` to its own support is the identity. -/
113+
@[scoped grind =]
114+
theorem coe_fromFun_id [Zero β] [DecidableEq α] [∀ y : β, Decidable (y = 0)] {f : α →₀ β} :
115+
(f ↾₀ f.support) = f := by grind
116+
117+
/-- Restricting a function twice to two supports is equal to restricting to their intersection. -/
118+
@[scoped grind =]
119+
theorem fromFun_inter [Zero β] [DecidableEq α]
120+
[∀ y : β, Decidable (y = 0)] {f : α → β} {support1 support2 : Finset α} :
121+
(f ↾₀ support1) ↾₀ support2 = f ↾₀ (support1 ∩ support2) := by grind
122+
123+
/-- Restricting a function is commutative. -/
124+
@[scoped grind =]
125+
theorem fromFun_comm [Zero β] [DecidableEq α]
126+
[∀ y : β, Decidable (y = 0)] {f : α → β} {support1 support2 : Finset α} :
127+
(f ↾₀ support1) ↾₀ support2 = (f ↾₀ support2) ↾₀ support1 := by grind
128+
129+
end FinFun
112130

113131
end Cslib

0 commit comments

Comments
 (0)