Zulip Chat Archive

Stream: iris-lean

Topic: Rationals needed for the Frac CMRA?


Shreyas Srinivas (Jun 22 2025 at 21:29):

Based on my reading of the Frac CMRA, we need rational numbers to define it. Currently Rationals are defined in batteries. Should we include batteries as a dependency?

Shreyas Srinivas (Jun 22 2025 at 21:41):

CC : @Mario Carneiro and @Markus de Medeiros

Shreyas Srinivas (Jun 22 2025 at 21:42):

When we used iris in my semantics course we used it with stdpp and batteries serves a similar role in lean (but has some content differences), so hopefully it isn't a controversial addition

Markus de Medeiros (Jun 22 2025 at 21:45):

To port this file you don't actually need the rationals! This is a case where we can generalize the existing Iris implementation to require a typeclass with the properties we want, rather than the Rational type itself (namely, a type of positive numbers). I'll push my implementation of Frac in an hour or so.

Markus de Medeiros (Jun 22 2025 at 21:46):

Adding batteries is a bit of an orthogonal issue to this (though I'm definitely not be opposed to adding it when the need arises!)

Shreyas Srinivas (Jun 22 2025 at 21:46):

Ah okay. I actually want to get a hang of CMRAs (I only learnt the simplified resource algebra story in my course)

Mario Carneiro (Jun 22 2025 at 21:47):

presumably a minimalistic type which serves the role here would actually be dyadic rationals

Shreyas Srinivas (Jun 22 2025 at 21:47):

So I think I can try the Frac CMRA or some variant, just as a practice. Feel free to close the PR if you prefer it

Markus de Medeiros (Jun 22 2025 at 21:47):

It would be better to use a typeclass. I am sure that eventually I will want to instantiate this with the positive reals.

Shreyas Srinivas (Jun 22 2025 at 23:40):

what exactly is a Leibniz structure?

Shreyas Srinivas (Jun 22 2025 at 23:40):

conceptually?

Mario Carneiro (Jun 22 2025 at 23:40):

Leibniz equality is Rocq-speak for Eq

Mario Carneiro (Jun 22 2025 at 23:40):

I'm quite dubious about using the terminology in iris-lean since no one uses it here

Mario Carneiro (Jun 22 2025 at 23:41):

A Leibniz OFE is one where the Equiv relation is Eq

Shreyas Srinivas (Jun 22 2025 at 23:45):

some possibly mixed up questions : Equivalence is in Init. Why isn't Equivalence Eq defined in core?

Shreyas Srinivas (Jun 22 2025 at 23:45):

and why do we use this extra indirection in Iris at the moment?

Mario Carneiro (Jun 22 2025 at 23:45):

what indirection?

Shreyas Srinivas (Jun 22 2025 at 23:46):

structure LeibnizO (T : Type _) where
  car : T

-- Move?
theorem Eq_Equivalence {T : Type _} : Equivalence (@Eq T) :=
  congrFun rfl, (Eq.symm ·), (·  ·)

instance : COFE (LeibnizO T) := COFE.ofDiscrete _ Eq_Equivalence

Mario Carneiro (Jun 22 2025 at 23:46):

LeibnizO is a type constructor which builds a COFE on an arbitrary type, which is also a leibniz OFE

Mario Carneiro (Jun 22 2025 at 23:47):

You don't want that structure on every type, it's like the discrete topology

Markus de Medeiros (Jun 22 2025 at 23:51):

Are there any rules about when we should use indirection? I brought up the fact that we put the discrete function CMRA on every function type but I don't think that point was ever resolved.

Mario Carneiro (Jun 22 2025 at 23:52):

The key question is whether there is a likelihood of conflict between different structures

Mario Carneiro (Jun 22 2025 at 23:53):

For function types, it's fine as long as you can argue that there will never be a different structure on function types (only wrapped function types)

Mario Carneiro (Jun 22 2025 at 23:54):

for LeibnizO this is clearly impossible to argue because it would be a blanket instance covering every type

Markus de Medeiros (Jun 22 2025 at 23:55):

Mario Carneiro said:

only wrapped function types

Ah--good point. I'm not sure I've ever seen a function type in Iris that wasn't wrapped into a discrete function with -d>.

Markus de Medeiros (Jun 22 2025 at 23:55):

So it might as well be the default

Mario Carneiro (Jun 22 2025 at 23:55):

I believe there is no competing instance for function types anyway; -n> isn't a function type, it is a subtype of a function type

Shreyas Srinivas (Jun 23 2025 at 00:00):

Also @Markus : there seems to be no clean way to map arbitrary typeclass instances from Fractional to Frac

Shreyas Srinivas (Jun 23 2025 at 00:00):

Referring to this part:

abbrev Frac (T : Type _) [Fractional T] := LeibnizO T

-- TODO: How do I get rid of these instances?
-- I definitely do not want to use an abbrev for Frac (there are many different OFE's for numerical types)
-- so do we need to keep around all of these coercions?
instance [Fractional T] : Coe (Frac T) T := (·.1)
instance [Fractional T] : Coe T (Frac T) := (⟨·⟩)
@[simp] instance [Fractional T] : COFE (Frac T) := by unfold Frac; infer_instance
@[simp] instance [Fractional T] : One (Frac T) := ⟨⟨One.one⟩⟩
@[simp] instance [Fractional T] : LE (Frac T) := fun x y => x.1  y.1
@[simp] instance [Fractional T] : LT (Frac T) := fun x y => x.1 < y.1
@[simp] instance [Fractional T] : Add (Frac T) := fun x y => x.1 + y.1

Shreyas Srinivas (Jun 23 2025 at 00:36):

I made a draft PR to your PR at leanprover-community/iris-lean#57

Shreyas Srinivas (Jun 23 2025 at 00:36):

I want to simplify this further tomorrow morning

Shreyas Srinivas (Jun 23 2025 at 00:37):

For example, I think the positivity condition is pointless

Shreyas Srinivas (Jun 23 2025 at 00:38):

Since we are focussed on positive numbers only, we could add define the four conditions of Fractional in simpler terms. However changes here will break proofs, so I will fix this after some sleep

Markus de Medeiros (Jun 23 2025 at 00:46):

Nice! Yeah feel free to play around with it, I didn't simplify the Fractional typeclass at all :)

Markus de Medeiros (Jun 23 2025 at 00:47):

I do think positivity will be hard to remove (the type in Rocq is actually the positive rationals in case you missed it!)

Shreyas Srinivas (Jun 23 2025 at 00:54):

To clarify I mean we can take positivity simpler

Shreyas Srinivas (Jun 23 2025 at 16:30):

Trying to simplify positivity has had the side effect of creating a lot of useful API lemmas for the Fractional class

Markus de Medeiros (Jun 23 2025 at 16:50):

Your definition looks nice! Is it minimal?

Shreyas Srinivas (Jun 23 2025 at 16:52):

I don't have a proof of that. I am trying to codify rationals as totally ordered commutative monoids (ignoring multiplication).

Shreyas Srinivas (Jun 23 2025 at 16:52):

I am basically remembering Apostol's definition of ordered fields in his real analysis book and simply removing the multiplication axioms. I don't have a copy so I have to go with my memory

Markus de Medeiros (Jun 23 2025 at 16:54):

Okay! We don't need a proof or anything, just want to be reasonably confident that this typeclass is as minimal as possible.

Shreyas Srinivas (Jun 23 2025 at 16:54):

I have marked a couple of the remaining fields for removal after I prove them as theorems

Shreyas Srinivas (Jun 23 2025 at 16:55):

Oh and I miss quite a few tactics I take for granted in mathlib like contrapose, apply .. at, nth_rw, and simp_rw

Markus de Medeiros (Jun 23 2025 at 16:57):

I pushed iris-lean#59, we'll want Fractional to be the smallest typeclass such that everything in DFractional can also be proved.

Markus de Medeiros (Jun 23 2025 at 16:58):

Uh also, I think you should not use Zero. This cannot be instantiated for types that are intrinsically positive like the positive reals or positive rationals.

Shreyas Srinivas (Jun 23 2025 at 16:59):

Okay, but then the class that comes out of it will not be a monoid

Markus de Medeiros (Jun 23 2025 at 16:59):

That's fine

Shreyas Srinivas (Jun 23 2025 at 16:59):

I also miss all those precious code actions that split cases and create proof holes

Shreyas Srinivas (Jun 23 2025 at 17:06):

Everywhere you see me using the conv mode, I would have used nth_rw instead.

Shreyas Srinivas (Jun 23 2025 at 17:20):

Btw the Zero class only adds the notation for zero. For various intervals of Q I think the correct way to do this is by adding properties with the upper and lower bounds

Shreyas Srinivas (Jun 23 2025 at 17:21):

So I think the zero and id laws should remain

Shreyas Srinivas (Jun 23 2025 at 17:23):

And then this Fractional class should be extended with another that bounds the valid range of values. Kinda like subtypes

Shreyas Srinivas (Jun 23 2025 at 17:27):

Otherwise the order relations’ compatibility with addition will become complicated. Further, we would have to reinvent many of the API lemmas I have already proved

Mario Carneiro (Jun 23 2025 at 17:28):

Shreyas Srinivas said:

I also miss all those precious code actions that split cases and create proof holes

Those are in Batteries

Markus de Medeiros (Jun 23 2025 at 17:41):

Shreyas Srinivas said:

Btw the Zero class only adds the notation for zero. For various intervals of Q I think the correct way to do this is by adding properties with the upper and lower bounds

There is no zero in the positive rationals.

Shreyas Srinivas (Jun 23 2025 at 17:44):

Yeah but for positive rationals you can restrict the allowed values using a property

Markus de Medeiros (Jun 23 2025 at 17:44):

I understand that mechanizing a monoid is easy but that is not the structure we want. If the type includes zero elements we will need to change the CMRA to accommodate this, but since this is CMRA is used extensively throughout the derived parts of Iris I am hesitant to do that.

Shreyas Srinivas (Jun 23 2025 at 17:44):

I would call it “PositiveFractional” and impose a restriction that all values a strictly greater than 0

Markus de Medeiros (Jun 23 2025 at 17:45):

...PositiveFractional cannot extend zero then.

Shreyas Srinivas (Jun 23 2025 at 17:55):

0 won’t be a valid value if PositiveFractional

Shreyas Srinivas (Jun 23 2025 at 17:55):

Only fractional

Shreyas Srinivas (Jun 23 2025 at 17:55):

0 will be ruled out by the inability to prove the strict positivity field

Shreyas Srinivas (Jun 23 2025 at 17:56):

It’s basically like docs#Subtype

Markus de Medeiros (Jun 23 2025 at 17:57):

The stuff you've done is great, and it will be convenient to say that the positive restrictions of the classes you laid out will be PositiveFractional! But PositiveFractional itself should be a typeclass.

Think of how you would instantiate PositiveFractional for the type of intrinsically positive fractions represented as pairs structure PFrac : {num : Nat+; den: Nat+; wf : gcd num den = 1}. Under your strategy, you would have to add a zero onto this type

inductive positiveFracPlusZero
| pos : PFrac
| zero

then lift all of the PFrac operations to positiveFracPlusZero, all to use {p : positiveFracPlusZero | 0 < p} as the type of your indices. This is a mess.

Shreyas Srinivas (Jun 23 2025 at 17:58):

Once I’m back I’ll flesh it out in code.

Shreyas Srinivas (Jun 23 2025 at 17:58):

Maybe it will be easier to communicate it that way

Shreyas Srinivas (Jun 23 2025 at 18:10):

In a way you are right. But we can add default tactic arguments like by omega to make most of the api seamless

Shreyas Srinivas (Jun 23 2025 at 18:11):

And then we just establish the API with general bound parameters. EDIT: this is how docs#Vector works on top of docs#Array

suhr (Jun 23 2025 at 18:11):

By the way, why do we need rationals for program verification?

suhr (Jun 23 2025 at 18:12):

(sorry if the question is silly, I do not have that much experience with Iris)

Shreyas Srinivas (Jun 23 2025 at 18:15):

Yeah. This cmra expresses fractional ownership later

Mario Carneiro (Jun 23 2025 at 18:21):

So fractions come up in "fractional ownership" which are a way of dealing with avoiding races between readers and writers. Let's say we want to ensure the property:

  • A writer can only write if there are no concurrent readers or writers
  • Multiple readers can read concurrently

To accomplish this, we can have a separating proposition 1v\ell\mapsto_1 v which represents full ownership of the value, and if you have this then you are the unique owner and can safely write. If you want to share read access to it, you can "split" the ownership into two pieces, (1/2v)(1/2v)(\ell\mapsto_{1/2} v)\ast (\ell\mapsto_{1/2} v), and now distribute these pieces to separate threads of execution. If you have only half of a permission, then you do not know that there are no concurrent readers so you are entitled only to read the value, not write it. The reason for having arbitrary rationals is so that these tokens can be arbitrarily split and passed around, and you can keep reading it as long as the fraction remains positive. When all is done you can recombine the pieces into one full permission and go back to writing.

Mario Carneiro (Jun 23 2025 at 18:25):

Here is what I would expect from an axiomatization of the type of positive fractions here, with the model (0,1](0,1] in mind:

  • 11 is a permission
  • a+b=ca+b=c is a functional ternary relation on permissions (write a+ba+b when this exists)
  • (a+b)+c=a+(b+c)(a+b)+c=a+(b+c)
  • a+b=b+aa+b=b+a
  • a/2a/2 is a function such that a/2+a/2=aa/2+a/2=a

I think that's it?

Shreyas Srinivas (Jun 23 2025 at 18:27):

I think Markus wants something more general. He mentioned wanting to use Real number intervals later

Markus de Medeiros (Jun 23 2025 at 18:27):

Adding onto Mario's explanation, the problem with a zero fraction  0v\ell\mapsto_0 v is that it _does not_ denote read permissions to a location, so it adds an edge case that needs to be handled by the validity predicate of the CMRA.

Shreyas Srinivas (Jun 23 2025 at 18:28):

But yeah I am still convinced that a dual api with default arguments and/or partial functions is the way to handle bounded fractions.

Shreyas Srinivas (Jun 23 2025 at 18:28):

Same as Array and Vector

Shreyas Srinivas (Jun 23 2025 at 18:28):

Otherwise we lose substantially simplicity of API

Mario Carneiro (Jun 23 2025 at 18:29):

note that all of the following satisfy my axioms:

  • (0,1](0,1]
  • Q(0,1]\mathbb{Q}\cap(0,1]
  • D(0,1]\mathbb{D}\cap(0,1], D\mathbb{D} = dyadic rationals
  • R+\mathbb{R}^+
  • Q+\mathbb{Q}^+
  • D+\mathbb{D}^+

You can only concretely construct fractions in D(0,1]\mathbb{D}\cap(0,1] as literals though.

Markus de Medeiros (Jun 23 2025 at 18:30):

That's right. I'm not convinced that your axiomatization actually has everything needed to instantiate CMRA (you don't have a relationship between addition and 1, for example) and the division by 2 axiom is useless.

Mario Carneiro (Jun 23 2025 at 18:30):

To construct 3/4 you need an axiom saying that when a+(b+c)a+(b+c) exists, a+ba+b also exists and a+(b+c)=(a+b)+ca+(b+c)=(a+b)+c (this is the associativity axiom but more precise about what exists)

Mario Carneiro (Jun 23 2025 at 18:32):

The division by 2 axiom is certainly not useless, it's the only way you can actually make fractions

Markus de Medeiros (Jun 23 2025 at 18:32):

Useless for the CMRA instance, which is what were talking about.

Mario Carneiro (Jun 23 2025 at 18:32):

otherwise {1}\{1\} would also be a model of the axioms

suhr (Jun 23 2025 at 18:33):

You also need a ≠ a/2.

Mario Carneiro (Jun 23 2025 at 18:33):

why?

Markus de Medeiros (Jun 23 2025 at 18:33):

Mario Carneiro said:

otherwise {1}\{1\} would also be a model of the axioms

{1}\{1\} is fine!

Mario Carneiro (Jun 23 2025 at 18:34):

is R+\mathbb{R}^+ fine?

Markus de Medeiros (Jun 23 2025 at 18:34):

Yes.

Markus de Medeiros (Jun 23 2025 at 18:35):

Oh-- actually {1} is not fine, sorry. You need addition.

Mario Carneiro (Jun 23 2025 at 18:35):

no you have addition, it's just an empty relation in my setting

Mario Carneiro (Jun 23 2025 at 18:36):

so you can talk about splitting but you can never actually do it

Markus de Medeiros (Jun 23 2025 at 18:36):

I suppose you could change Add to be a relation instead of a function, yes. Then {1} is fine.

Markus de Medeiros (Jun 23 2025 at 18:36):

Frac {1} is the same as Excl I believe.

Mario Carneiro (Jun 23 2025 at 18:37):

I use a relation because I expect that the CMRA wants to do this PCM style

Mario Carneiro (Jun 23 2025 at 18:37):

so we have this way of combining values which is just not always defined

Markus de Medeiros (Jun 23 2025 at 18:38):

The well-definition is already controlled by the validity predicate of the CMRA

Mario Carneiro (Jun 23 2025 at 18:38):

if we restrict to those sets which have 1 as an upper bound then we get the relation a+1=ba+1=b\to\bot which is nice

Markus de Medeiros (Jun 23 2025 at 18:39):

I could believe that. The question then becomes if using a relation instead of a function is easier for clients.

Markus de Medeiros (Jun 23 2025 at 18:40):

Is there (rational/real/natural) addition defined as a relation somewhere? I only know of it defined as a function.

Mario Carneiro (Jun 23 2025 at 18:40):

You can define a<ba<b as c,c+a=b\exists c,c+a=b (which works also when 1 is not an upper bound) and then ¬a<1\neg a<1 means that you can write

suhr (Jun 23 2025 at 18:40):

Anyway, speaking of axioms, what stops defining + in a trivial way: 1 + 1 = 1, where 1 is only value of the type?

This should satisfy the axioms?

Mario Carneiro (Jun 23 2025 at 18:41):

and you want an axiom ¬a<a\neg a<a

Mario Carneiro (Jun 23 2025 at 18:41):

which will prevent your example @suhr

Markus de Medeiros (Jun 23 2025 at 18:41):

Concretely, this is not sound from a logical perspective. You can't exchange 1v\ell\mapsto_1 v for 1v1v\ell\mapsto_1 v \ast \ell\mapsto_1 v because then you get two write permissions to the same place.

Mario Carneiro (Jun 23 2025 at 18:42):

right, but the question is whether this should be prevented in the axioms somehow so that you can actually prove a soundness theorem for generalized fractional permissions using this typeclass

Mario Carneiro (Jun 23 2025 at 18:45):

there is a bit of difference between the sets that have 1 as an upper bound (which prefer the partial addition version) and those that are unbounded (which can have addition as a function but need more side conditions saying things are less than 1)

Mario Carneiro (Jun 23 2025 at 18:45):

What is the type actually used in iris for this?

Markus de Medeiros (Jun 23 2025 at 18:46):

Just to bring the discussion back down to earth, Iris does the following:

  • Use a function for fraction addition
  • Internalize that the fraction values are positive
  • Externalize that the fraction values are at most 1 (using the CMRA validity predicate)

Iris uses Qp, the type of positive rationals for this.

Mario Carneiro (Jun 23 2025 at 18:46):

is division used?

Markus de Medeiros (Jun 23 2025 at 18:47):

Not for the CMRA instance

Markus de Medeiros (Jun 23 2025 at 18:49):

The generalization I'm suggesting matches Iris in terms of externalizing/internalizing different features of Qp, but generalizes the Qp type itself. Other generalizations are possible, but may change the derived proofs in tricky ways.

Mario Carneiro (Jun 23 2025 at 18:49):

in that case how about this:

  • 11 exists
  • a+ba+b exists
  • a+b=b+aa+b=b+a, (a+b)+c=a+(b+c)(a+b)+c=a+(b+c)
  • define a<ba<b as c,c+a=b\exists c,c+a=b, define aba\le b as ¬b<a\neg b<a
  • aaa\le a

Mario Carneiro (Jun 23 2025 at 18:52):

there is no explicit relationship between 1 and + but you would use a1a\le 1 in the validity predicate

Markus de Medeiros (Jun 23 2025 at 18:53):

Mario just for context you can look at the Fractional class in my PR to see a sufficient set of axioms. @Shreyas Srinivas it's worth a shot to try reducing my typeclass to Mario's axioms.

Markus de Medeiros (Jun 23 2025 at 18:53):

Mario Carneiro said:

there is no explicit relationship between 1 and + but you would use a1a\le 1 in the validity predicate

Right, I get it. I did use the field one_strict_max in my DFractional typeclass (which Fractional should also subsume) but I feel that may be able to be removed.

Mario Carneiro (Jun 23 2025 at 18:54):

You should be able to prove transitivity of << and \le using associativity

Mario Carneiro (Jun 23 2025 at 18:54):

oh we do need to add c+a=c+ba=bc+a=c+b\to a=b

Mario Carneiro (Jun 23 2025 at 18:55):

le_refl and positive are the same thing if you have ab¬b<aa\le b\leftrightarrow\neg b<a

Mario Carneiro (Jun 23 2025 at 18:56):

and so add_le_mono should be provable

Mario Carneiro (Jun 23 2025 at 19:07):

(Just to be clear, I'm approving of the general structure of @Markus de Medeiros 's PR, this should be a custom typeclass.)

Mario Carneiro (Jun 23 2025 at 19:07):

the details of the axioms aren't too important

Markus de Medeiros (Jun 23 2025 at 19:09):

Your axiom "a+ba+b exists" means that we should keep + as a function (as it is now) instead of a relation, right? This detail does matter for the downstream code I am working on.

Mario Carneiro (Jun 23 2025 at 19:10):

yes

Mario Carneiro (Jun 23 2025 at 19:11):

this does mean that we won't be able to support D(0,1]\mathbb{D}\cap (0,1] as a model; there is no way you can do addition as a function without saturating, which violates cancellativity

Mario Carneiro (Jun 23 2025 at 19:13):

it also means 1 + 1 = 1 which violates irreflexivity

Markus de Medeiros (Jun 23 2025 at 19:17):

Hm. What is  D\mathbb{D} in this context?

Mario Carneiro (Jun 23 2025 at 19:17):

dyadic rationals

Mario Carneiro (Jun 23 2025 at 19:18):

R\mathbb{R} and Q\mathbb{Q} also have the same behavior though

Markus de Medeiros (Jun 23 2025 at 19:19):

Ah right--the issue is the $$...\cap (0,1]$$ part.

Shreyas Srinivas (Jun 23 2025 at 21:51):

There seems to be a lot of messages here. What’s the conclusion?

Markus de Medeiros (Jun 23 2025 at 21:55):

  • You should try and refine the type class on my initial PR (without a Zero instance), Mario's axioms are probably the refinement you want
  • Your work (the classes with a Zero instance) can stay, the positive subtype of them can be used as a generic instance for the Fractional typeclass

Markus de Medeiros (Jun 23 2025 at 22:03):

  • The Fractional typeclass you end up with should subsume both Fractional from the frac branch and DFractional from the DFrac branch.

Shreyas Srinivas (Jun 23 2025 at 22:26):

My work is a bit generic. It just happens to subsume Fractional. Since Fractional is meant to use a different axiom set, should I move my work to a separate file? Alternatively, I can understand point 2 to mean that you wish to have all the API lemmas and properties from the typeclass, but have them proved from these axioms.

Markus de Medeiros (Jun 23 2025 at 22:30):

Where you put your work or if you keep it is largely irrelevant, just give it a shot (if you still want to!) and let me know when you're done so I can review it and give more detailed feedback.

Shreyas Srinivas (Jun 23 2025 at 22:38):

Okay. I’ll do this tomorrow. It’s past midnight in my time zone.

Shreyas Srinivas (Jun 23 2025 at 22:42):

Fwiw, the APi lemmas I showed for this typeclass will also be needed to make Mario’s definitions usable downstream.

Shreyas Srinivas (Jun 23 2025 at 22:43):

We are basically foregoing all the order theory and algebra that was developed for mathlib. So we need to recover the basic properties that a mathlib user can take for granted.

Mario Carneiro (Jun 23 2025 at 23:38):

I think that's the wrong way to look at it. As suhr remarked, it's surprising that we need rational numbers to begin with, and the truth is that we don't really, we need an extremely basic structure of which rational numbers are the simplest to explain model. Generalizing to the minimum properties necessary actually makes it clearer what's actually going on, and reveals that this really has little to do with rational numbers or any of their algebraic properties.

I am reminded of the evolution of "the interval" in HoTT and cubical type theory. Once upon a time people did homotopy theory using the type [0,1]R[0,1]\subseteq \mathbb{R}, which captures a bunch of structure of topological spaces. But when going synthetic, people started to realize that they can get away with just a rational interval and they get something computable, and in modern cubical type theory it's an extremely basic type, like "thing that has a 0 and 1 and maybe max\max?" And keeping things to that minimal subset is exactly what makes it possible to have a decidable theory of equality, which is part of what makes cubical type theory possible.

Shreyas Srinivas (Jun 24 2025 at 00:14):

hmm.. In neural nets, researchers went from all sorts of non-linear functions like the sigmoid function to softmax for the threshold

Shreyas Srinivas (Jun 24 2025 at 00:15):

I see your point. But I just realised that my generalisation also captures the (N,+)(\N, +) resource for counters I had in my course. Except their core is the constant function 00. EDIT : It also captures (N,max)(\N, max)

Shreyas Srinivas (Jun 24 2025 at 00:17):

For now I will use your axioms in the Frac CMRA, but keep the PR for the stuff I wrote open

Mario Carneiro (Jun 24 2025 at 00:18):

Shreyas Srinivas said:

I see your point. But I just realised that my generalisation also captures the (N,+)(\N, +) resource for counters I had in my course. Except their core is the constant function 00

I think that's a different use case, which uses a monoid

Shreyas Srinivas (Jun 24 2025 at 00:19):

It's a commutative monoid with a total order. EDIT : Both (N,+)(\N, +) and (N,max)(\N, max)

Shreyas Srinivas (Jun 24 2025 at 00:21):

Screenshot from 2025-06-24 02-21-15.png

Markus de Medeiros (Jun 24 2025 at 00:24):

Yes, commutative monoids can be a resource (you should do the numbers resources after this!) But these are different than Frac. It is essential that Frac have a maximum valid element which neither (Nat, +) nor (Nat, max) really do.

Shreyas Srinivas (Jun 24 2025 at 00:29):

Okay. In that case I will proceed as follows:

  1. I will move my current Frac.lean to Numbers.lean. Maybe even rename the PR.

Shreyas Srinivas (Jun 24 2025 at 00:30):

  1. Separately start a new PR against your PR to add the axioms Mario suggested for Frac.lean

All this tomorrow

Shreyas Srinivas (Jun 25 2025 at 11:53):

Mild confusion. In Init.Prelude online, I see a typeclass One here

Shreyas Srinivas (Jun 25 2025 at 11:53):

But somehow I can't access this in iris-lean

Shreyas Srinivas (Jun 25 2025 at 11:54):

Did something change between toolchain updates?

Shreyas Srinivas (Jun 25 2025 at 11:55):

This is the same part of the same file but opened in my editor with Ctrl + Click and the One typeclass is missing

Screenshot from 2025-06-25 13-55-15.png

Oliver Soeser (Jun 25 2025 at 12:04):

That typeclass was only added two months ago, maybe the version of Lean you're using doesn't have it yet?

Shreyas Srinivas (Jun 25 2025 at 12:06):

Okay that's possible. I haven't merged master and I don't think Markus has either

Shreyas Srinivas (Jun 25 2025 at 12:07):

I remember this class being around for a while.

Oliver Soeser (Jun 25 2025 at 12:11):

That typeclass has certainly been around for a while, but I think it was scattered across different files

Shreyas Srinivas (Jun 25 2025 at 12:12):

Nah it isn't there even in v4.20.0

Oliver Soeser (Jun 25 2025 at 12:12):

This seems to be the commit that brought it into the Prelude: # Commit 196d899

Shreyas Srinivas (Jun 25 2025 at 12:14):

Then it is surprising that it wasn't in this month's release

Oliver Soeser (Jun 25 2025 at 12:21):

Are you sure you're running v4.20? One is in the Prelude for me: image.png

Shreyas Srinivas (Jun 25 2025 at 12:24):

Oh thanks. I was in the wrong branch

Oliver Soeser (Jun 25 2025 at 12:24):

No worries!

Shreyas Srinivas (Jun 25 2025 at 12:36):

We need totality of the order relation

Oliver Soeser (Jun 25 2025 at 12:41):

What requires that?

Shreyas Srinivas (Jun 25 2025 at 12:48):

You can't prove it from the axioms above

Shreyas Srinivas (Jun 25 2025 at 12:48):

But maybe I am thinking about this the wrong way.

Shreyas Srinivas (Jun 25 2025 at 12:48):

Maybe we don't need to prove it

Oliver Soeser (Jun 25 2025 at 13:04):

Mario Carneiro said:

in that case how about this:

  • 11 exists
  • a+ba+b exists
  • a+b=b+aa+b=b+a, (a+b)+c=a+(b+c)(a+b)+c=a+(b+c)
  • define a<ba<b as c,c+a=b\exists c,c+a=b, define aba\le b as ¬b<a\neg b<a
  • aaa\le a

These axioms?

Shreyas Srinivas (Jun 25 2025 at 13:38):

leanprover-community/iris-lean#62

Kevin Buzzard (Jun 25 2025 at 13:44):

FWIW (Z>0)2(\Z_{>0})^2 (with 1 defined arbitrarily and + defined componentwise) seems to satisfy the both the axioms quoted by Oliver and the ones in the PR, and isn't total.

Shreyas Srinivas (Jun 25 2025 at 14:01):

I am beginning to think the only thing that we really need to check is if x<1x < 1 for some xx

Shreyas Srinivas (Jun 25 2025 at 14:02):

If not, then x=1x = 1 and we have total ownership of some resource

Shreyas Srinivas (Jun 25 2025 at 14:06):

More concretely, total ordering is not necessary

Markus de Medeiros (Jun 25 2025 at 14:06):

That's an interesting thought

Oliver Soeser (Jun 25 2025 at 14:07):

That's my intuition too, totality seems like an unnecessary restriction

Markus de Medeiros (Jun 25 2025 at 14:09):

It would be cool if we could instantiate it with abstract fractional permissions like here (though I wouldn't read too much into their exact permissions structure itself)

Shreyas Srinivas (Jun 25 2025 at 14:09):

Lol. I was thinking of something just like that

Shreyas Srinivas (Jun 25 2025 at 14:10):

Basically you don't need any operation other than halving and recombining

Shreyas Srinivas (Jun 25 2025 at 14:10):

there is an implicit partial order in halving

Shreyas Srinivas (Jun 25 2025 at 14:11):

So we only combine things that are equal in this order

Shreyas Srinivas (Jun 25 2025 at 14:14):

what we really need is an abstract binary tree structure where leaf nodes at the same level can be combined into a leaf node at the parent level or split

Markus de Medeiros (Jun 25 2025 at 14:14):

DFrac is more like the abstract fractional permissions in that paper, and deals with the invalid combinations, I think? Would be interesting to see if generalizing to partial orders (with total comparisons to 1) could simplify these OFE's.

Markus de Medeiros (Jun 25 2025 at 14:16):

Shreyas Srinivas said:

what we really need is an abstract binary tree structure where leaf nodes at the same level can be combined into a leaf node at the parent level or split

The rationals are not a binary tree so this is not right.

Shreyas Srinivas (Jun 25 2025 at 14:17):

no they are not, but as Mario says, we just need dyadic rationals

Oliver Soeser (Jun 25 2025 at 14:17):

I don't see why we can only combine equals?

Shreyas Srinivas (Jun 25 2025 at 14:17):

It means we don't have to keep track of what we are adding

Shreyas Srinivas (Jun 25 2025 at 14:18):

two equal leaf child nodes yield a new leaf node at the parent level

suhr (Jun 25 2025 at 14:19):

The rationals are not a binary tree so this is not right.

SternBrocotTree.svg.png

Oliver Soeser (Jun 25 2025 at 14:19):

Would that still work in practice though? To my knowledge it's fairly common for unequal fractional permissions to be combined

Shreyas Srinivas (Jun 25 2025 at 14:20):

So let's take a step back and consider what the fractional ownership actually represents

Shreyas Srinivas (Jun 25 2025 at 14:21):

In terms of heaplang, we get to create two threads out of one, if we fork at a thread.

Shreyas Srinivas (Jun 25 2025 at 14:24):

we recombine ownership when we join two threads

suhr (Jun 25 2025 at 14:25):

One could also spawn N threads simultaneously and then somehow use the fact that threads are essentially the same?

Shreyas Srinivas (Jun 25 2025 at 14:25):

You can model that general scenario using dyadic rationals

Shreyas Srinivas (Jun 25 2025 at 14:26):

but the model that I think Mario has in mind is based on what we do in heaplang

suhr (Jun 25 2025 at 14:26):

You can, but the proof might become more complex.

Shreyas Srinivas (Jun 25 2025 at 14:26):

We can instantiate the class with rationals

Shreyas Srinivas (Jun 25 2025 at 14:26):

and define the combining operation in terms of addition

Shreyas Srinivas (Jun 25 2025 at 14:27):

We will have the issue you speak of in any case @suhr : The definition Mario proposed is rather general

Shreyas Srinivas (Jun 25 2025 at 14:28):

The creation of n threads is an atomic sequence of (n-1) forks

Markus de Medeiros (Jun 25 2025 at 14:28):

Yes. Halving of any kind is not necessary for the proof that Frac is a CMRA and therefore should not be part of the Fractional class.

Shreyas Srinivas (Jun 25 2025 at 14:30):

Anyway, I think I have most of it nailed. I only made two changes. I added an axiom that a < b -> a \neq b

Shreyas Srinivas (Jun 25 2025 at 14:30):

and defined a \leq b as a < b \or a = b

Shreyas Srinivas (Jun 25 2025 at 14:30):

This is much more convenient for proofs

Markus de Medeiros (Jun 27 2025 at 19:01):

Shreyas Srinivas said:

I am beginning to think the only thing that we really need to check is if x<1x < 1 for some xx

Just curious, did this not end up working out for some reason?

Shreyas Srinivas (Jun 27 2025 at 19:12):

Oh no it works. I didn’t imagine this PR was the right place to experiment since Mario and you already had a plan.

Shreyas Srinivas (Jun 27 2025 at 19:12):

In general I think frac should just be a rooted tree

Shreyas Srinivas (Jun 27 2025 at 19:13):

The arity need not be fixed

Shreyas Srinivas (Jun 27 2025 at 19:13):

Basically it would model thread ownership

Markus de Medeiros (Jun 27 2025 at 19:13):

Shreyas Srinivas said:

Oh no it works. I didn’t imagine this PR was the right place to experiment since Mario and you already had a plan.

Good to know!

Shreyas Srinivas (Jun 27 2025 at 19:18):

A thread can only return fractional ownership to its creator if it has the entirety of its piece. When a thread spawns with split ownership, then, correspondingly many child nodes are tacked on. When the child nodes return their piece they are deleted. Ownership is 1 only if the tree has a root node with no children.

Frac is a very special case of this where, we always maintain a version of the tree flattened upto the root node plus its children nodes :
meaning either of the following :

  1. only the root node produces more pieces of partial ownership (or)
  2. The only non root nodes are leaf nodes, which are children of the root. Instead of adding child nodes to a non root node, we simply replace that node with more leaf nodes.

In either case total ownership is represented by a tree with only the root node.

Shreyas Srinivas (Jun 27 2025 at 19:36):

The nice thing is one can effectively compute when ownership is complete without the imprecision of machine arithmetic or overhead of rational arithmetic.

Shreyas Srinivas (Jun 27 2025 at 19:37):

One can also precisely reason about when a thread can relinquish ownership of its fraction.

Mario Carneiro (Jun 27 2025 at 20:52):

Markus de Medeiros said:

Yes. Halving of any kind is not necessary for the proof that Frac is a CMRA and therefore should not be part of the Fractional class.

While true, I think you will probably want a class that extends this which actually contains a halving or similar operation, since without halving you can't actually do anything interesting with the class that would motivate including this CMRA in your iProp. And you might want to reconsider calling this class Fractional as there is nothing fractional about it, PNat satisfies the definition

Mario Carneiro (Jun 27 2025 at 20:55):

Shreyas Srinivas said:

In general I think frac should just be a rooted tree

That doesn't sound associative

Mario Carneiro (Jun 27 2025 at 21:05):

Shreyas Srinivas said:

A thread can only return fractional ownership to its creator if it has the entirety of its piece. When a thread spawns with split ownership, then, correspondingly many child nodes are tacked on. When the child nodes return their piece they are deleted. Ownership is 1 only if the tree has a root node with no children.

Your model, where you only recombine elements at the same height, is more restrictive than the actual fractional permissions model over Q, which in particular lets you do the following:

  • Initialize with x1vx\mapsto_1 v
  • Split x1vx\mapsto_1 v into (xav)(xbv)(x\mapsto_a v)\ast (x\mapsto_b v) where a+b=1a+b=1
  • Fork off thread A with xavx\mapsto_a v permission
  • Split xbvx\mapsto_b v into (xcv)(xdv)(x\mapsto_c v)\ast (x\mapsto_d v) where c+d=bc+d=b
  • Fork off thread C with xcvx\mapsto_c v permission
  • Thread C passes its ownership of xcvx\mapsto_c v to thread A (say, through a mutex) and exits
  • Thread A recombines permissions to xevx\mapsto_e v where a+c=ea+c=e
  • Thread A dies and returns its permissions to main thread
  • Main thread recombines permissions to x1vx\mapsto_1 v where d+e=1d+e=1

If you keep track of what is happening with the fractions, you will see that at least one of the splits/merges a+b=1a+b=1, c+d=bc+d=b, a+c=ea+c=e, d+e=1d+e=1 has to involve adding two fractions that are not at the same height. Permitting splits and merges only in perfect halves only permits stack-like usage of read permissions and would ban the cross cut here where thread C passes its permission over to thread A.

Shreyas Srinivas (Jun 27 2025 at 21:49):

Okay but you can recover this by allowing child nodes to be transferred

Shreyas Srinivas (Jun 27 2025 at 21:50):

At that point that fractional piece (c) is shifted to a different subtree so the subtree rooted at (b) is not concerned about it

Mario Carneiro (Jun 27 2025 at 21:52):

my point is that you need associativity to hold, and it doesn't if you only allow combining equal pieces

Mario Carneiro (Jun 27 2025 at 21:52):

if we suppose that all the splits are equal, then a=b=1/2a=b=1/2, c=d=1/4c=d=1/4, and then e=3/4e=3/4 and 1/4+3/4=11/4+3/4=1 is an unequal merge

Mario Carneiro (Jun 27 2025 at 21:54):

that is, you need dyadic rationals, not just {1/2nnN}\{1/2^n\mid n\in\mathbb{N}\}

Shreyas Srinivas (Jun 27 2025 at 21:56):

Okay so concretely we need an operation that works on combining arbitrary subtrees associatively.

Mario Carneiro (Jun 27 2025 at 21:56):

what exactly are the trees you are referring to?

Markus de Medeiros (Jun 27 2025 at 21:59):

Mario Carneiro said:

While true, I think you will probably want a class that extends this which actually contains a halving or similar operation,

Yeah I wouldn't be opposed to this by any stretch! This won't affect anything beneath a program logic though, so it's library code.

Mario Carneiro (Jun 27 2025 at 22:00):

well you can't type a fork operation without it

Markus de Medeiros (Jun 27 2025 at 22:00):

Good thing the language I care about doesn't have fork :p

Mario Carneiro (Jun 27 2025 at 22:00):

why would you care about fractional permissions if you can't make fractions?

Mario Carneiro (Jun 27 2025 at 22:01):

we already have Auth, right?

Markus de Medeiros (Jun 27 2025 at 22:02):

Yes. But it's downstream of some constructions like gmap_view which I do care about.

Markus de Medeiros (Jun 27 2025 at 22:04):

In gmap_view the authoritative part has a fractional permission, and is used in a variety of program logics. We could write a version of gmap_view which elides this but it's nice that I can get the one I want without duplicating the structure.

Markus de Medeiros (Jun 27 2025 at 22:06):

It might be possible to parameterize gmap_view even further (parameterize the auth part by an arbitrary CMRA or something?) but I'm not sure about that yet.

Markus de Medeiros (Jun 27 2025 at 22:10):

Anyways, I ended up refactoring Shreyvas' code to implement his idea about comparing to 1

section Fractional
class Fractional (α : Type _) extends Add α where
  /-- Validity predicate on fractions. Generalizes the notion of `(· ≤ 1)` from rational fractions. -/
  proper : α  Prop
  add_comm :  {a b : α}, a + b = b + a
  add_assoc :  {a b c : α}, a + (b + c) = (a + b) + c
  add_left_cancel :  {a b c : α}, a + b = a + c  b = c
  /-- There does not exist a zero fraction. -/
  add_ne : {a b : α}, a  b + a
  proper_add_mono_left :  {a b : α}, proper (a + b)  proper a

/-- A type of fractions with a unique whole element. -/
class UFractional (α : Type _) extends Fractional α, One α where
  whole_iff_one {a : α} : whole a  a = 1

section NumericFractional
/-- Generic fractional instance for types with comparison and 1 operators. -/
class NumericFractional (α : Type _) extends One α, Add α, LE α, LT α where
  add_comm :  {a b : α}, a + b = b + a
  add_assoc :  {a b c : α}, a + (b + c) = (a + b) + c
  add_left_cancel :  {a b c : α}, a + b = a + c  b = c
  le_def :  {a b : α}, a  b  a = b  a < b
  lt_def :  {a b : α}, a < b   c : α, a + c = b
  lt_not_eq :  {a b : α}, a < b  a  b

instance [NumFractional α] : UFractional α where ...

I think UFractional is probably the most general class that doesn't cause real problems downstream, but I'll keep playing around with it over the next few days to find out.

Mario Carneiro (Jun 27 2025 at 22:11):

what is whole?

Shreyas Srinivas (Jun 27 2025 at 22:13):

I think it’s an implementation of the idea that the only order comparison you actually care about it with 1

Mario Carneiro (Jun 27 2025 at 22:13):

what is add_left_cancel used for?

Mario Carneiro (Jun 27 2025 at 22:13):

I thought that was proper @Shreyas Srinivas

Mario Carneiro (Jun 27 2025 at 22:13):

I don't think @Markus de Medeiros 's code compiles as written since whole isn't defined

Shreyas Srinivas (Jun 27 2025 at 22:14):

whole is proper when the order is total I suppose.

Mario Carneiro (Jun 27 2025 at 22:14):

no, whole appears to generalize a >= 1 while proper generalizes a <= 1

Mario Carneiro (Jun 27 2025 at 22:15):

or maybe 1 | a, I'm not sure because there are no axioms for it

Markus de Medeiros (Jun 27 2025 at 22:16):

Ah right--sorry I snipped that segment out of a couple parts of the PR. Here's the missing piece:

open Fractional in
/-- A fraction does not represent the entire resource.
Generalizes the notion of `(· < 1)` from rational fractions. -/
abbrev fractional [Fractional α] (a : α) : Prop :=
  proper a   b, proper (a + b)

open Fractional in
/-- A fraction that is tne entire resource.
Generalizes the notion of `1` from rational fractions. -/
abbrev whole [Fractional α] (a : α) : Prop :=
  proper a  ¬∃ b, proper (a + b)

Mario Carneiro (Jun 27 2025 at 22:17):

I would expect the first conjunct in fractional to not be needed

Mario Carneiro (Jun 27 2025 at 22:17):

FYI proper, fractional and whole should be capitalized

Markus de Medeiros (Jun 27 2025 at 22:17):

Proper generalizes <=, fractional generalizes <, whole generalizes 1. I wanted to see how general I could make the CMRA construction, but for some downstream code it is (or at least seems) important to have a unique whole element.

Shreyas Srinivas (Jun 27 2025 at 22:18):

Mario Carneiro said:

what is add_left_cancel used for?

I recall using it to prove a few lemmas about order in my pr to Markus’s PR

Markus de Medeiros (Jun 27 2025 at 22:18):

Mario Carneiro said:

I would expect the first conjunct in fractional to not be needed

I think you're right about that

Mario Carneiro (Jun 27 2025 at 22:19):

and if you do that then whole can be simplified using fractional

Mario Carneiro (Jun 27 2025 at 22:19):

these shouldn't be abbrevs

Shreyas Srinivas (Jun 28 2025 at 00:01):

The intuitive idea behind the tree is to represent how ownership of a resource is split. Each subtree represents the split up of some fragment of a resource. All currently owned resources are represented by leaf nodes. When a fragment of a resource is split (or some fragment taken out of the resource, the corresponding leaf node branches out into child nodes.

Shreyas Srinivas (Jun 28 2025 at 00:01):

These splits don’t have to be “equal”

Shreyas Srinivas (Jun 28 2025 at 00:02):

You might choose to transfer ownership of an entire subtree of resources to a different branch.

Shreyas Srinivas (Jun 28 2025 at 00:02):

I am trying to come up with the most free version of resource splitting that keeps track of the history of resource partitioning and sharing.

Shreyas Srinivas (Jun 28 2025 at 00:12):

One can also think of the internal & leaf nodes of the tree as threads, where only leaf threads hold resource fragments. When, say, a single thread is forked from another, the node of the parent thread splits into two child nodes of which one carries the resource of the newly forked thread, and the other carries what’s left in the parent thread. Conversely the join operation joins child nodes. If there are no other child threads left then a child node is merged into its parent node. If you wish to represent the transfer of resources to a different thread, this is done by attaching the corresponding subtrees as children of the node corresponding to the recipient thread.

Shreyas Srinivas (Jun 28 2025 at 00:14):

That being said, the more I write, the more complicated this seems to be getting. Rational numbers seem much simpler

Mario Carneiro (Jun 28 2025 at 00:16):

sure, you can imagine the resources as a finite antichain in the complete binary tree, but then the "numbers" need to represent sets of leaves, not just one

Mario Carneiro (Jun 28 2025 at 00:16):

and that's equivalent to dyadic rationals

Mario Carneiro (Jun 28 2025 at 00:34):

Actually if you really want the free structure defined by these axioms I think you would get:

inductive Rat where
  | one : Rat
  | left : Rat  Rat
  | right : Rat  Rat
  | add : Rat  Rat  Rat

inductive Equiv : Rat  Rat  Prop
  | refl : Equiv a a
  | symm : Equiv a b  Equiv b a
  | trans : Equiv a b  Equiv b c  Equiv a c
  | left : Equiv a a'  Equiv (.left a) (.left a')
  | right : Equiv a a'  Equiv (.right a) (.right a')
  | add : Equiv a a'  Equiv b b'  Equiv (.add a b) (.add a' b')
  | comm : Equiv (.add a b) (.add b a)
  | assoc : Equiv (.add a (.add b c)) (.add (.add a b) c)
  | split : Equiv (.add (.left a) (.right a)) a

inductive Disjoint : Rat  Rat  Prop
  | symm : Disjoint a b  Disjoint b a
  | split : Disjoint (.left a) (.right a)
  | left : Disjoint a b  Disjoint a (.left b)
  | right : Disjoint a b  Disjoint a (.right b)
  | add : Disjoint a b  Disjoint a c  Disjoint a (.add b c)
  | equiv : Equiv b b'  Disjoint a b  Disjoint a b'

inductive Valid : Rat  Prop
  | one : Valid .one
  | left : Valid a  Valid (.left a)
  | right : Valid a  Valid (.right a)
  | add : Valid a  Valid b  Disjoint a b  Valid (.add a b)
  | equiv : Equiv a b  Valid a  Valid b

def FreeRational := @Quot {x : Rat // Valid x} (Equiv ·.1 ·.1)

Mario Carneiro (Jun 28 2025 at 00:35):

This is including an axiom saying splits exist, i.e. x,ab,a+b=x\forall x,\exists a b,a+b=x

Mario Carneiro (Jun 28 2025 at 00:36):

but not specifically that half exists, which is why left and right are not the same

Markus de Medeiros (Jun 28 2025 at 00:57):

Mario Carneiro said:

what is add_left_cancel used for?

Only the CMRA.Cancellable instance right now, for which it seems necessary. No idea what else downstream.

Shreyas Srinivas (Jun 28 2025 at 01:18):

Mario Carneiro said:

sure, you can imagine the resources as a finite antichain in the complete binary tree, but then the "numbers" need to represent sets of leaves, not just one

Yes. But the sets would correspond to a subtree. So merging a set of resources would be collapsing a subtree to its root node.

Mario Carneiro (Jun 28 2025 at 01:19):

but there isn't necessarily one root node

Shreyas Srinivas (Jun 28 2025 at 01:19):

If some fragment of the subtree is actually moved, the corresponding subsubtree is transplanted to the other root node

Shreyas Srinivas (Jun 28 2025 at 01:19):

Mario Carneiro said:

but there isn't necessarily one root node

Root of a subtree.

Shreyas Srinivas (Jun 28 2025 at 01:20):

The overall tree has one root node that represents 1, and when the tree collapses to its root there is sole ownership again.

Mario Carneiro (Jun 28 2025 at 01:20):

if you have

    1
   / \
  d   c
 / \
a   b

then {b, c} can't be represented by the root node 1 because it's missing a

Mario Carneiro (Jun 28 2025 at 01:20):

there is no single node which represents {b, c}

Mario Carneiro (Jun 28 2025 at 01:21):

in the above inductive this would be add (right (left one)) (right one)

Mario Carneiro (Jun 28 2025 at 01:23):

and actually I don't think sets of nodes from the tree is enough, since if you decided to split {b, c} then there is no set of nodes that would be equivalent to (i.e. left (add (right (left one)) (right one)) isn't a set of nodes)

Shreyas Srinivas (Jun 28 2025 at 01:25):

If you split b and c, you would have a new internal node

Mario Carneiro (Jun 28 2025 at 01:26):

no, I'm splitting b + c

Mario Carneiro (Jun 28 2025 at 01:26):

not b or c

Shreyas Srinivas (Jun 28 2025 at 01:26):

Oh right

Shreyas Srinivas (Jun 28 2025 at 01:27):

But once we do b + c, do we care to keep their identities separate?

Shreyas Srinivas (Jun 28 2025 at 01:28):

Can’t they just be one thread carrying b and c as on big fragment of resource?

Mario Carneiro (Jun 28 2025 at 01:28):

we don't, but that's the problem, there is no single node representing that combination so now we can't draw children of that non-node to represent the result of splitting it

Mario Carneiro (Jun 28 2025 at 01:29):

we would need a representation where sums of elements are themselves elements

Shreyas Srinivas (Jun 28 2025 at 01:29):

In my idea you would combine the two nodes of b and c into one node

Mario Carneiro (Jun 28 2025 at 01:29):

which the inductive type does

Mario Carneiro (Jun 28 2025 at 01:29):

what does that mean?

Shreyas Srinivas (Jun 28 2025 at 01:29):

If it is the sole remaining child, then just delete and let the parent node represent the combination of b and c

Mario Carneiro (Jun 28 2025 at 01:30):

You can definitely do it if you have mutable state, the challenge here is that this "rational" type has to represent every possible split pattern and history

Mario Carneiro (Jun 28 2025 at 01:30):

if you have mutable state you just need a list of objects that add to 1

Mario Carneiro (Jun 28 2025 at 01:31):

and every split just removes that element and adds two more

Shreyas Srinivas (Jun 28 2025 at 01:31):

Let’s say you have a node structure a-> [b,c,d]. When merging b and c you create a combined node e. Then you get a -> [e,d]

Shreyas Srinivas (Jun 28 2025 at 01:32):

If you combine e and d into a new blob f, first that node becomes a -> [f]`

Shreyas Srinivas (Jun 28 2025 at 01:32):

Then because a has only one child

Shreyas Srinivas (Jun 28 2025 at 01:32):

a gets replaced by f

Mario Carneiro (Jun 28 2025 at 01:32):

but Rational is just a type, it isn't living in some particular context which keeps track of what other rationals have been created

Mario Carneiro (Jun 28 2025 at 01:34):

and in particular even if you did store that context inside each rational you would get in trouble when it goes out of date because you try to hold on to one piece while splitting another one

Shreyas Srinivas (Jun 28 2025 at 01:40):

My hope is that we don’t actually have to track exact numbers. After all we only care that we can merge the same things we split and check that we have all the pieces. Suppose we split a cake into pieces and put them in different plates on a table. We then put the pieces back on the cake tray. Even if we don’t rearrange them into the exact shape, and have a haphazard arrangement, ,we can determine that all the pieces we cut out have been returned. This is because all the plates are empty

Shreyas Srinivas (Jun 28 2025 at 01:41):

The tree analogy just introduces some bigger intermediate plates and the tree structure tracks which small plates derive their cake from which intermediate plate.

Markus de Medeiros (Jun 28 2025 at 01:41):

Iris is affine so you can lose plates

Shreyas Srinivas (Jun 28 2025 at 01:56):

Right, but even if someone steals a plate, we can track of whether someone owns a totality or part of the remaining cake by checking for emptiness of remaining plates

Shreyas Srinivas (Jun 28 2025 at 01:57):

Even in the rational number case, if some resource fragment just disappears, then thanks to our full ownership check (=?= 1) , the remainig fragments can’t sum up to 1.

Shreyas Srinivas (Jun 28 2025 at 01:58):

To be upfront, I can see the simplicity of just using rational numbers here. The more I think about this tree system, the more complicated it seems.

Mario Carneiro (Jun 28 2025 at 01:59):

Shreyas Srinivas said:

My hope is that we don’t actually have to track exact numbers. After all we only care that we can merge the same things we split and check that we have all the pieces. Suppose we split a cake into pieces and put them in different plates on a table. We then put the pieces back on the cake tray. Even if we don’t rearrange them into the exact shape, and have a haphazard arrangement, ,we can determine that all the pieces we cut out have been returned. This is because all the plates are empty

The free rational type is strictly harder than just tracking actual numbers. But really you don't need to do this in logical reasoning, you existentially quantify all of these numbers so you aren't actually working with them concretely anyway

Mario Carneiro (Jun 28 2025 at 02:00):

The point of this type is to have an abstract accounting mechanism that in principle does not forget anything

Mario Carneiro (Jun 28 2025 at 02:00):

that's what you need to prove the lemmas

Mario Carneiro (Jun 28 2025 at 02:00):

but in the program logic you don't actually need to deal with the numbers

Markus de Medeiros (Jun 28 2025 at 02:09):

Mario Carneiro said:

you existentially quantify all of these numbers

Well that's not strictly true, you could want a finitely splittable fractional resource for example (Nat+ with addition and One.one := n). But your overall point that program logics abstract over the concrete fractions is true.

Mario Carneiro (Jun 28 2025 at 02:10):

if you are doing finite splitting then yes you will have to do math

Mario Carneiro (Jun 28 2025 at 02:11):

maybe the fistful of dollars paper is doing that?

Mario Carneiro (Jun 28 2025 at 02:14):

(misremembered, that was not an iris paper, but https://iris-project.org/pdfs/2019-esop-time.pdf is the iris version)

Markus de Medeiros (Jun 28 2025 at 02:15):

IIRC that's different, more like the numbers CMRA's

Mario Carneiro (Jun 28 2025 at 02:16):

I guess if you want a finitely divisible read permission that would be something like a bounded concurrency primitive

Markus de Medeiros (Jun 28 2025 at 02:26):

Or maybe to specify an invariant for a size N circular buffer, something like [ exists n, Frac(n) * pure (sizeof buf + n = N) ]so if you have Frac i it is sound to add i elements to the buffer without overflow.

Markus de Medeiros (Jun 28 2025 at 02:27):

Anyways, this is interesting but no reason to add anything about splitting to Frac haha

Mario Carneiro (Jun 28 2025 at 02:27):

I don't see why you wouldn't just use numbers CMRA for that

Markus de Medeiros (Jun 28 2025 at 02:28):

Sure, yeah

Mario Carneiro (Jun 28 2025 at 02:28):

I think this discussion has been a bit too theoretical, we don't actually have the code that uses Frac

Mario Carneiro (Jun 28 2025 at 02:30):

the only motivation thus far is that it is one part of the stack to define something we want, but obviously that was added for a reason and I am sure we don't have anything that would notice if we just skipped that part

Markus de Medeiros (Jul 29 2025 at 19:50):

FYI: I did end up needing to add in class of fractions with splitting for this update lemma:

theorem dfrac_undiscard_update [IsSplitFraction F] :
    (.discard : DFrac F) ~~>: fun k =>  q, k = .own q

Last updated: Dec 20 2025 at 21:32 UTC