Skip to content

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

Merged
chenson2018 merged 14 commits into
leanprover:mainfrom
Lsonic233:na-reverse
Aug 15, 2026
Merged

feat(Automata): Regular languages are closed under reversal. #775
chenson2018 merged 14 commits into
leanprover:mainfrom
Lsonic233:na-reverse

Conversation

@Lsonic233

Copy link
Copy Markdown
Contributor

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.
Comment thread Cslib/Foundations/Semantics/LTS/Reverse.lean
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
Comment thread Cslib/Foundations/Semantics/LTS/Reverse.lean

@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
Contributor 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?

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.

I don't think it matters much!

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 thread Cslib/Computability/Automata/NA/Reverse.lean Outdated
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?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yeah - I added them as 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.

Comment thread Cslib/Foundations/Semantics/LTS/Reverse.lean Outdated
@Lsonic233
Lsonic233 requested a review from chenson2018 August 12, 2026 17:41
@ctchou

ctchou commented Aug 14, 2026

Copy link
Copy Markdown
Collaborator

Hi @eric-wieser and @chenson2018 Have all your concerns been addressed in this PR? It seems to me ready to merge. Thanks in advance!

public import Cslib.Computability.Automata.NA.Basic
public import Cslib.Foundations.Semantics.LTS.Reverse

/-! # Reversal of nondeterministic automata. -/

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.

Perhaps this should have a few sentences noting that this defines Cslib.Automata.NA.FinAcc.reverse, and that reverse_language_eq proves that the language accepted by this automata is the Language.reverse of the one accepted by the original.

In general, it's handy for module docstring to call out the key results.

Same for the other files.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed - for NA/Reverse.lean and LTS/Reverse.lean.

@eric-wieser eric-wieser 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.

Just minor documentation nits; otherwise this now looks great!

@chenson2018 chenson2018 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.

It looks like Eric's last comments were addressed. Thanks for the contribution!

@chenson2018
chenson2018 added this pull request to the merge queue Aug 15, 2026
Merged via the queue into leanprover:main with commit 2e1824a Aug 15, 2026
2 checks passed
@Lsonic233

Copy link
Copy Markdown
Contributor Author

Thanks everyone!

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