lean4-htt/tests/lean/run/mutual_coinduction.lean
Wojciech Rozowski 2d52d44710
feat: fixpoint_induct and partial_correctness lemmas for mutual blocks come in conjunction and projected variants (#9651)
This PR modifies the generation of induction and partial correctness
lemmas for `mutual` blocks defined via `partial_fixpoint`. Additionally,
the generation of lattice-theoretic induction principles of functions
via `mutual` blocks is modified for consistency with `partial_fixpoint`.

The lemmas now come in two variants:
1. A conjunction variant that combines conclusions for all elements of
the mutual block. This is generated only for the first function inside
of the mutual block.
2. Projected variants for each function separately

## Example 1
```lean4
axiom A : Type
axiom B : Type

axiom A.toB : A → B
axiom B.toA : B → A

mutual
noncomputable def f : A := g.toA
partial_fixpoint
noncomputable def g : B := f.toB
partial_fixpoint
end
```

Generated `fixpoint_induct` lemmas:
```lean4
f.fixpoint_induct (motive_1 : A → Prop) (motive_2 : B → Prop) (adm_1 : admissible motive_1)
  (adm_2 : admissible motive_2) (h_1 : ∀ (g : B), motive_2 g → motive_1 g.toA)
  (h_2 : ∀ (f : A), motive_1 f → motive_2 f.toB) : motive_1 f

g.fixpoint_induct (motive_1 : A → Prop) (motive_2 : B → Prop) (adm_1 : admissible motive_1)
  (adm_2 : admissible motive_2) (h_1 : ∀ (g : B), motive_2 g → motive_1 g.toA)
  (h_2 : ∀ (f : A), motive_1 f → motive_2 f.toB) : motive_2 g
```

Mutual (conjunction) variant:
```lean4
f.mutual_fixpoint_induct (motive_1 : A → Prop) (motive_2 : B → Prop) (adm_1 : admissible motive_1) (adm_2 : admissible motive_2)
  (h_1 : ∀ (g : B), motive_2 g → motive_1 g.toA) (h_2 : ∀ (f : A), motive_1 f → motive_2 f.toB) :
  motive_1 f ∧ motive_2 g
```

## Example 2 
```lean4
mutual
  def f (n : Nat) : Option Nat :=
    g (n + 1)
  partial_fixpoint

  def g (n : Nat) : Option Nat :=
    if n = 0 then .none else f (n + 1)
  partial_fixpoint
end
```
Generated `partial_correctness` lemmas (in a projected variant):
```lean4
f.partial_correctness (motive_1 motive_2 : Nat → Nat → Prop)
  (h_1 :
    ∀ (g : Nat → Option Nat),
      (∀ (n r : Nat), g n = some r → motive_2 n r) → ∀ (n r : Nat), g (n + 1) = some r → motive_1 n r)
  (h_2 :
    ∀ (f : Nat → Option Nat),
      (∀ (n r : Nat), f n = some r → motive_1 n r) →
        ∀ (n r : Nat), (if n = 0 then none else f (n + 1)) = some r → motive_2 n r)
  (n r✝ : Nat) : f n = some r✝ → motive_1 n r✝

g.partial_correctness (motive_1 motive_2 : Nat → Nat → Prop)
  (h_1 :
    ∀ (g : Nat → Option Nat),
      (∀ (n r : Nat), g n = some r → motive_2 n r) → ∀ (n r : Nat), g (n + 1) = some r → motive_1 n r)
  (h_2 :
    ∀ (f : Nat → Option Nat),
      (∀ (n r : Nat), f n = some r → motive_1 n r) →
        ∀ (n r : Nat), (if n = 0 then none else f (n + 1)) = some r → motive_2 n r)
  (n r✝ : Nat) : g n = some r✝ → motive_2 n r✝
```

Mutual (conjunction) variant:
```
f.mutual_partial_correctness (motive_1 motive_2 : Nat → Nat → Prop)
  (h_1 :
    ∀ (g : Nat → Option Nat),
      (∀ (n r : Nat), g n = some r → motive_2 n r) → ∀ (n r : Nat), g (n + 1) = some r → motive_1 n r)
  (h_2 :
    ∀ (f : Nat → Option Nat),
      (∀ (n r : Nat), f n = some r → motive_1 n r) →
        ∀ (n r : Nat), (if n = 0 then none else f (n + 1)) = some r → motive_2 n r) :
  (∀ (n r : Nat), f n = some r → motive_1 n r) ∧ ∀ (n r : Nat), g n = some r → motive_2 n r
```
2025-08-18 15:26:30 +00:00

123 lines
3.7 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

namespace MutualCoinduction
mutual
def f : Prop :=
g
coinductive_fixpoint
def g : Prop :=
f
coinductive_fixpoint
end
/--
info: MutualCoinduction.f.coinduct (pred_1 pred_2 : Prop) (hyp_1 : pred_1 → pred_2) (hyp_2 : pred_2 → pred_1) : pred_1 → f
-/
#guard_msgs in
#check MutualCoinduction.f.coinduct
/--
info: MutualCoinduction.f.mutual_induct (pred_1 pred_2 : Prop) (hyp_1 : pred_1 → pred_2) (hyp_2 : pred_2 → pred_1) :
(pred_1 → f) ∧ (pred_2 → g)
-/
#guard_msgs in
#check MutualCoinduction.f.mutual_induct
/--
info: MutualCoinduction.g.coinduct (pred_1 pred_2 : Prop) (hyp_1 : pred_1 → pred_2) (hyp_2 : pred_2 → pred_1) : pred_2 → g
-/
#guard_msgs in
#check MutualCoinduction.g.coinduct
end MutualCoinduction
namespace MutualInduction
mutual
def f : Prop :=
g
inductive_fixpoint
def g : Prop :=
f
inductive_fixpoint
end
/--
info: MutualInduction.f.induct (pred_1 pred_2 : Prop) (hyp_1 : pred_2 → pred_1) (hyp_2 : pred_1 → pred_2) : f → pred_1
-/
#guard_msgs in
#check MutualInduction.f.induct
/--
info: MutualInduction.f.mutual_induct (pred_1 pred_2 : Prop) (hyp_1 : pred_2 → pred_1) (hyp_2 : pred_1 → pred_2) :
(f → pred_1) ∧ (g → pred_2)
-/
#guard_msgs in
#check MutualInduction.f.mutual_induct
/--
info: MutualInduction.g.induct (pred_1 pred_2 : Prop) (hyp_1 : pred_2 → pred_1) (hyp_2 : pred_1 → pred_2) : g → pred_2
-/
#guard_msgs in
#check MutualInduction.g.induct
end MutualInduction
namespace MixedInductionCoinduction
mutual
def f : Prop :=
g → f
inductive_fixpoint
def g : Prop :=
f → g
coinductive_fixpoint
end
/--
info: MixedInductionCoinduction.f.induct (pred_1 pred_2 : Prop) (hyp_1 : (pred_2 → pred_1) → pred_1)
(hyp_2 : pred_2 → pred_1 → pred_2) : f → pred_1
-/
#guard_msgs in
#check f.induct
/--
info: MixedInductionCoinduction.f.mutual_induct (pred_1 pred_2 : Prop) (hyp_1 : (pred_2 → pred_1) → pred_1)
(hyp_2 : pred_2 → pred_1 → pred_2) : (f → pred_1) ∧ (pred_2 → g)
-/
#guard_msgs in
#check f.mutual_induct
/--
info: MixedInductionCoinduction.g.coinduct (pred_1 pred_2 : Prop) (hyp_1 : (pred_2 → pred_1) → pred_1)
(hyp_2 : pred_2 → pred_1 → pred_2) : pred_2 → g
-/
#guard_msgs in
#check g.coinduct
end MixedInductionCoinduction
namespace DifferentPredicateTypes
mutual
def f (n : Nat) : Prop :=
g (n+1) (n+2)
coinductive_fixpoint
def g (n m : Nat): Prop :=
f (n + 2) g (m + 1) m
coinductive_fixpoint
end
/--
info: DifferentPredicateTypes.f.coinduct (pred_1 : Nat → Prop) (pred_2 : Nat → Nat → Prop)
(hyp_1 : ∀ (x : Nat), pred_1 x → pred_2 (x + 1) (x + 2))
(hyp_2 : ∀ (x x_1 : Nat), pred_2 x x_1 → pred_1 (x + 2) pred_2 (x_1 + 1) x_1) (x✝ : Nat) : pred_1 x✝ → f x✝
-/
#guard_msgs in
#check f.coinduct
/--
info: DifferentPredicateTypes.f.mutual_induct (pred_1 : Nat → Prop) (pred_2 : Nat → Nat → Prop)
(hyp_1 : ∀ (x : Nat), pred_1 x → pred_2 (x + 1) (x + 2))
(hyp_2 : ∀ (x x_1 : Nat), pred_2 x x_1 → pred_1 (x + 2) pred_2 (x_1 + 1) x_1) :
(∀ (x : Nat), pred_1 x → f x) ∧ ∀ (x x_1 : Nat), pred_2 x x_1 → g x x_1
-/
#guard_msgs in
#check f.mutual_induct
/--
info: DifferentPredicateTypes.g.coinduct (pred_1 : Nat → Prop) (pred_2 : Nat → Nat → Prop)
(hyp_1 : ∀ (x : Nat), pred_1 x → pred_2 (x + 1) (x + 2))
(hyp_2 : ∀ (x x_1 : Nat), pred_2 x x_1 → pred_1 (x + 2) pred_2 (x_1 + 1) x_1) (x✝ x✝¹ : Nat) :
pred_2 x✝ x✝¹ → g x✝ x✝¹
-/
#guard_msgs in
#check g.coinduct
end DifferentPredicateTypes