Run dune build (ignore the warinings) in this directory to compile the project. It will compile the Rocq code and
generate OCaml code from it (see _RocqProject file).
- Run
dune exec _build/default/executable/schulzecode/main.exeto run the Schulze method on the example used by Markus Schulze in his paper. The output shows the pairwise victory matrix, the strongest path strengths (A*), and the pairwise winners — candidate D is the Condorcet winner. - Run
dune exec _build/default/executable/schulzepathcode/main.exeto run the Schulze language (trace) semiring example. Unlike the max-min value semiring ofSchulze.v,Schulzepath.vbuilds the language semiring of sets of paths (union + pairwise concatenation), where distributivity holds exactly. The output shows the pairwise victory matrix, the strongest beatpath strengthsschulze_star M = (M + I)³, the pairwise winners (candidate D), the closure actionA*·b(list-based vs functional), and verifies the language-semiring witness beatpaths againstschulze_star. - Run
dune exec _build/default/executable/shortestpath/main.exeto run the shortest path code (min-plus semiring). Shows the adjacency matrix, all-pairs shortest distances (A*), and the fixed-point iteration converging from source node A.. - Run
dune exec _build/default/executable/widestpathcode/main.exeto run the widest-shortest path algorithm (lexicographic semiring: length first, then width). Shows adjacency matrix, all-pairs optimal paths (A*), and fixed-point iteration. - Run
dune exec _build/default/executable/wikimedia/main.exeto run the Wikipedia Schulze method example (18-candidate Board election). Shows strongest path strengths and fixed-point iteration converging in 17 steps. - Run
dune exec executable/fivegslicing/main.exeto run the 5G Network Slicing example, which computes optimal routing paths through a 5G core network (UE → gNB → UPF → DN) using a latency × bandwidth product semiring. Each link has two attributes — latency (minimized, min-plus) and bandwidth (maximized, max-min) — and A* finds the Pareto-optimal end-to-end path weights.
We have compiled this project with Rocq 9.0.1 (with rocq-elpi and
Hierarchy Builder); if you want to use it with any other Rocq version, please
let us know.
algorithm/ is the reusable library. The Schulze development used to live in a
single SocialchoiceN.v; each criterion now has its own file over five shared
base files, and SocialchoiceN.v re-exports all of them, so From Semiring Require Import SocialchoiceN. still brings in everything.
Infrastructure
| File | Contents |
|---|---|
Structures.v |
the HB algebraic hierarchy (semiring, bounded, commutative) |
OrelN.v |
the natural order a ≤ b := a + b = b |
MatN.v |
matrices, pow, geom_sum, the Kleene star |
PathN.v |
paths, path measures, and the closure over a candidate list |
SemimoduleN.v |
the semimodule layer and affine fixed points |
OrderSemiring.v, NormalizedOrder.v, ExtendOrder.v |
building a carrier from an order alone |
Schulze, shared base
| File | Contents |
|---|---|
SchulzeDefsN.v |
the five definitions, mat_star, kleene_exp |
SchulzeOrderN.v |
order and semiring algebra over a bounded semiring |
SchulzeClosureN.v |
the closure, powers, transposition, path-measure bounds |
SchulzeBasicsN.v |
order facts about beating and winning |
SchulzeWitnessN.v |
the triangle and four-cycle witness matrices |
One file per criterion, in dependency order: ResolvabilityN,
TransitivityN, WinnerexistenceN, CharacterisationsN, ReversalsymmetryN,
MonotonicityN, ParetoN, CondorcetN, SmithN, PrudenceN, MinMaxN,
NeutralityN, IsolateN. SocialchoiceN.v re-exports these.
Comparing two elections over different alternative sets. Independence of
clones and Smith-IIA both replace or delete an alternative, which changes
|A|, so they use the closure parameterised by a candidate list rather than by
a fixed type.
| File | Contents |
|---|---|
ClosureTransportN.v |
carrying a path from one election to another; used by both |
BeatsOnN.v |
beats_on and winner_on over a candidate list, and their agreement with schulze_beats/schulze_winner at elements |
CloneN.v |
independence of clones (Schulze 4.6) |
CloneCharacterisationN.v |
its converse, and its equivalence with winner existence |
SmithiiaN.v |
Smith-IIA in removal form, on both sides of the cut (Schulze's 4.7.5a and 4.7.6) |
SchulzeOnNT.v discharges the algebraic side conditions once for a concrete
carrier. examples/ instantiates the framework and checks the separating
examples by reflection; extraction/ drives the OCaml targets.
Each of the first three sits beside the criterion it completes; the last three combine both structural guarantees and so belong to neither.
| Theorem | File |
|---|---|
transitivity_characterisation |
TransitivityN.v |
winner_exists_characterisation |
WinnerexistenceN.v |
clone_characterisation, clone_iff_winner_exists |
CloneCharacterisationN.v |
output_well_formed_characterisation |
CharacterisationsN.v |
strict_partial_order_characterisation |
CharacterisationsN.v |
winner_beats_nonwinner_characterisation |
CharacterisationsN.v |
Every numbered result of the ICALP paper is machine-checked. The table below mirrors the paper's status table and adds
the file each theorem lives in. PAPER-MAP.md at the repository root gives
the finer-grained map keyed to the equation numbers of Schulze's own paper.
| Paper result | Rocq name | File |
|---|---|---|
| Asymmetry of the beat relation | schulze_beats_asym |
algorithm/SchulzeBasicsN.v |
| Paths, not walks | reduce_path_into_elem_path_gen |
algorithm/PathN.v |
| Neutrality | neutrality_beats, neutrality_winner |
algorithm/NeutralityN.v |
| Weak Pareto | pareto_weaker, pareto_weaker_winner_transfer |
algorithm/ParetoN.v |
| Monotonicity | monotonicity_beats, winner_monotonicity |
algorithm/MonotonicityN.v |
| Smith criterion | smith_criterion_weaker |
algorithm/SmithN.v |
| Smith-IIA, weak side | smith_iia_removal, smith_iia_winner_set |
algorithm/SmithiiaN.v |
| Smith-IIA, strong side | smith_iia_removal_strong_beats; smith_iia_isolate_strong |
algorithm/SmithiiaN.v; algorithm/IsolateN.v |
| Condorcet consistency | condorcet_implies_strict_winner_weaker |
algorithm/CondorcetN.v |
| Resolution step | untied_winner_is_strict, untied_winner_unique |
algorithm/ResolvabilityN.v |
| Transitivity and winner existence | schulze_trans_weaker_necessary; winner_exists_weaker_necessary |
algorithm/TransitivityN.v; algorithm/WinnerexistenceN.v |
| Independence of clones | independence_of_clones_selective |
algorithm/CloneN.v |
| Prudence | prudence, prudence_not_winner |
algorithm/PrudenceN.v |
| MinMax set | minmax_beats, minmax_winner |
algorithm/MinMaxN.v |
| Reversal symmetry, relation and winner level | reversal_symmetry_O; reversal_symmetry_S_level2 |
algorithm/ReversalsymmetryN.v |
| Commutativity from the meet property | mul_comm_of_meet |
algorithm/SchulzeOrderN.v |
| Structure theorem | structure_add_is_max, structure_mul_is_min |
algorithm/SchulzeOrderN.v |
| The characterisations | see the table above | algorithm/ |
| Tropical three-cycle | tropical_no_winner_at_three |
examples/SharpnessWitness.v |
| Diamond witnesses and counts | diamond_no_winner_at_four, diamond_every_profile_has_winner, diamond_order3_intransitive_count |
examples/SharpnessWitness.v |
| Level-2 tightness (all five criteria) | smith_fails_over_diamond, smith_iia_fails_over_diamond, smith_iia_strong_fails_over_diamond, condorcet_fails_over_diamond, resolution_fails_over_diamond |
examples/SharpnessWitness.v |
| Clone bound: five alternatives optimal | diamond_clone_independence_at_four, clone_characterisation_four_insufficient; beats_on_cycle3_cyclic_triple |
examples/CloneFour.v; algorithm/BeatsOnN.v |
| The worked beatpath example | worked_example_star, worked_example_order, worked_example_winner |
examples/SharpnessWitness.v |
./scripts/audit.sh (after dune build) checks the two claims the papers
make: that no file contains an admitted statement, and that every headline
theorem is closed under the global context. The theorems it checks are listed
in scripts/audit_theorems.txt, one per line, so the list can be extended
without touching the script. GitHub Actions runs dune build and then the
audit on every push (.github/workflows/ci.yml).
If you want to verify that your algebra is a semiring, do the following:
- Define your carrier type
Rtogether with its operations — the zero element, addition, the one element, and multiplication — matching the fields of the HB mixins in algorithm/Structures.v:
Section MySemiring.
Inductive R := ... .
Definition zero : R := ... .
Definition add : R -> R -> R := ... .
Definition one : R := ... .
Definition mul : R -> R -> R := ... .
End MySemiring.
- Prove the semiring laws as lemmas — exactly the proof fields of
IsCommutativeMonoidandIsSemiring:
(* commutative monoid (additive) *)
Lemma addA_proof : forall x y z : R, add (add x y) z = add x (add y z). (* associativity *)
Lemma addC_proof : forall x y : R, add x y = add y x. (* commutativity *)
Lemma add0r_proof : forall x : R, add zero x = x. (* zero: left identity *)
Lemma addr0_proof : forall x : R, add x zero = x. (* zero: right identity *)
(* semiring (multiplicative, distributivity, annihilators) *)
Lemma mulA_proof : forall a b c : R, mul (mul a b) c = mul a (mul b c). (* associativity *)
Lemma mul1r_proof : forall a : R, mul one a = a. (* one: left identity *)
Lemma mulr1_proof : forall a : R, mul a one = a. (* one: right identity *)
Lemma mulDr_proof : forall a b c : R, mul (add a b) c = add (mul a c) (mul b c). (* right distributivity *)
Lemma mulDl_proof : forall a b c : R, mul a (add b c) = add (mul a b) (mul a c). (* left distributivity *)
Lemma mul0r_proof : forall a : R, mul zero a = zero. (* zero: left annihilator *)
Lemma mulr0_proof : forall a : R, mul a zero = zero. (* zero: right annihilator *)
- Register your algebra as an HB instance. Once the
Semiringinstance exists, the generic matrix machinery in algorithm/MatN.v (pow,powN,powN_fun,geom_sum,matrix_mul, …) works forR:
HB.instance Definition _ := IsCommutativeMonoid.Build R
zero add addA_proof addC_proof add0r_proof addr0_proof.
HB.instance Definition _ := IsSemiring.Build R
one mul mulA_proof mul1r_proof mulr1_proof
mulDr_proof mulDl_proof mul0r_proof mulr0_proof.
- The fixed-point and Kleene-closure theorems additionally require the semiring to be bounded:
1 + a = 1. (Idempotencea + a = athen follows automatically — seebounded_add_idemin algorithm/PathN.v.) Prove it and register the instance:
Lemma add_bound_proof : forall a : R, add one a = one.
HB.instance Definition _ := IsBoundedSemiring.Build R add_bound_proof.
- You can now use the generic functions directly, e.g. the closure
powN_fun (matrix_add m (I : Node -> Node -> R)) nand the matrix-vector iterationmatrix_vector_action m vfrom algorithm/SemimoduleN.v. For the full pattern — including theIsFinTypeinstance on your node type and theIsSemimoduleinstance — see the concrete examples in examples:Schulze.v(max-min),Shortestpath.v(min-plus),WidestShortestPath.v(lexicographic length × width), andSchulzepath.v(language/trace semiring).
Some design decisions: we have used function type (A -> B -> C) to model a matrix datatype but this may not be efficient if your matrix size is large. However, we use refinement approach to make it efficient (see powN_eqv).