Problem: max_matching

Your task is to implement an algorithm to find the Maximum Bipartite Matching and verify its functional correctness in Lean using Mathlib. Preconditions The input is a Bipartite Graph consisting of two disjoint sets of vertices: Left Set (U) with left_size nodes. Right Set (V) with right_size nodes. The graph is represented as an adjacency list adj (Array of Array Nat) where adj[u] contains a list of neighbors in the Right Set. Requirements: Implement the function max_bipartite_matching which returns the size (number of edges) of the maximum matching. Verification Challenges: (1) Valid Pairing: Proving that the match array maintained by your algorithm correctly maps nodes in the Right Set to their matched partners in the Left Set, and that every recorded match corresponds to a real edge exists in adj. (2) Disjointness: Proving that the algorithm never assigns two different Left-nodes to the same Right-node (and vice-versa). (3) Optimality: Proving that when the algorithm terminates, the matching size is indeed maximal. This usually requires proving Berge's Lemma: a matching is maximum if and only if there is no augmenting path.


import Mathlib

structure MaxMatchingGraph where
  left_size : Nat
  right_size : Nat
  adj : Array (Array Nat)

def MaxMatchingGraph.well_formed (g : MaxMatchingGraph) : Prop :=
  g.adj.size = g.left_size ∧
  ∀ u, u < g.left_size →
    ∀ v, v ∈ g.adj.getD u #[] → v < g.right_size

def MaxMatchingGraph.has_edge (g : MaxMatchingGraph) (u v : Nat) : Prop :=
  u < g.left_size ∧
  v ∈ g.adj.getD u #[]

-- Matching definition
structure Matching where
  edges : List (Nat × Nat)

def Matching.is_valid (m : Matching) (g : MaxMatchingGraph) : Prop :=
  ∀ e, e ∈ m.edges → g.has_edge e.1 e.2

def Matching.is_disjoint (m : Matching) : Prop :=
  ∀ e1 e2, e1 ∈ m.edges → e2 ∈ m.edges → e1 ≠ e2 →
    e1.1 ≠ e2.1 ∧ e1.2 ≠ e2.2

def Matching.size (m : Matching) : Nat :=
  m.edges.length

def is_matching (g : MaxMatchingGraph) (m : Matching) : Prop :=
  m.is_valid g ∧ 
  m.is_disjoint ∧
  m.edges.Nodup

def is_max_matching_size (g : MaxMatchingGraph) (k : Nat) : Prop :=
  (∃ m, is_matching g m ∧ m.size = k) ∧
  (∀ m, is_matching g m → m.size ≤ k)

-- Precondition definitions
@[reducible, simp]
def max_bipartite_matching_precond (graph : MaxMatchingGraph) : Prop :=
  -- !benchmark @start precond
  graph.well_formed ∧ graph.left_size ≤ 1000 ∧ graph.right_size ≤ 1000
  -- !benchmark @end precond

-- !benchmark @start auxcode
-- !benchmark @end auxcode

-- Main function definition
def max_bipartite_matching (graph : MaxMatchingGraph)
    (h_precond : max_bipartite_matching_precond graph) : Nat :=
  -- !benchmark @start code
  sorry
  -- !benchmark @end code

-- Postcondition definitions
@[reducible, simp]
def max_bipartite_matching_postcond (graph : MaxMatchingGraph) (result : Nat)
    (_ : max_bipartite_matching_precond graph) : Prop :=
  -- !benchmark @start postcond
  is_max_matching_size graph result
  -- !benchmark @end postcond

-- !benchmark @start lemma
-- !benchmark @end lemma

-- Proof content
theorem max_bipartite_matching_postcond_satisfied (graph : MaxMatchingGraph)
    (h_precond : max_bipartite_matching_precond graph) :
    max_bipartite_matching_postcond graph
      (max_bipartite_matching graph h_precond) h_precond := by
  -- !benchmark @start proof
  sorry
  -- !benchmark @end proof