Coinductive Program Analysis β€” Is Anyone Actually Doing This? HOT 43 Replies

Forum: Static Analysis Views: 1,847 Started by: AbstractKitty Tags: coinduction fixpoints coalgebra abstract-interpretation coq haskell
πŸ“Œ Mod note (Ξ›atticeWhiskers): Thread stickied due to quality discussion. Related: see The Widening Debate and Final Coalgebra Semantics for Fun and Profit.
🐱
Denotational Diva
🐾🐾🐾🐾
Posts: 1,204
Joined: 3y ago
Rep: +892
Ξ½F enjoyer
Coinductive Program Analysis β€” Is Anyone Actually Doing This? #1

Ok so I've been down a rabbit hole (no pun intended) for the past few weeks reading about coalgebraic approaches to program analysis and I genuinely cannot tell if anyone is actually doing this in a practical setting, or if it's purely theoretical fodder. Let me lay out what I mean.

The standard story in abstract interpretation is: you have a collecting semantics defined as the least fixpoint ΞΌF of some monotone operator F on a complete lattice. You approximate it from below, you deal with termination via widening, you cry about precision loss. Fine. We've all been through this. We have a whole dedicated thread about the widening debate.

But here's the thing β€” not all interesting program properties are least fixpoints. Safety properties in particular have a natural reading as greatest fixpoints. If you think of "nothing bad ever happens" as an invariant that must hold at every step of execution, you're actually carving out the largest set of states consistent with some property β€” that's Ξ½F, not ΞΌF.

More broadly: the whole tradition of coalgebra / final coalgebra semantics gives you a completely different way to think about program behaviour. Instead of building up from the bottom with inductive data, you're observing from the outside β€” coinductive codata. Programs-as-coalgebras model state-based behaviour, transitions, streams of outputs. The semantics lives in the final coalgebra of some functor. This is conceptually beautiful but I've never seen it show up in, say, an actual static analyser anyone ships.

So my question: is anyone aware of work that uses coinductive/final coalgebra approaches β€” not just as a theoretical backdrop, but as a practical computational method for program analysis? I'm thinking things like:

  • Greatest-fixpoint-based invariant generation (as opposed to widening toward ΞΌF)
  • Bisimulation-based program equivalence checking
  • Coinductive type systems that feed into analysis
  • Actually computing with final coalgebras / anamorphisms in an analysis tool

References, personal experience, hot takes β€” all welcome. I'm aware this is very niche even for CGPA. 🐱

😸
Tarski Enthusiast
🐾🐾🐾🐾🐾
Posts: 3,441
Joined: 5y ago
Rep: +2,105
ΞΌ and Ξ½ both
Re: Coinductive Program Analysis β€” Is Anyone Actually Doing This? #2

OH. This is the thread I have been waiting for. Sit down everyone.

First: you're completely right about safety vs liveness and greatest fixpoints. The classical Alpern-Schneider characterisation distinguishes safety ("bad thing never happens") from liveness ("good thing eventually happens"). Safety properties are closed sets in the trace topology, and when you unpack what that means computationally, you get greatest fixpoints staring you in the face.

But here's what I find even more interesting β€” the coinductive principle for program analysis is dual to the usual inductive one in a very precise way. Instead of saying "prove your invariant is preserved going forward from an initial state," you're saying "exhibit a set that is a post-fixpoint" β€” a set where if you're in it, you stay in it under the transition function. That's the coinduction proof rule:

Coinduction principle: if X is consistent for F (i.e. X βŠ† F(X)), then X βŠ† Ξ½F.

This is the strongest thing you can prove about infinite behaviour using just a post-fixpoint witness. You're not computing the greatest fixpoint by iterating from ⊀ (which may not terminate) β€” you're exhibiting a set X and showing it's consistent. That's much more amenable to verification-style reasoning.

Now, practical tools: I am aware that model checkers like NuSMV and some variants of μ-calculus model checking absolutely do compute νF via backward iteration from ⊀ with narrowing (the dual of widening). The question is whether you lose as much precision as you do going forward with widening. My intuition says... the dual problem applies but maybe differently shaped? @GaloisCat would know more.

🐈
Haskell Witch
🐾🐾🐾
Posts: 874
Joined: 2y ago
Rep: +561
codata all the way down
Re: Coinductive Program Analysis β€” Is Anyone Actually Doing This? #3

I am already writing Haskell. You cannot stop me.

Okay but actually, Haskell's lazy evaluation already gives you coinductive data "for free" in a certain sense β€” infinite lists, streams, cyclic structures are all natural. The key insight that applies to analysis is that codata types are greatest fixpoints:

-- Inductive (least fixpoint): finite trees
data Tree a = Leaf | Node a (Tree a) (Tree a)

-- Coinductive (greatest fixpoint): potentially infinite trees (codata)
data CoTree a = CoNode a (CoTree a) (CoTree a)
-- In Haskell this "just works" because laziness

-- A simple abstract domain for an analyser:
-- Instead of iterating UP to a fixpoint, we iterate DOWN from Top
type Narrowing a = Stream a  -- a (potentially infinite) descending chain

newtype Stream a = Cons { runStream :: (a, Stream a) }
-- this IS a final coalgebra for the functor F(X) = A Γ— X

The point is: when you want to compute the greatest fixpoint of an abstract transformer F, you iterate F from ⊀ downward: ⊀, F(⊀), FΒ²(⊀), ... This is a descending chain. If the lattice has finite depth (DCC), it stabilises. If not, you need narrowing β€” the dual of widening.

I'm currently hacking on a tiny interval analyser that tries to compute the greatest fixpoint of the "stay in bounds" transformer rather than widening toward the least. Early results: precision is actually better for safety-style queries. Obviously doesn't terminate without narrowing for loops though. More results incoming once I stop yak-shaving the typeclass hierarchy...

😼
Connection Skeptic
🐾🐾🐾🐾🐾
Posts: 5,892
Joined: 7y ago
Rep: +4,231
α ⊣ γ forever
Re: Coinductive Program Analysis β€” Is Anyone Actually Doing This? #4

I'm going to push back slightly on the framing here, not because I think coinductive analysis is uninteresting, but because I want to make sure we're being precise about what "coinductive" actually buys you computationally vs. just being a different presentation of the same fixpoint computation.

The standard Galois connection framework can handle both ΞΌF and Ξ½F. Cousot & Cousot's 1979 paper already discusses greatest fixpoints in the context of safety. The question isn't really "inductive vs. coinductive" β€” it's about which fixpoint you want, and what your abstract domain approximates it.

AbstractKitty wrote:
Actually computing with final coalgebras / anamorphisms in an analysis tool

This is the part where I want to see evidence. An anamorphism is just an unfold β€” it's the unique coalgebra homomorphism into the final coalgebra. The question is whether framing things this way gives you a better implementation strategy or better compositional structure, not whether it "works" in principle. In principle, everything is just a fixpoint iteration.

That said β€” bisimulation-based equivalence checking is a real thing and does use the coinductive structure non-trivially. Partition refinement algorithms (Paige-Tarjan, etc.) are essentially computing the greatest fixpoint of a bisimulation relation. The coinductive framing there is genuinely illuminating because it tells you you're looking for the largest relation with the bisimulation property, not building up evidence inductively.

So: interested but skeptical. Convince me there's something you can do with the coalgebraic framing that you can't do (or can't easily explain) without it.

🐯
Completeness Junkie
🐾🐾
Posts: 312
Joined: 1y ago
Rep: +187
still reading Davey&Priestley
Re: Coinductive Program Analysis β€” Is Anyone Actually Doing This? #5

Sorry to be the obvious question person but β€” can someone give a really concrete example of what a coinductive analysis would look like on an actual small program? Like, an actual piece of code with actual abstract states? I have a decent grip on the lattice theory but the coalgebra framing is still fuzzy for me.

I understand that bisimilarity is defined as the greatest fixpoint of some operator on binary relations. But I don't see how that directly maps to, e.g., "analyse this C function for buffer overruns."

🐱
Denotational Diva
🐾🐾🐾🐾
Posts: 1,204
Joined: 3y ago
Rep: +892
Ξ½F enjoyer
Re: Coinductive Program Analysis β€” Is Anyone Actually Doing This? #6
LatticeTabby wrote:
can someone give a really concrete example of what a coinductive analysis would look like on an actual small program?

Good question! Let me try. Consider a simple loop: while (x > 0) { x = x - 1; }

Standard abstract interpretation asks: what is the least invariant holding at the loop head? You iterate the abstract transformer from βŠ₯ upward. With intervals you'd eventually get something like [0, x_init].

The coinductive/greatest-fixpoint approach asks instead: what is the largest set of states from which the safety property "x stays non-negative" holds forever? You start from ⊀ (all states) and intersect backwards. For this example:

-- Abstract transformer post(S) = { x - 1 | x ∈ S, x > 0 } βˆͺ { x | x ≀ 0 }
-- Greatest fixpoint of the "safety" operator T(X) = X ∩ pre(X)
-- where pre(X) = { s | post(s) βŠ† X }

T(⊀)   = { x | x β‰₯ 0 }           -- ⊀ intersected with "stays safe from ⊀"
TΒ²(⊀)  = { x | x β‰₯ 0 }           -- already stabilised!
-- Ξ½T = { x | x β‰₯ 0 }  βœ“ the exact invariant, no widening needed

In this simple case the greatest fixpoint gives you the exact invariant immediately because the lattice is well-founded enough. The interesting (hard) cases are with complex loops where the descending chain from ⊀ doesn't stabilise β€” and that's where narrowing comes in. Narrowing is the dual of widening: instead of making sets bigger to force stabilisation, you make them smaller.

The coalgebra framing makes this natural: your program is a coalgebra c : S β†’ F(S) (a transition structure). The safety property is a subset P βŠ† S that is a sub-coalgebra β€” i.e., it maps to F(P) βŠ† F(S). Finding the largest such P is finding the final object among all "safe" sub-coalgebras, i.e., the greatest fixpoint. @GaloisCat this is what the framing buys you: the compositional structure on sub-coalgebras is exactly the right structure to reason about safety modularly.

😼
Connection Skeptic
🐾🐾🐾🐾🐾
Posts: 5,893
Joined: 7y ago
Rep: +4,231
α ⊣ γ forever
Re: Coinductive Program Analysis β€” Is Anyone Actually Doing This? #7
AbstractKitty wrote:
the compositional structure on sub-coalgebras is exactly the right structure to reason about safety modularly.

Okay, this is a fair point and I'll concede ground here. The modularity argument is real. If you have a coalgebra morphism f : (S, c) β†’ (S', c'), then safety properties (sub-coalgebras) pull back along f. This is not obvious at all in the "just iterate your abstract transformer" framing β€” you'd have to argue separately about how your abstractions compose.

But here's my remaining concern: the concrete computation is still fixed-point iteration, just starting from ⊀ instead of βŠ₯. The coalgebraic framing provides beautiful semantic foundations and helps you understand what you're computing. But does it suggest different algorithms? Does it tell you anything new about convergence or precision?

I'll grant that for bisimulation-based analysis, the coinductive framing absolutely changes the algorithm. Partition refinement doesn't have a natural "just iterate from βŠ₯" dual. The coinductive structure there is computationally essential, not just semantically convenient.

Grudgingly warming up to this. Don't tell anyone.

🐾
Star Operator
🐾🐾🐾
Posts: 641
Joined: 2y ago
Rep: +430
automata theory nerd
Re: Coinductive Program Analysis β€” Is Anyone Actually Doing This? #8

Coming in from the automata theory angle: the entire framework of language equivalence checking via bisimulation is a direct practical application of coinduction. When you want to check if two NFAs accept the same language, the coinductive approach is to look for a bisimulation relation between their states.

There's beautiful work by Bonchi & Pous ("Checking NFA equivalence with bisimulations up to congruence", POPL 2013) on making this efficient via up-to techniques β€” enhancements of the coinduction proof method where instead of finding a full bisimulation you find a weaker relation that is nonetheless "contained in" bisimilarity. This is way faster in practice.

The up-to framing is interesting for program analysis too: rather than computing the full greatest fixpoint (which might be expensive), you compute a relation that's "almost" a bisimulation and use the up-to machinery to certify it. There's a nice categorical treatment via the "companion" β€” the largest compatible function, which subsumes most useful up-to techniques.

Does this count as "practical"? I think checking NFA equivalence is program analysis in some sense β€” you're checking if two regex-like patterns describe the same behaviour. And it ships in tools.

😸
Tarski Enthusiast
🐾🐾🐾🐾🐾
Posts: 3,442
Joined: 5y ago
Rep: +2,105
ΞΌ and Ξ½ both
Re: Coinductive Program Analysis β€” Is Anyone Actually Doing This? #9

Let me bring in the proof assistant angle since nobody's mentioned it yet. Coinduction in Coq and Agda is very much a practical thing, even if it's painful.

In Coq, co-fixpoints (CoFixpoint) let you define corecursive functions over coinductive types (like streams). The guardedness condition is Coq's way of ensuring productivity β€” that every observation terminates. Agda uses sized types for finer control of productivity. When you want to prove a safety property of an infinite computation in Coq, you almost certainly need a coinductive argument.

Here's the relevant thing for program analysis: if you want to verify that a reactive system (say, a control loop that runs forever) satisfies some invariant, the natural proof in Coq is coinductive. You define the property as a coinductive predicate, exhibit a witness, and apply the coinduction principle. This is mechanised program analysis at the proof-assistant level.

There's a particularly elegant construction I keep coming back to: parameterized coinduction (Hur et al., POPL 2013). The idea is to support compositional, incremental coinductive proofs using a "parameterized greatest fixpoint" construction. The lattice-theoretic foundations are clean and it's been mechanised in Coq. If you're building verified static analysers in a proof assistant, this is the kind of thing you'd reach for.

I'll link: Coinduction Reference List β€” I've been maintaining this. Har et al. is on there, Sangiorgi's book, the Pous-Sangiorgi historical perspective.

🐈
Haskell Witch
🐾🐾🐾
Posts: 875
Joined: 2y ago
Rep: +561
codata all the way down
Re: Coinductive Program Analysis β€” Is Anyone Actually Doing This? #10

Update from the Haskell mines: I've got a minimal working example. Here's a coinductive interval analysis that computes the greatest fixpoint of a "stays safe" predicate for a simple counter program:

module CoinductiveInterval where

import Data.Maybe (fromMaybe)

-- Abstract domain: closed intervals (possibly empty)
data Interval = Empty | Iv Int Int  -- [lo, hi]
  deriving (Show, Eq)

-- Meet (intersection) β€” key operation for greatest fixpoint iteration
meet :: Interval -> Interval -> Interval
meet Empty _ = Empty
meet _ Empty = Empty
meet (Iv a b) (Iv c d) =
  let lo = max a c; hi = min b d
  in if lo > hi then Empty else Iv lo hi

-- "Pre-image" of safety property P under transition x -> x - 1
-- pre(P) = { x > 0 | x-1 ∈ P } βˆͺ { x ≀ 0 }
preSafe :: Interval -> Interval
preSafe (Iv lo hi) = Iv (lo + 1) (hi + 1)  -- shift by 1 for decrement
preSafe Empty     = Empty

-- Greatest fixpoint iteration: start from Top, apply T(X) = X ∩ pre(X)
-- This descends to the largest safe invariant
greatestFixpoint :: Interval -> [Interval]
greatestFixpoint top = iterate step top
  where step x = meet x (preSafe x)

-- Ξ½T for our example: [0, MAX_INT]  (non-negative reals stay non-negative)

Obviously this is a toy, but the structure is real. The iterate here gives you the descending chain β€” it's lazy (coinductive!) so you don't need to know in advance where it stabilises. You can check equality between consecutive elements to detect the fixpoint.

The thing I find elegant: the descending chain from ⊀ is a codata structure β€” it's potentially infinite, and you consume it lazily. The analysis itself is coinductive in the implementation sense, not just the semantic sense.

🐯
Completeness Junkie
🐾🐾
Posts: 313
Joined: 1y ago
Rep: +187
still reading Davey&Priestley
Re: Coinductive Program Analysis β€” Is Anyone Actually Doing This? #11
AbstractKitty wrote:
The coinductive framing makes this natural: your program is a coalgebra c : S β†’ F(S)

Ok I think I'm starting to get it. So the functor F determines the "type" of transitions? For a deterministic program F(S) = S (just a next state). For a nondeterministic program F(S) = 𝒫(S) (a set of possible next states). For a probabilistic program F(S) = π’Ÿ(S) (a distribution). And the final coalgebra in each case captures the "full observable behaviour" of any system of that type?

And then a Galois connection between the concrete domain (coalgebras over β„˜(S)) and an abstract domain is just... abstract interpretation but for coalgebras? Is that the right mental model?

Sorry if this is basic, I'm genuinely trying to build intuition here.

🐱
Denotational Diva
🐾🐾🐾🐾
Posts: 1,205
Joined: 3y ago
Rep: +892
Ξ½F enjoyer
Re: Coinductive Program Analysis β€” Is Anyone Actually Doing This? #12
LatticeTabby wrote:
Is that the right mental model?

Yes, exactly right! You've got it. The functor F encodes the "shape" of the transition step. Different choices of F give you different classes of systems:

  • F(X) = X → deterministic transition systems (plain coalgebras)
  • F(X) = 𝒫(X) → nondeterministic (powerset coalgebras)
  • F(X) = A Γ— X → streams / Mealy machines outputting from alphabet A
  • F(X) = π’Ÿ(X) → Markov chains (probabilistic)

And yes β€” abstract interpretation over coalgebras is a real research direction. There's work on "coalgebraic abstract interpretation" where you define a Galois connection between the category of F-coalgebras (or some slice of it) and an abstract domain. The advantage is that you get correctness of the abstract analysis "for free" from the structure of the Galois connection, exactly as in Cousot-style abstract interpretation, but now uniformly over all system types determined by F.

This is not just theory. The ΞΌ-calculus model checker design is essentially this: you have a coalgebra (your Kripke structure), you have properties expressed in the ΞΌ-calculus (alternating least/greatest fixpoints), and the model checking algorithm is computing those fixpoints β€” using the coalgebra structure to decide what states to explore.

🌸
Fold Evangelist
🐾🐾🐾
Posts: 719
Joined: 3y ago
Rep: +398
unfolds > folds, fight me
Re: Coinductive Program Analysis β€” Is Anyone Actually Doing This? #13

Can I bring up coinductive type systems specifically? Because I think this is an underexplored connection to program analysis.

In type theory, a coinductive type is exactly the carrier of a final coalgebra. If you have a coinductive type for, say, "infinite streams of integers," the type checker is essentially verifying that your corecursive definitions are productive β€” that every observation terminates. That's a form of guardedness analysis.

Now, guardedness checking is a program analysis! You're statically verifying a property (productivity/totality) of potentially infinite computation. The criterion used by Agda (and discussed in various papers) is a syntactic/semantic approximation of the coinductive property you care about. Sized types (Abel & Pientka) give you a more precise, type-theoretic handle on this.

So coinductive type systems β†’ coinductive program analysis β†’ actual practical tool (Agda). QED? 🐱

Also worth mentioning: the type theory / program analysis duality runs deep. A coinductive type system for a language where programs can diverge is essentially embedding liveness analysis into the type system. Whether a term has a "coinductive" type tells you something about its infinite behaviour. I keep meaning to write a post about this on the CGPA blog.

😼
Connection Skeptic
🐾🐾🐾🐾🐾
Posts: 5,894
Joined: 7y ago
Rep: +4,232
α ⊣ γ forever
Re: Coinductive Program Analysis β€” Is Anyone Actually Doing This? #14

Fine. Fine! I'm convinced this is a real research area with real practical applications. Let me summarise what I think the thread has established so far:

Definitely practical, coinductive structure is essential:

  • Bisimulation-based equivalence checking (NFA equivalence, process equivalence) β€” partition refinement is computing Ξ½F
  • ΞΌ-calculus model checking β€” explicitly alternates ΞΌ and Ξ½ iterations
  • Guardedness/productivity checking in Agda/Coq β€” coinductive type systems

Practical but coinductive framing is "optional" (semantically illuminating, not computationally essential):

  • Safety invariant analysis via greatest fixpoint / backward iteration from ⊀
  • Narrowing-based abstract interpretation

Promising but I want to see it ship:

  • Coalgebraic abstract interpretation as a unified framework
  • Anamorphism-based analysis algorithms
  • MeownadTransformer's interval analyser

Good thread. AbstractKitty you owe me a reference list.

😸
Tarski Enthusiast
🐾🐾🐾🐾🐾
Posts: 3,443
Joined: 5y ago
Rep: +2,106
ΞΌ and Ξ½ both
Re: Coinductive Program Analysis β€” Is Anyone Actually Doing This? #15

One more thing I want to throw in before this page fills up: the connection between coinductive analysis and the parity/modal ΞΌ-calculus.

The modal ΞΌ-calculus lets you express both reachability (ΞΌ, least fixpoint) and safety/invariance (Ξ½, greatest fixpoint) properties, and crucially also their alternations. Liveness properties often require mixed ΞΌ/Ξ½ formulas: "something good happens infinitely often" is Ξ½ΞΌ, and "something bad eventually stops" is ΞΌΞ½. The parity game interpretation of ΞΌ-calculus model checking is literally about which player wins games involving alternating fixpoints.

This is 100% shipped software β€” SPIN, nuXmv, mCRL2 all do this. The coinductive iterations are not hidden or theoretical; they're first-class in the algorithms.

So the answer to OP's question "is anyone doing this" is: yes, every model checker that handles liveness properties is doing it. The question is whether the abstract interpretation community has fully absorbed the coinductive framing, or whether it's still seen as a "model checking thing." I'd argue the abstraction barrier between these communities is the main reason coinductive analysis feels exotic when discussed as abstract interpretation.

Someone should write a bridge paper. @AbstractKitty?? πŸ‘€

✏️ Post a Reply