Skip to content
Open
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
1 change: 1 addition & 0 deletions Cslib.lean
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public import Cslib.Computability.Automata.NA.BuchiInter
public import Cslib.Computability.Automata.NA.Concat
public import Cslib.Computability.Automata.NA.Hist
public import Cslib.Computability.Automata.NA.Loop
public import Cslib.Computability.Automata.NA.Pair
public import Cslib.Computability.Automata.NA.Prod
public import Cslib.Computability.Automata.NA.Sum
public import Cslib.Computability.Automata.NA.ToDA
Expand Down
30 changes: 6 additions & 24 deletions Cslib/Computability/Automata/NA/Loop.lean
Original file line number Diff line number Diff line change
Expand Up @@ -120,30 +120,12 @@ the state `()` marks the boundaries between the finite words in `xls`. -/
theorem loop_run_exists [Inhabited Symbol] {xls : ωSequence (List Symbol)}
(h : ∀ k, (xls k) ∈ language na - 1) :
∃ ss, na.loop.Run xls.flatten ss ∧ ∀ k, ss (xls.cumLen k) = inl () := by
have : Inhabited State := by
choose s0 _ using (h 0).left
exact { default := s0 }
choose sls h_sls using fun k ↦ loop_fin_run_exists <| (h k).left
let segs := ωSequence.mk fun k ↦ (sls k).take (xls k).length
have h_len : xls.cumLen = segs.cumLen := by ext k; induction k <;> grind
have h_pos (k : ℕ) : (segs k).length > 0 := by grind [List.eq_nil_iff_length_eq_zero]
have h_mono := cumLen_strictMono h_pos
have h_zero := cumLen_zero (ls := segs)
have h_seg0 (k : ℕ) : (segs k)[0]! = inl () := by grind
refine ⟨segs.flatten, Run.mk ?_ ?_, ?_⟩
· simp [h_seg0, flatten_def, FinAcc.loop]
· intro n
simp only [h_len, flatten_def]
have := segment_lower_bound h_mono h_zero n
by_cases h_n : n + 1 < segs.cumLen (segment segs.cumLen n + 1)
· grind [segment_range_val h_mono (by grind) h_n]
· have h1 : segs.cumLen (segment segs.cumLen n + 1) = n + 1 := by
grind [segment_upper_bound h_mono h_zero n]
have h2 : segment segs.cumLen (n + 1) = segment segs.cumLen n + 1 := by
simp [← h1, segment_idem h_mono]
simp [h1, h2, h_seg0]
grind
· simp [h_len, flatten_def, segment_idem h_mono, h_seg0]
let ts := ωSequence.const (inl () : Unit ⊕ State)
have h_mtr (k : ℕ) : na.loop.MTr (ts k) (xls k) (ts (k + 1)) := by grind [loop_fin_run_mtr]
have h_pos (k : ℕ) : (xls k).length > 0 := by grind
obtain ⟨ss, _, _⟩ := LTS.ωTr.flatten h_mtr h_pos
use ss
grind [Run.mk, FinAcc.loop, cumLen_zero (ls := xls)]

namespace Buchi

Expand Down
81 changes: 81 additions & 0 deletions Cslib/Computability/Automata/NA/Pair.lean
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/-
Copyright (c) 2025 Ching-Tsun Chou. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Ching-Tsun Chou
-/

module

public import Cslib.Computability.Languages.RegularLanguage

@[expose] public section

/-! # Languages determined by pairs of states
-/

namespace Cslib

open Language Automata Acceptor

variable {Symbol : Type*} {State : Type}

/-- `LTS.pairLang s t` is the language of finite words that can take the LTS
from state `s` to state `t`. -/
def LTS.pairLang (lts : LTS State Symbol) (s t : State) : Language Symbol :=
{ xl | lts.MTr s xl t }

@[simp, scoped grind =]
theorem LTS.mem_pairLang (lts : LTS State Symbol) (s t : State) (xl : List Symbol) :
xl ∈ (lts.pairLang s t) ↔ lts.MTr s xl t := Iff.rfl

/-- `LTS.pairLang s t` is a regular language if there are only finitely many states. -/
@[simp]
theorem LTS.pairLang_regular [Finite State] (lts : LTS State Symbol) (s t : State) :
Comment on lines +24 to +33
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems these should be in the LTS module.

(lts.pairLang s t).IsRegular := by
rw [IsRegular.iff_nfa]
use State, inferInstance, (NA.FinAcc.mk ⟨lts, {s}⟩ {t})
ext xl; simp [LTS.mem_pairLang]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Either of these works:

Suggested change
ext xl; simp [LTS.mem_pairLang]
ext
simp

or

Suggested change
ext xl; simp [LTS.mem_pairLang]
ext xl
grind [NA.FinAcc.instAcceptor]

(is this a missing grind annotation maybe?)


namespace NA.Buchi

open Set Filter ωSequence ωLanguage ωAcceptor

/-- The ω-language accepted by a finite-state Büchi automaton is the finite union of ω-languages
of the form `L * M^ω`, where all `L`s and `M`s are regular languages. -/
theorem language_eq_fin_iSup_hmul_omegaPow
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you try to open the LTS scope here, grind times out on some proofs, which is a bit at odds with us right above adding an annotation for LTS.mem_pairLang.

Soon there will be an equivalent for simp sets for grind, where you can specify custom sets of grind rules as opposed to having everything attached to scopes. I think this could help separate out what I think are problems with the LTS annotations being too costly. It would be helpful for me if you can think a bit about what grind sets you might like for automata once core releases this feature, e.g. one set for all of automata or if more refinement is helpful.

[Inhabited Symbol] [Finite State] (na : NA.Buchi State Symbol) :
language na = ⨆ s ∈ na.start, ⨆ t ∈ na.accept, (na.pairLang s t) * (na.pairLang t t)^ω := by
ext xs
simp only [NA.Buchi.instωAcceptor, ωAcceptor.mem_language,
ωLanguage.mem_iSup, ωLanguage.mem_hmul, LTS.mem_pairLang]
constructor
· rintro ⟨ss, h_run, h_inf⟩
obtain ⟨t, _, h_t⟩ := frequently_in_finite_type.mp h_inf
refine ⟨ss 0, by grind [h_run.start], t, by assumption, ?_⟩
Comment on lines +53 to +54
Copy link
Collaborator

@chenson2018 chenson2018 Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two things here:

  • when it is very easy to replace assumption with the term I prefer this, for both readability and because I think sometimes the check for definitional equality can be expensive
  • Using h_run.start is a bit odd, because grind should handle projecting out of structures
Suggested change
obtain ⟨t, _, h_t⟩ := frequently_in_finite_type.mp h_inf
refine ⟨ss 0, by grind [h_run.start], t, by assumption, ?_⟩
obtain ⟨t, tmem, h_t⟩ := frequently_in_finite_type.mp h_inf
use ss 0, by grind [NA.Run], t, tmem

obtain ⟨f, h_mono, h_f⟩ := frequently_iff_strictMono.mp h_t
refine ⟨xs.take (f 0), ?_, xs.drop (f 0), ?_, by grind⟩
· grind [extract_eq_drop_take, LTS.ωTr_mTr h_run.trans (Nat.zero_le (f 0))]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is a bit more clear if you split it up and pass off arithmetic to grind:

Suggested change
· grind [extract_eq_drop_take, LTS.ωTr_mTr h_run.trans (Nat.zero_le (f 0))]
· have : na.MTr (ss 0) (xs.extract 0 (f 0)) (ss (f 0)) := by grind [LTS.ωTr_mTr, NA.Run]
grind [extract_eq_drop_take]

· simp only [omegaPow_seq_prop, LTS.mem_pairLang]
refine ⟨(f · - f 0), by grind [Nat.base_zero_strictMono], by simp, ?_⟩
intro n
grind [extract_drop, h_mono.monotone (Nat.zero_le n), h_mono.monotone (Nat.zero_le (n + 1)),
LTS.ωTr_mTr h_run.trans <| h_mono.monotone (show n ≤ n + 1 by grind)]
Comment on lines +59 to +62
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's what I came up with for readability:

Suggested change
refine ⟨(f · - f 0), by grind [Nat.base_zero_strictMono], by simp, ?_⟩
intro n
grind [extract_drop, h_mono.monotone (Nat.zero_le n), h_mono.monotone (Nat.zero_le (n + 1)),
LTS.ωTr_mTr h_run.trans <| h_mono.monotone (show n ≤ n + 1 by grind)]
use (f · - f 0)
split_ands
· grind [Nat.base_zero_strictMono]
· grind
· intro n
have mono_f (k : ℕ) : f 0 ≤ f (n + k) := h_mono.monotone (by grind)
grind [extract_drop, mono_f 0, LTS.ωTr_mTr h_run.trans <| h_mono.monotone (?_ : n ≤ n + 1)]

In general, I think we are doing something wrong with grind if we're ever writing something as low level as Nat.zero_le.

· rintro ⟨s, _, t, _, yl, h_yl, zs, h_zs, rfl⟩
obtain ⟨zls, rfl, h_zls⟩ := mem_omegaPow.mp h_zs
let ts := ωSequence.const t
have h_mtr (n : ℕ) : na.MTr (ts n) (zls n) (ts (n + 1)) := by
grind [Language.mem_sub_one, LTS.mem_pairLang]
have h_pos (n : ℕ) : (zls n).length > 0 := by
grind [Language.mem_sub_one, List.eq_nil_iff_length_eq_zero]
obtain ⟨zss, h_zss, _⟩ := LTS.ωTr.flatten h_mtr h_pos
have (n : ℕ) : zss (zls.cumLen n) = t := by grind
obtain ⟨xss, _, _, _, _⟩ := LTS.ωTr.append h_yl h_zss (by grind [cumLen_zero (ls := zls)])
refine ⟨xss, by grind [NA.Run.mk], ?_⟩
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
refine ⟨xss, by grind [NA.Run.mk], ?_⟩
use xss, by grind [NA.Run]

apply (drop_frequently_iff_frequently yl.length).mp
apply frequently_iff_strictMono.mpr
use zls.cumLen
grind [cumLen_strictMono]

end NA.Buchi

end Cslib
33 changes: 30 additions & 3 deletions Cslib/Computability/Languages/OmegaRegularLanguage.lean
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ module
public import Cslib.Computability.Automata.DA.Buchi
public import Cslib.Computability.Automata.NA.BuchiEquiv
public import Cslib.Computability.Automata.NA.BuchiInter
public import Cslib.Computability.Automata.NA.Concat
public import Cslib.Computability.Automata.NA.Loop
public import Cslib.Computability.Automata.NA.Pair
public import Cslib.Computability.Automata.NA.Sum
public import Cslib.Computability.Languages.ExampleEventuallyZero
public import Cslib.Computability.Languages.RegularLanguage
public import Mathlib.Data.Finite.Card
public import Mathlib.Data.Finite.Sigma
public import Mathlib.Data.Finite.Sum
public import Mathlib.Logic.Equiv.Fin.Basic

@[expose] public section

Expand Down Expand Up @@ -185,6 +185,33 @@ theorem IsRegular.omegaPow [Inhabited Symbol] {l : Language Symbol}
use Unit ⊕ State, inferInstance, ⟨na.loop, {inl ()}⟩
exact NA.Buchi.loop_language_eq

/-- An ω-language is regular iff it is the finite union of ω-languages of the form `L * M^ω`,
where all `L`s and `M`s are regular languages. -/
theorem IsRegular.eq_fin_iSup_hmul_omegaPow [Inhabited Symbol] (p : ωLanguage Symbol) :
p.IsRegular ↔ ∃ n : ℕ, ∃ l m : Fin n → Language Symbol,
(∀ i, (l i).IsRegular ∧ (m i).IsRegular) ∧ p = ⨆ i, (l i) * (m i)^ω := by
constructor
· rintro ⟨State, _, na, rfl⟩
rw [NA.Buchi.language_eq_fin_iSup_hmul_omegaPow na]
have eq_start := Finite.equivFin ↑na.start
have eq_accept := Finite.equivFin ↑na.accept
have eq_prod := eq_start.prodCongr eq_accept
have eq := (eq_prod.trans finProdFinEquiv).symm
refine ⟨Nat.card ↑na.start * Nat.card ↑na.accept,
fun i ↦ na.pairLang (eq i).1 (eq i).2,
fun i ↦ na.pairLang (eq i).2 (eq i).2,
by grind [LTS.pairLang_regular], ?_⟩
ext xs
simp only [mem_iSup]
refine ⟨?_, by grind⟩
rintro ⟨s, h_s, t, h_t, h_mem⟩
use eq.invFun (⟨s, h_s⟩, ⟨t, h_t⟩)
simp [h_mem]
· rintro ⟨n, l, m, _, rfl⟩
rw [← iSup_univ]
apply IsRegular.iSup
grind [IsRegular.hmul, IsRegular.omegaPow]

/-- McNaughton's Theorem. -/
proof_wanted IsRegular.iff_da_muller {p : ωLanguage Symbol} :
p.IsRegular ↔
Expand Down
2 changes: 1 addition & 1 deletion Cslib/Foundations/Data/Nat/Segment.lean
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ private lemma base_zero_shift (f : ℕ → ℕ) :
(f · - f 0) 0 = 0 := by
simp

private lemma base_zero_strictMono (hm : StrictMono f) :
theorem base_zero_strictMono (hm : StrictMono f) :
StrictMono (f · - f 0) := by
intro m n h_m_n; simp
have := hm h_m_n
Expand Down
19 changes: 19 additions & 0 deletions Cslib/Foundations/Data/OmegaSequence/InfOcc.lean
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Authors: Ching-Tsun Chou
module

public import Cslib.Foundations.Data.OmegaSequence.Defs
public import Mathlib.Data.Fintype.Pigeonhole
public import Mathlib.Order.Filter.AtTopBot.Basic
public import Mathlib.Order.Filter.Cofinite

Expand Down Expand Up @@ -40,6 +41,24 @@ theorem frequently_iff_strictMono {p : ℕ → Prop} :
have h_range : range f ⊆ {n | p n} := by grind
grind [Infinite.mono, infinite_range_of_injective, StrictMono.injective]

/-- In a finite type, the elements of a set occurs infinitely often iff
some element in the set occurs infinitely often. -/
theorem frequently_in_finite_type [Finite α] {s : Set α} {xs : ωSequence α} :
(∃ᶠ k in atTop, xs k ∈ s) ↔ ∃ x ∈ s, ∃ᶠ k in atTop, xs k = x := by
constructor
· intro h_inf
rw [Nat.frequently_atTop_iff_infinite] at h_inf
have : Infinite (xs ⁻¹' s) := h_inf.to_subtype
let rf := Set.restrictPreimage s xs
obtain ⟨⟨x, h_x⟩, h_inf'⟩ := Finite.exists_infinite_fiber rf
rw [← Set.infinite_range_iff (Subtype.val_injective.comp Subtype.val_injective)] at h_inf'
simp only [range, comp_apply, Subtype.exists, mem_preimage, mem_singleton_iff,
restrictPreimage_mk, Subtype.mk.injEq, ← Nat.frequently_atTop_iff_infinite, rf] at h_inf'
grind
· rintro ⟨_, _, h_inf⟩
apply Frequently.mono h_inf
grind

end ωSequence

end Cslib
50 changes: 44 additions & 6 deletions Cslib/Foundations/Semantics/LTS/Basic.lean
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Authors: Fabrizio Montesi
module

public import Cslib.Init
public import Cslib.Foundations.Data.OmegaSequence.Init
public import Cslib.Foundations.Data.OmegaSequence.Flatten
public import Cslib.Foundations.Semantics.FLTS.Basic
public import Mathlib.Data.Set.Finite.Basic
public import Mathlib.Order.ConditionallyCompleteLattice.Basic
Expand Down Expand Up @@ -264,17 +264,54 @@ theorem LTS.ωTr.cons (htr : lts.Tr s μ t) (hωtr : lts.ωTr ss μs) (hm : ss 0
induction i <;> grind

/-- Prepends an infinite execution with a finite execution. -/
theorem LTS.ωTr.append (hmtr : lts.MTr s μl t) (hωtr : lts.ωTr ss μs)
(hm : ss 0 = t) : ∃ ss', lts.ωTr ss' (μl ++ω μs) ∧ ss' 0 = s ∧ ss' μl.length = t := by
theorem LTS.ωTr.append
(hmtr : lts.MTr s μl t) (hωtr : lts.ωTr ss μs) (hm : ss 0 = t) :
∃ ss', lts.ωTr ss' (μl ++ω μs) ∧ ss' 0 = s ∧ ss' μl.length = t ∧ ss'.drop μl.length = ss := by
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this new condition have been here from the initial addition of this theorem?

obtain ⟨sl, _, _, _, _⟩ := LTS.MTr.exists_states hmtr
refine ⟨sl ++ω ss.drop 1, ?_, by grind [get_append_left], by grind [get_append_left]⟩
refine ⟨sl.take μl.length ++ω ss, ?_,
by grind [get_append_left], by grind [get_append_left], by grind [drop_append_of_ge_length]⟩
Comment on lines +271 to +272
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My rule of thumb would be if it doesn't fit on a single line and you have a nicely ordered conjunction as your goal, do

Suggested change
refine ⟨sl.take μl.length ++ω ss, ?_,
by grind [get_append_left], by grind [get_append_left], by grind [drop_append_of_ge_length]⟩
use sl.take μl.length ++ω ss
split_ands

and then a bullet for each proof.

intro n
by_cases n < μl.length
· grind [get_append_left]
· by_cases n = μl.length
· grind [get_append_left]
· grind [get_append_left, get_append_right']
· grind [get_append_right', hωtr (n - μl.length - 1)]

open Nat in
/-- Concatenating an infinite sequence of finite executions that connect an infinite sequence
of intermediate states. -/
theorem LTS.ωTr.flatten [Inhabited Label] {ts : ωSequence State} {μls : ωSequence (List Label)}
(hmtr : ∀ k, lts.MTr (ts k) (μls k) (ts (k + 1))) (hpos : ∀ k, (μls k).length > 0) :
∃ ss, lts.ωTr ss μls.flatten ∧ ∀ k, ss (μls.cumLen k) = ts k := by
have : Inhabited State := by exact {default := ts 0}
choose sls h_sls using fun k ↦ LTS.MTr.exists_states (hmtr k)
let segs := ωSequence.mk fun k ↦ (sls k).take (μls k).length
have h_len : μls.cumLen = segs.cumLen := by ext k; induction k <;> grind
have h_pos (k : ℕ) : (segs k).length > 0 := by grind [List.eq_nil_iff_length_eq_zero]
have h_mono := cumLen_strictMono h_pos
have h_zero := cumLen_zero (ls := segs)
have h_seg0 (k : ℕ) : (segs k)[0]! = ts k := by grind
refine ⟨segs.flatten, ?_, by simp [h_len, flatten_def, segment_idem h_mono, h_seg0]⟩
intro n
simp only [h_len, flatten_def]
have := segment_lower_bound h_mono h_zero n
by_cases h_n : n + 1 < segs.cumLen (segment segs.cumLen n + 1)
· have := segment_range_val h_mono (by grind) h_n
have : n + 1 - segs.cumLen (segment segs.cumLen n) < (μls (segment segs.cumLen n)).length := by
grind
grind
· have h1 : segs.cumLen (segment segs.cumLen n + 1) = n + 1 := by
grind [segment_upper_bound h_mono h_zero n]
have h2 : segment segs.cumLen (n + 1) = segment segs.cumLen n + 1 := by
simp [← h1, segment_idem h_mono]
have : n + 1 - segs.cumLen (segment segs.cumLen n) = (μls (segment segs.cumLen n)).length := by
grind
have h3 : ts (segment segs.cumLen n + 1) =
(sls (segment segs.cumLen n))[n + 1 - segs.cumLen (segment segs.cumLen n)]! := by
grind
simp [h1, h2, h_seg0, h3]
Comment on lines +309 to +312
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this file very slow to edit for you as well? This is all the proof golfing I could stand to do:

Suggested change
have h3 : ts (segment segs.cumLen n + 1) =
(sls (segment segs.cumLen n))[n + 1 - segs.cumLen (segment segs.cumLen n)]! := by
grind
simp [h1, h2, h_seg0, h3]
simp [h1, h2, h_seg0]

grind

end ωMultiStep

section Total
Expand Down Expand Up @@ -319,7 +356,8 @@ theorem LTS.Total.mTr_ωTr [Inhabited Label] [ht : lts.Total] {μl : List Label}
(hm : lts.MTr s μl t) : ∃ μs ss, lts.ωTr ss (μl ++ω μs) ∧ ss 0 = s ∧ ss μl.length = t := by
let μs : ωSequence Label := .const default
obtain ⟨ss', ho, h0⟩ := LTS.Total.ωTr_exists (h := ht) t μs
refine ⟨μs, LTS.ωTr.append hm ho h0⟩
use μs
grind [LTS.ωTr.append hm ho h0]

/-- `LTS.totalize` constructs a total LTS from any given LTS by adding a sink state. -/
def LTS.totalize (lts : LTS State Label) : LTS (State ⊕ Unit) Label where
Expand Down