Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions Linglib/Data/UD/Basic.lean
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,7 @@ def MorphFeatures.compatible (f1 f2 : MorphFeatures) : Bool :=
f.compatible f = true := by
simp only [MorphFeatures.compatible, beq_self_eq_true, Bool.or_true, Bool.and_true]

private theorem MorphFeatures.compat_clause_comm {α : Type _} [BEq α] [LawfulBEq α]
private theorem MorphFeatures.compatible_clause_comm {α : Type _} [BEq α] [LawfulBEq α]
(a b : Option α) :
(a.isNone || b.isNone || a == b) = (b.isNone || a.isNone || b == a) := by
cases a <;> cases b <;> simp [beq_iff_eq, eq_comm]
Expand All @@ -387,13 +387,13 @@ private theorem MorphFeatures.compat_clause_comm {α : Type _} [BEq α] [LawfulB
theorem MorphFeatures.compatible_comm (f1 f2 : MorphFeatures) :
f1.compatible f2 = f2.compatible f1 := by
unfold MorphFeatures.compatible
rw [compat_clause_comm f1.number, compat_clause_comm f1.gender,
compat_clause_comm f1.case_, compat_clause_comm f1.definite,
compat_clause_comm f1.degree, compat_clause_comm f1.pronType,
compat_clause_comm f1.person, compat_clause_comm f1.verbForm,
compat_clause_comm f1.tense, compat_clause_comm f1.aspect,
compat_clause_comm f1.mood, compat_clause_comm f1.voice,
compat_clause_comm f1.polarity]
rw [compatible_clause_comm f1.number, compatible_clause_comm f1.gender,
compatible_clause_comm f1.case_, compatible_clause_comm f1.definite,
compatible_clause_comm f1.degree, compatible_clause_comm f1.pronType,
compatible_clause_comm f1.person, compatible_clause_comm f1.verbForm,
compatible_clause_comm f1.tense, compatible_clause_comm f1.aspect,
compatible_clause_comm f1.mood, compatible_clause_comm f1.voice,
compatible_clause_comm f1.polarity]

/-- The information-join of two bundles, field-by-field: keep every committed value
(left-biased per field, which on `compatible` inputs is symmetric since doubly
Expand Down
242 changes: 232 additions & 10 deletions Linglib/Morphology/Unification.lean
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,29 @@ Unification (§3.2.3) is "the most general feature structure `D` such that `D′
* `instance : PartialOrder UD.MorphFeatures` — subsumption ("only a partial order",
§3.2.3), with decidable `≤`.
* `instance : OrderBot UD.MorphFeatures` — the empty bundle is bottom.
* `UD.MorphFeatures.Compatible` — `Prop`-native compatibility;
`compatible_iff_bddAbove` characterizes the `Bool` check as boundedness above.
* `unify_isLUB`, `unify_comm`, `unify_self`, `bot_unify` — the §3.2.3 laws.
* `instance : SemilatticeInf UD.MorphFeatures` — the meet is Shieber's
*generalization* (anti-unification): total, unlike the join.
* `UD.MorphFeatures.Compatible` — boundedness above (`BddAbove {f, g}`), decidable
via the `Bool` check (`compatible_iff_bddAbove`).
* `unify_isLUB`, `unify_eq_some_iff_isLUB`, `unify_comm`, `unify_assoc`,
`unify_self`, `bot_unify`/`unify_bot`, `unify_mono` — the §3.2.3 laws plus
associativity and guarded monotonicity.

## Theory-neutrality boundary

Three strata with different statuses: the *record* is annotation consensus
(`Data/UD/Basic.lean`); the *order* `≤` is shared substrate every framework consumes
its own way (DM's matching clause, underspecification, syncretism down-sets); the
*operations* `⊔`/`⊓` are commitments of the unification tradition — [shieber-1986]
§3.1 states unification-as-sole-combinator as a design constraint, and rival
frameworks combine differently (DM matches and competes; Minimalist Agree values
asymmetrically). This file is *not* that tradition's headquarters: unification-based
grammar (PATR, HPSG, LFG — reentrant feature structures, phrasal combination) is a
syntax family whose substrate belongs in `Syntax/` when consuming studies demand it.
What lives here is only the tradition's morphological-bundle fragment — the algebra
of one token's Feats column — which it shares with rivals: at the level of claims
about morphological feature combination this file is a sibling of `DM/` and
`Nanosyntax/`, not a foundation beneath them.

## Implementation notes

Expand Down Expand Up @@ -146,10 +166,11 @@ instance : OrderBot MorphFeatures where

/-! ### Compatibility is boundedness above; unification is the least upper bound -/

/-- `Prop`-native compatibility: the two bundles have an upper bound in the
subsumption order. Characterized by the executable `compatible` check via
`compatible_iff_bddAbove`. -/
def Compatible (f g : MorphFeatures) : Prop := ∃ u, f ≤ u ∧ g ≤ u
/-- `Prop`-native compatibility: the pair is bounded above in the subsumption
order — mathlib's `BddAbove`, so the bounds API applies directly. Characterized by
the executable `compatible` check via `compatible_iff_bddAbove`, which also makes
it decidable. -/
def Compatible (f g : MorphFeatures) : Prop := BddAbove ({f, g} : Set MorphFeatures)

private theorem clause_of_some_some {α : Type _} [BEq α] [LawfulBEq α] {x y : α}
(h : ((some x : Option α).isNone || (some y : Option α).isNone
Expand Down Expand Up @@ -245,9 +266,17 @@ order-theoretic identity of "compatible". -/
theorem compatible_iff_bddAbove (f g : MorphFeatures) :
f.compatible g = true ↔ Compatible f g := by
constructor
· exact fun h => ⟨f.merge g, le_merge_left f g, le_merge_right h⟩
· rintro ⟨u, hf, hg⟩
exact compatible_of_le hf hg
· intro h
refine ⟨f.merge g, fun x hx => ?_⟩
rcases hx with rfl | hx
· exact le_merge_left _ g
· rw [Set.mem_singleton_iff.mp hx]
exact le_merge_right h
· rintro ⟨u, hu⟩
exact compatible_of_le (hu (Set.mem_insert _ _)) (hu (Set.mem_insert_of_mem _ rfl))

instance (f g : MorphFeatures) : Decidable (Compatible f g) :=
decidable_of_iff _ (compatible_iff_bddAbove f g)

/-- Unification succeeds exactly on compatible inputs (§3.2.3: otherwise it "fails"). -/
theorem unify_eq_some_iff (f g : MorphFeatures) :
Expand Down Expand Up @@ -329,4 +358,197 @@ the empty bundle returns the other input. -/
cases f
rfl

/-! ### Generalization: the meet

Shieber's *generalization* (anti-unification): the most specific bundle subsumed by
both inputs. Unlike unification it is total — the meet always exists — so
`MorphFeatures` is a genuine `SemilatticeInf` with `⊥`. -/

private def slotInf {α : Type _} [DecidableEq α] (a b : Option α) : Option α :=
if a = b then a else none

private theorem slotInf_flatLE_left {α : Type _} [DecidableEq α] (a b : Option α) :
(slotInf a b).FlatLE a := by
unfold slotInf; split
· exact .refl _
· exact .none_le _

private theorem slotInf_flatLE_right {α : Type _} [DecidableEq α] (a b : Option α) :
(slotInf a b).FlatLE b := by
unfold slotInf; split
· next h => subst h; exact .refl _
· exact .none_le _

private theorem flatLE_slotInf {α : Type _} [DecidableEq α] {a b c : Option α}
(h1 : c.FlatLE a) (h2 : c.FlatLE b) : c.FlatLE (slotInf a b) := by
intro x hx
have ha := h1 x hx
have hb := h2 x hx
unfold slotInf
rw [ha, hb]
simp

instance : Min MorphFeatures where
min f g :=
{ number := slotInf f.number g.number
gender := slotInf f.gender g.gender
case_ := slotInf f.case_ g.case_
definite := slotInf f.definite g.definite
degree := slotInf f.degree g.degree
pronType := slotInf f.pronType g.pronType
reflex := f.reflex && g.reflex
person := slotInf f.person g.person
verbForm := slotInf f.verbForm g.verbForm
tense := slotInf f.tense g.tense
aspect := slotInf f.aspect g.aspect
mood := slotInf f.mood g.mood
voice := slotInf f.voice g.voice
polarity := slotInf f.polarity g.polarity }

private theorem band_true_left {x y : Bool} (h : (x && y) = true) : x = true := by
cases x <;> simp_all

private theorem band_true_right {x y : Bool} (h : (x && y) = true) : y = true := by
cases y <;> simp_all

private theorem band_true_intro {x y : Bool} (hx : x = true) (hy : y = true) :
(x && y) = true := by simp [hx, hy]

instance : SemilatticeInf MorphFeatures :=
{ (inferInstance : PartialOrder MorphFeatures),
(inferInstance : Min MorphFeatures) with
inf := min
inf_le_left := fun f g => show Subsumes (min f g) f from
⟨slotInf_flatLE_left _ _, slotInf_flatLE_left _ _, slotInf_flatLE_left _ _,
slotInf_flatLE_left _ _, slotInf_flatLE_left _ _, slotInf_flatLE_left _ _,
fun hr => band_true_left hr,
slotInf_flatLE_left _ _, slotInf_flatLE_left _ _, slotInf_flatLE_left _ _,
slotInf_flatLE_left _ _, slotInf_flatLE_left _ _, slotInf_flatLE_left _ _,
slotInf_flatLE_left _ _⟩
inf_le_right := fun f g => show Subsumes (min f g) g from
⟨slotInf_flatLE_right _ _, slotInf_flatLE_right _ _, slotInf_flatLE_right _ _,
slotInf_flatLE_right _ _, slotInf_flatLE_right _ _, slotInf_flatLE_right _ _,
fun hr => band_true_right hr,
slotInf_flatLE_right _ _, slotInf_flatLE_right _ _, slotInf_flatLE_right _ _,
slotInf_flatLE_right _ _, slotInf_flatLE_right _ _, slotInf_flatLE_right _ _,
slotInf_flatLE_right _ _⟩
le_inf := fun c f g hcf hcg => by
obtain ⟨a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14⟩ := hcf
obtain ⟨b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14⟩ := hcg
exact ⟨flatLE_slotInf a1 b1, flatLE_slotInf a2 b2, flatLE_slotInf a3 b3,
flatLE_slotInf a4 b4, flatLE_slotInf a5 b5, flatLE_slotInf a6 b6,
fun hr => band_true_intro (a7 hr) (b7 hr),
flatLE_slotInf a8 b8, flatLE_slotInf a9 b9, flatLE_slotInf a10 b10,
flatLE_slotInf a11 b11, flatLE_slotInf a12 b12, flatLE_slotInf a13 b13,
flatLE_slotInf a14 b14⟩ }

/-! ### Unification computes least upper bounds — further laws -/

/-- Unification succeeds with value `u` exactly when `u` is the least upper bound. -/
theorem unify_eq_some_iff_isLUB {f g u : MorphFeatures} :
f.unify g = some u ↔ IsLUB {f, g} u := by
refine ⟨unify_isLUB, fun hu => ?_⟩
have hc : f.compatible g = true :=
compatible_of_le (hu.1 (Set.mem_insert _ _)) (hu.1 (Set.mem_insert_of_mem _ rfl))
have hm : f.unify g = some (f.merge g) := by simp [unify, hc]
rw [hm, Option.some_inj]
exact (unify_isLUB hm).unique hu

private theorem isLUB_pair_step {f g h v u : MorphFeatures} (hv : IsLUB {f, g} v) :
IsLUB {v, h} u ↔ IsLUB ({f, g, h} : Set MorphFeatures) u := by
have hub : upperBounds ({v, h} : Set MorphFeatures) = upperBounds {f, g, h} := by
ext w
constructor
· intro hw x hx
rcases hx with rfl | rfl | rfl
· exact (hv.1 (Set.mem_insert _ _)).trans (hw (Set.mem_insert _ _))
· exact (hv.1 (Set.mem_insert_of_mem _ rfl)).trans (hw (Set.mem_insert _ _))
· exact hw (Set.mem_insert_of_mem _ rfl)
· intro hw x hx
rcases hx with rfl | rfl
· exact hv.2 fun y hy => by
rcases hy with rfl | rfl
· exact hw (Set.mem_insert _ _)
· exact hw (Set.mem_insert_of_mem _ (Set.mem_insert _ _))
· exact hw (Set.mem_insert_of_mem _ (Set.mem_insert_of_mem _ rfl))
unfold IsLUB
rw [hub]

/-- Unification is associative, with failure propagating ([shieber-1986] §3.2.3's
order-independence): both associations compute the lub of all three bundles. -/
theorem unify_assoc (f g h : MorphFeatures) :
(f.unify g).bind (·.unify h) = (g.unify h).bind (f.unify ·) := by
apply Option.ext
intro u
simp only [Option.bind_eq_some_iff]
constructor
· rintro ⟨v, hv, hu⟩
have h3 : IsLUB ({f, g, h} : Set MorphFeatures) u :=
(isLUB_pair_step (unify_isLUB hv)).mp (unify_isLUB hu)
have hgh : g.compatible h = true := by
refine compatible_of_le (u := u) ?_ ?_
· exact h3.1 (Set.mem_insert_of_mem _ (Set.mem_insert _ _))
· exact h3.1 (Set.mem_insert_of_mem _ (Set.mem_insert_of_mem _ rfl))
refine ⟨g.merge h, by simp [unify, hgh], ?_⟩
have hw : IsLUB ({g, h} : Set MorphFeatures) (g.merge h) :=
unify_isLUB (by simp [unify, hgh])
rw [unify_eq_some_iff_isLUB]
have : IsLUB ({g, h, f} : Set MorphFeatures) u := by
have : ({g, h, f} : Set MorphFeatures) = {f, g, h} := by
ext x; simp [Set.mem_insert_iff]; tauto
rw [this]; exact h3
have step := (isLUB_pair_step hw).mpr this
have : ({g.merge h, f} : Set MorphFeatures) = {f, g.merge h} := Set.pair_comm _ _
rwa [← this]
· rintro ⟨w, hw, hu⟩
have hu' : IsLUB ({g.merge h, f} : Set MorphFeatures) u := by
rw [Set.pair_comm]
rw [unify_eq_some_iff_isLUB] at hu
have hwm : g.merge h = w := by
have : g.unify h = some w := hw
have hgh : g.compatible h = true :=
compatible_of_le ((unify_isLUB hw).1 (Set.mem_insert _ _))
((unify_isLUB hw).1 (Set.mem_insert_of_mem _ rfl))
simpa [unify, hgh] using this
rwa [← hwm] at hu
have h3 : IsLUB ({g, h, f} : Set MorphFeatures) u := by
have hgh : g.compatible h = true :=
compatible_of_le ((unify_isLUB hw).1 (Set.mem_insert _ _))
((unify_isLUB hw).1 (Set.mem_insert_of_mem _ rfl))
exact (isLUB_pair_step (unify_isLUB (by simp [unify, hgh] :
g.unify h = some (g.merge h)))).mp hu'
have h3' : IsLUB ({f, g, h} : Set MorphFeatures) u := by
have : ({g, h, f} : Set MorphFeatures) = {f, g, h} := by
ext x; simp [Set.mem_insert_iff]; tauto
rwa [this] at h3
have hfg : f.compatible g = true := by
refine compatible_of_le (u := u) ?_ ?_
· exact h3'.1 (Set.mem_insert _ _)
· exact h3'.1 (Set.mem_insert_of_mem _ (Set.mem_insert _ _))
refine ⟨f.merge g, by simp [unify, hfg], ?_⟩
rw [unify_eq_some_iff_isLUB]
exact (isLUB_pair_step (unify_isLUB (by simp [unify, hfg] :
f.unify g = some (f.merge g)))).mpr h3'

/-- The empty bundle is a right identity for unification. -/
@[simp] theorem unify_bot (f : MorphFeatures) : f.unify (⊥ : MorphFeatures) = some f := by
rw [unify_comm]; exact bot_unify f

/-- Unification fails exactly on incompatible inputs. -/
theorem unify_eq_none_iff (f g : MorphFeatures) :
f.unify g = none ↔ ¬ Compatible f g := by
rw [← compatible_iff_bddAbove]
unfold unify
by_cases hc : f.compatible g = true <;> simp [hc]

/-- Unification is monotone where defined: shrinking both inputs preserves success and
shrinks the output. (Unguarded `merge`-monotonicity is false — the guard is needed.) -/
theorem unify_mono {f₁ f₂ g₁ g₂ u₂ : MorphFeatures} (hf : f₁ ≤ f₂) (hg : g₁ ≤ g₂)
(h2 : f₂.unify g₂ = some u₂) : ∃ u₁, f₁.unify g₁ = some u₁ ∧ u₁ ≤ u₂ := by
have hlub := unify_isLUB h2
have hf1 : f₁ ≤ u₂ := hf.trans (hlub.1 (Set.mem_insert _ _))
have hg1 : g₁ ≤ u₂ := hg.trans (hlub.1 (Set.mem_insert_of_mem _ rfl))
have hc : f₁.compatible g₁ = true := compatible_of_le hf1 hg1
exact ⟨f₁.merge g₁, by simp [unify, hc], merge_le hf1 hg1⟩

end UD.MorphFeatures
19 changes: 17 additions & 2 deletions Linglib/Morphology/Word.lean
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,27 @@ instance (w1 w2 : Word) : Decidable (Word.Agree w1 w2) := by
UD.MorphFeatures.compatible_self w.phi

/-- φ-agreement is symmetric — the docstring's "symmetric tolerance relation", as a
theorem. (It is *not* transitive: an unspecified feature is a wildcard, so
`she ~ they ~ he` while `she ≁ he`.) -/
theorem. -/
@[symm] theorem Word.Agree.symm {w1 w2 : Word} (h : Word.Agree w1 w2) : Word.Agree w2 w1 := by
unfold Word.Agree at h ⊢
rwa [UD.MorphFeatures.compatible_comm]

/-- φ-agreement is *not* transitive: an unspecified feature is a wildcard, so
underspecified *they* agrees with both *she* and *he* while *she ≁ he*. -/
theorem Word.Agree.not_transitive :
¬ ∀ w1 w2 w3 : Word, Word.Agree w1 w2 → Word.Agree w2 w3 → Word.Agree w1 w3 := by
intro h
exact absurd
(h ⟨"she", .PRON, { person := some .third, number := some .Sing, gender := some .Fem }⟩
⟨"they", .PRON, { person := some .third }⟩
⟨"he", .PRON, { person := some .third, number := some .Sing, gender := some .Masc }⟩
(by decide) (by decide))
(by decide)

-- `reflex` is deliberately not an agreement feature: a reflexive-marked token still
-- agrees with an unmarked one (the φ-projection drops it).
example : Word.Agree ⟨"sich", .PRON, { reflex := true }⟩ ⟨"Kind", .NOUN, {}⟩ := by decide

/-- Derive a passive variant: sets voice to passive. The valence change
(detransitivization) is a frame-level fact carried by the passive analysis on
`DepTree.frames`, not token data. Composes with `VerbEntry.toWordPastPart`. -/
Expand Down
1 change: 1 addition & 0 deletions Linglib/Studies/Bubnov2026.lean
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ set_option autoImplicit false
namespace Bubnov2026

open DeganoAloni2025
open scoped Syntax.Case.Caha
open DeganoAloni2025.DependenceLogic
open Morphology.Nanosyntax
open Dekier2021
Expand Down
3 changes: 2 additions & 1 deletion Linglib/Studies/Caha2009.lean
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,12 @@ Ch. 6), and Hungarian (GEN-less, dative-as-possessor syncretism per
namespace Caha2009

open Features (Case)
open scoped Syntax.Case.Caha

/-! ## Caha containment-respect predicate

Does an inventory respect Caha's containment hierarchy? True iff `inv`
is downward-closed under the canonical `PartialOrder Case` (Caha
is downward-closed under the scoped Caha order (`Syntax.Case.Caha`;
containment) defined in `Core/Case/Order.lean`: whenever `c ∈ inv` and
`d ≤ c`, then `d ∈ inv`. Off-hierarchy cases (ERG, ABS, INST, COM, …)
impose no constraint — in the Caha order they only have `c ≤ c`, so
Expand Down
Loading
Loading