Skip to content

feat(Automata): Regular languages are closed under reversal. - #775

Open
Lsonic233 wants to merge 11 commits into
leanprover:mainfrom
Lsonic233:na-reverse
Open

feat(Automata): Regular languages are closed under reversal. #775
Lsonic233 wants to merge 11 commits into
leanprover:mainfrom
Lsonic233:na-reverse

Conversation

@Lsonic233

Copy link
Copy Markdown

Proves that A^R = {w^R | w ∈ A} is regular if A is regular, i.e regular languages are closed under reversal. The proof takes the NFA for A and constructs a new NFA by swapping the accept and start states and reversing all the transition arrows.

Reverse.lean defines the reversal of a FinAcc and proves that it accepts the reverse of the language accepted by the original automaton. The Language reversal is defined in mathlib.

Added a theorem IsRegular.reverse in RegularLanguage.lean to prove that regular languages are closed under reversal.

AI Usage : I wrote down all the theorem statements and initial proofs with minimal. I then used claude to rewrite proofs for style compliance.

Comment thread Cslib/Computability/Languages/RegularLanguage.lean Outdated
Comment thread Cslib/Computability/Automata/NA/Reverse.lean Outdated
Comment thread Cslib/Computability/Automata/NA/Reverse.lean Outdated
Comment thread Cslib/Computability/Automata/NA/Reverse.lean
Comment thread Cslib/Computability/Automata/NA/Reverse.lean
Comment thread Cslib/Computability/Automata/NA/Reverse.lean Outdated
Comment thread Cslib/Computability/Automata/NA/Reverse.lean Outdated
Comment thread Cslib/Computability/Automata/NA/Reverse.lean Outdated
The commit adding Cslib/Foundations/Semantics/LTS/Reverse.lean did not
add the corresponding import to Cslib.lean, so lake exe mk_all --check
failed in CI.
obtain ⟨mid, h1, h2⟩ := LTS.MTr.split hmtr
simp only [LTS.MTr.singleton_iff] at h2
exact ⟨mid, h2, ih.mpr h1⟩

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Can you add the result about CanReach too?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Generally it would be good to write a theorem about every def in Cslib/Foundations/Semantics/LTS/Basic.lean, assuming there is something true to state!

@Lsonic233 Lsonic233 Aug 5, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Will do. Would this be an exhaustive list of theorems to prove?
CanReach
UnlabelledTr - this relation gets flipped
image - image of a state in reverse is the preimage in original
imageMultistep - same as above
HasOutLabel - outgoing labels in the reverse are its incoming labels in the original.
BoundedUpTo - reversal preserves this

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Those all sound great!

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

While you are at it, could you also add a corresponding theorem for LTS.Execution? I hope that doesn't require too much work.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Wrote up proofs for the theorems stated above - please let me know if there is anything to be added.
For LTS.Execution I wrote down a long and not so reader friendly proof - claude helped use more automation and make it compact.

Comment thread Cslib/Computability/Languages/RegularLanguage.lean Outdated
Comment thread Cslib/Foundations/Semantics/LTS/Reverse.lean Outdated
Comment thread Cslib/Computability/Automata/NA/Reverse.lean Outdated
Comment thread Cslib/Computability/Automata/NA/Reverse.lean Outdated
Comment thread Cslib/Computability/Languages/RegularLanguage.lean
obtain ⟨mid, h1, h2⟩ := LTS.MTr.split hmtr
simp only [LTS.MTr.singleton_iff] at h2
exact ⟨mid, h2, ih.mpr h1⟩

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

While you are at it, could you also add a corresponding theorem for LTS.Execution? I hope that doesn't require too much work.

@ctchou ctchou left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Except for the minor import issue, I think this PR is ready for merge.

Comment thread Cslib/Foundations/Semantics/LTS/Reverse.lean Outdated
Comment thread Cslib/Computability/Languages/RegularLanguage.lean Outdated
Comment thread Cslib/Computability/Languages/RegularLanguage.lean Outdated
Comment thread Cslib/Foundations/Semantics/LTS/Reverse.lean

open Acceptor Language

variable {Symbol State : Type*}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

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

to match the order of arguments to FinAcc

Comment thread Cslib/Foundations/Semantics/LTS/Reverse.lean
Comment on lines +194 to +207
open NA in
/-- The reversal of a regular language is regular. -/
theorem IsRegular.reverse {l : Language Symbol} (h : l.IsRegular) : (l.reverse).IsRegular := by
rw [IsRegular.iff_nfa] at h ⊢
obtain ⟨State, h_fin, nfa, rfl⟩ := h
use State, inferInstance, nfa.reverse, FinAcc.reverse_language_eq nfa

/-- A language is regular iff its reversal is regular. -/
@[simp]
theorem IsRegular.reverse_iff {l : Language Symbol} : (l.reverse).IsRegular ↔ l.IsRegular := by
constructor
· intro h
simpa using IsRegular.reverse h
· exact IsRegular.reverse

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Optional:

Suggested change
open NA in
/-- The reversal of a regular language is regular. -/
theorem IsRegular.reverse {l : Language Symbol} (h : l.IsRegular) : (l.reverse).IsRegular := by
rw [IsRegular.iff_nfa] at h ⊢
obtain ⟨State, h_fin, nfa, rfl⟩ := h
use State, inferInstance, nfa.reverse, FinAcc.reverse_language_eq nfa
/-- A language is regular iff its reversal is regular. -/
@[simp]
theorem IsRegular.reverse_iff {l : Language Symbol} : (l.reverse).IsRegular ↔ l.IsRegular := by
constructor
· intro h
simpa using IsRegular.reverse h
· exact IsRegular.reverse
open NA in
/-- A language is regular iff its reversal is regular. -/
@[simp]
theorem IsRegular.reverse_iff {l : Language Symbol} : l.reverse.IsRegular ↔ l.IsRegular := by
simp_rw [IsRegular.iff_nfa]
congr! 4
rw [FinAcc.reverse_involutive.surjective.exists]
simp [FinAcc.reverse_language_eq, Language.reverse_injective.eq_iff]
alias ⟨_, IsRegular.reverse⟩ := IsRegular.reverse_iff

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Your proof looks better - we could replace the congr! 4 line with refine exists₂_congr fun State _ => ?_.
Also the previous proof follows the same pattern that all other closure proofs follow (reversal stands out because its the only iff theorem of the closure ones). Would you recommend switching it out for this?

Comment thread Cslib/Computability/Automata/NA/Reverse.lean
Comment thread Cslib/Foundations/Semantics/LTS/Reverse.lean Outdated
Comment thread Cslib/Foundations/Semantics/LTS/Reverse.lean Outdated

@ctchou ctchou left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

See my comment on Execution.reverse.

@ctchou ctchou left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Actually Execution.reverse can be proved in two lines using grind:

  use by grind
  grind [reverse_tr]

Note that the definition of LTS.Execution consists of a universally quantified statement over successive pairs of states in ss plus some boundary conditions, which are the sort of things that grind is very good at using and proving. There are enough facts about List.reverse already in mathlib that you don't even need to think about them.

@chenson2018

Copy link
Copy Markdown
Collaborator

See my comment on Execution.reverse.

I'm not following this PR closely, but this suggestion of use by grind seems like a very odd style to me.

@ctchou

ctchou commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator

What is a better solution? A single grind [reverse_tr] didn't work. The goal has the following form:

  ∃ _ : ss.length = μs.length + 1, ss[0] = s1 ∧ ss[ss.length - 1] = s2 ∧
  ∀ k, {_ : k < μs.length} → lts.Tr ss[k] μs[k] ss[k + 1]

@ctchou

ctchou commented Aug 8, 2026

Copy link
Copy Markdown
Collaborator

@chenson2018 Do you want to add anything concerning the proof of Execution.reverse which you are not very happy about? Personally I think this PR is ready to merge.

@ctchou ctchou left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM now.

Comment on lines +56 to +57
ext xs
simp only [mem_language, mem_reverse, accepts_reverse]

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Terminal simp should not be squeezed

Suggested change
ext xs
simp only [mem_language, mem_reverse, accepts_reverse]
ext; simp

Comment on lines +49 to +50
simp only [Accepts, reverse_mTr]
aesop

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

We typically have tried to standardize on using grind over aesop. I note that

Suggested change
simp only [Accepts, reverse_mTr]
aesop
have : na.reverse.start = na.accept := rfl
have : na.reverse.accept = na.start := rfl
grind [Accepts, reverse_mTr]

works. Should the have here be lemmas?

Comment on lines +99 to +100
use by grind
grind [reverse_tr]

@chenson2018 chenson2018 Aug 8, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Both uses of grind here are fragile in that grind? fails. (And for whatever reason linter.tacticAnalysis.verifyGrindOnly won't detect it, which is troubling). If you can turn these into a grind only that works that is fine, otherwise this becomes painful for maintenance.

(@ctchou I suppose I don't have a problem with use here looking at the signature. I had thought previously this would be nicer if Execution were a structure but had trouble with this refactor. If you ever feel like trying this out yourself please feel free.)

@ctchou

ctchou commented Aug 8, 2026

Copy link
Copy Markdown
Collaborator

In view of @chenson2018 's comment above, I just pushed a patch that replaced the first grind by simpa and the second by grind only [...]. It seems that explicitly unpacking the hypothesis h helped grind? find a grind only that actually works.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants