37 Dom relation for assignment

This solution using the dom relation was developed in 2008.

According to Lossy assignment operations we can avoid the whole need for a HB suffix to transform remote operations by using a map from FieldId to the right most enabled assignment in the HB, and by using backward chaining of enabled assignment operations for a given FieldId.

In the following treatment we go even further because we don't even want to store a history buffer as a linear list of operations.

Consider that a composite assignment operation O stores the following state:

  • vin(O) and vout(O), or what amounts to the same thing, for each siteid record a half open interval [t1,t2) for the operations that are present in the operation.
  • A map<FieldId, list<AssignmentEntry> > that records entries for enabled assignments. Disabled assignments are not recorded.

where we have defined


struct AssignmentEntry
{
    SiteId s;
    int t;
    Value v;
};

Definition: Let Li denote the ith element of list L. We say that list L of assignment entries is causally ordered if

i < j ⇔ Li → Lj

Note that if L is causally ordered then there can be no entries with Li || Lj.

Definition: A subsequence of a list is any list formed from a subset of the elements as long as they appear in the same relative order.

Obviously any sub-sequence of a causally ordered list is also causally ordered.

Definition: Given causally valid vector time v, let Q(v) be characterised as follows:

  1. vin(Q(v)) = v and vout(Q(v)) = v
  2. Q(v) only contains a subset of the atomic assignments op(s,t) ∈ 𝜒(v) on the field.
  3. Q(v) is causally ordered.
  4. Q(v) is maximal, meaning no additional operations can be inserted into the sequence without breaking the causal ordering.

Claim: Q(v) is uniquely defined.

The list<AssignmentEntry> is interpreted as an ordered sequence of enabled assignments, where each assignment dominates all previous assignments in the sequence. The list is assumed to be causally ordered.

The justification for not storing disabled assignments (or indeed q positions) is that the proposed control algorithm never uses transpose (or ET).

In the following we focus attention on assignment operations on the same FieldId.

Dual IT between two lists

Let L1, L2 be concurrent, context equivalent, causally ordered lists of atomic assignment operations on the same field.

The dualIT transforms every operation in L1 with every operation in L2 according to the following figure.

Definition:

  • Let s be a special siteid value which compares larger to all other siteids. A site cannot use s for its siteid.
  • Let smin(L) be the minimum siteid that appears in L, or else s if L = [].
  • L1 ⊕ L2 = (smin(L1) < smin(L2)) ? L1 : L2

L1,L2 are causally ordered so therefore L1 ⊕ L2 must be causally ordered.

Claim: L1 ⊕ L2 = L2 ⊕ L1

Proof: L1 ⊕ L2
= (smin(L1) < smin(L2)) ? L1 : L2
= (smin(L2) < smin(L1)) ? L2 : L1
= L2 ⊕ L1

Claim: (L1 ⊕ L2) ⊕ L3 = L1 ⊕ (L2 ⊕ L3)

Proof:
Let si = smin(Li).
Then Li ⊕ Lj = (si < sj ? Li : Lj) and smin(Li ⊕ Lj) = min {si,sj }
(L1 ⊕ L2) ⊕ L3
= min {s1,s2 } < s3 ? (s1 < s2 ? L1 : L2) : L3
= (s1 < min {s2,s3 } ? L1 : (s2 < s3 ? L2 : L3))
= L1 ⊕ (L2 ⊕ L3)

Associativity and commutativity of ⊕ shows that at quiescence all sites will converge to the same representation of the operation which represents the merge over all assignments performed across all sites. ie we have a uniqueness of representation.

Claim: (L1 ⊕ L2) ~ (L1 + IT(L2,L1)).

Proof:
To show equivalence we show that both sides act on the same initial state and produce the same output state.

vin(L1) = vin(L2) = vin(L1 ⊕ L2) = vin(L1 + IT(L2,L1))

Let S1 denote the set of siteids appearing in L1, and S2 the set of siteids appearing in L2. Since L1 || L2, S1 ⋂ S2 = ∅.

Assuming either L1 and L2 are non-empty, we can define sm = min S1 ⋃ S2 which is the minimum siteid that appears in either L1 or L2. Since S1 ⋂ S2 = ∅, sm appears either in S1 or S2 but not both.

If sm ∈ S1 then we know that all operations in L2 are disabled as they IT past L1. Referring to the above figure we can consider the path from the bottom left corner to the top right along the left and top sides. This leads to the result that (L1 + IT(L2,L1)) ~ L1 = L1⊕L2.

Alternatively, if sm ∈ S2, then we deduce L1 + IT(L2,L1) ~ L2 + IT(L1,L2) ~ L2 = L1⊕L2.

Claim: L1 ⊕ L2 is maximal in its use of atomic operations compatible with the constraint that it be causally ordered and all operations in the sequence are enabled.

We now extend the definition of ⊕ to deal with context equivalent operations O1,O2 (ie satisfying vin(O1) = vin(O2)). Let O1C O2 = Lf(O1,v) + (Rf(O1,v) ⊕|| Rf(O2,v)) where v = vout(O1) ↓ vout(O2).

Claim: O1C O2 = O2C O1

Proof: This is true if Lf(O1,v) = Lf(O2,v), and we could only assume that with some kind of inductive argument!

Factorising an assignment operation

Note that

Ψ(Lf(O,v)) = { op(s,t) ∈ Ψ(O) | t < v(s) }

Ψ(Rf(O,v)) = { op(s,t) ∈ Ψ(O) | t ≥ v(s) }

Factorisation of a list of assignment entries doesn't require transposing of entries because there is no pair O1,O2 that are concurrent (O1 || O2).

Instead factorisation with respect to a causally valid vector time v simply involves splitting the list into prefix and suffix where assignment entries in the prefix satisfy t < v(s), and in the suffix satisfy t ≥ v(s).

That this is always possible without need for transpose follows directly from the definition of a causally valid vector time.

v is causally valid so O1 → O2 and O2 ∈ 𝜒(v) ⇒ O1 ∈ 𝜒(v)

so if O1 appears before O2 in the list, O2 ∈ 𝜒(v) ⇒ O1 ∈ 𝜒(v)

Note that since both an LFactor or RFactor of a list is a subsequence of the original, each preserves the causal ordering.

Merge of assignments

Let O1,O2 be two composite assignment operations on the same FieldId, and satisfying vin(O1) ≤ v and vin(O2) ≤ v where

v = vout(O1) ↓ vout(O2).

By definition O1R O2 = ∑[ vin (O1), vout(O1)↑vout(O2) )

Consider that we factorise O1 with respect to v. From the above we have seen that this simply involves splitting its list of assignment entries into two parts without any need for transposition. Similarly we factorise O2 with respect to v.

It was shown previously that Rf(O1,v) || Rf(O2,v). Therefore given an assignment entry in Rf(O1,v) and an assignment entry in Rf(O2,v) we can assume they are concurrent. Therefore they were generated by different sites and the entry with smaller siteid is regarded as dominating the other.

We have previously treated the case of merging two concurrent lists.

Definition: O1R O2 = smin (Rf(O1,v)) < smin(Rf(O2,v)) ? O1 : Lf(O1,v) + Rf(O2,v) where + denotes list concatenation.

Claim: O1, O2 causally ordered ⇒ O1R O2 is causally ordered.
Proof:
If smin(Rf(O1,v)) < smin(Rf(O2,v)) then O1R O2 = O1 which is causally ordered.

Otherwise O1R O2 = Lf(O1,v) + Rf(O2,v).

Hand waving: Since ⊕ is associative and commutative, there is a uniqueness of representation of O1 and O2. Therefore we expect some commonality in Lf(O1,v) and Lf(O2,v), and in fact Lf(O1,v) = Lf(O2,v) if vin(O1) = vin(O2). That suggests all operations in Lf(O1,v) causally precede operations in Rf(O2,v).

TODO: formalise this!

Bogus

Definition: Let R(O1,O2) = Rf(O1, vout(O1) ↓ vout(O2))

Definition: Let S(O1,O2) be the set of siteids over all the assignment entries in R(O1,O2).

We can assume S(O1,O2) ⋂ S(O2,O1) = ∅.

Definition: Let F(O1,O2) be the subsequence of operations taken from R(O1,O2) without those entries with siteid s1 ∈ S(O1,O2) where ∃ s2 ∈ S(O2,O1) such that s2 < s1.

Since F(O1,O2) is a subsequence of R(O1,O2), we see that it preserves causal ordering.

Claim: F(O1,O2) ≠ ∅ ⇒ F(O2,O1) = ∅

Proof: Suppose ∃ op(s,t) in F(O1,O2). Then ∀ s2 ∈ S(O2,O1), s < s2. But that implies that every entry in R(O2,O1) is dominated by op(s,t), so F(O2,O1) = ∅.

Definition: O1R O2 = Lf(O1,v) + F(O1,O2) + F(O2,O1) where + denotes list concatenation.

Claim: O1, O2 causally ordered ⇒ O1R O2 is causally ordered.

Proof: Lf(O1,v) + F(O1,O2) is a subsequence of O1 so must be causally ordered. We also know that F(O2,O1) is causally ordered.

It remains to show that whenever atomic operation Ox is in Lf(O1,v) + F(O1,O2), and atomic operation Oy is in F(O2,O1) then Ox → Oy.

If Ox in Lf(O1,v) then Ox also appears in Lf(O2,v) [TODO: Does it?]. Therefore Ox → Oy because Lf(O2,v) + F(O2,O1) is causally ordered.

If Ox in F(O1,O2) then F(O2,O1) = ∅.

Further compression of assignment operations

Consider that a site never takes an LFactor of an operation, and it always sends an entire RFactor of the existing HB.

It would appear possible to achieve further compression of an assignment operation by only recording the value for the last assignment in the list, and by ignoring the relative order of the enabled assignments in the list.

It turns out that this is enough information to apply operations, take RFactors and to merge two operations. However, it doesn't allow for an LFactor to be calculated.

Definition: Let N be the set of non-negative integers. ie N = { 0,1,2,3,... }

Definition: Let T⊆ N and n ∈ N. Then we define

Tleft(T,n) = { t ∈ T | t < n } ⊆ T

Tright(T,n) = { t ∈ T | t ≥ n } ⊆ T

Note that ∀n, T = Tleft(T,n) ⋃ Tright(T,n).

Definition: for a given FieldId, O and s, let

tset(O,s) = { t | op(s,t) ∈ Ψ(O) } ⊆ N.

Claim: tset(Lf(O,v),s) = Tleft( tset(O,s), v(s) )

Proof:
tset(Lf(O,v),s)
= { t | op(s,t) ∈ Ψ(Lf(O,v)) }
= { t | op(s,t) ∈ Ψ(O) & t < v(s) }
= Tleft( { t | op(s,t) ∈ Ψ(O) }, v(s) }
= Tleft( tset(O,s), v(s) )

Similarly: tset(Rf(O,v),s) = Tright( tset (O,s), v(s) )

For the given FieldId, let vend(O) be the vector time satisfying (for each siteid s),

vend(O)(s) = ( (tset(O,s) = ∅) ? 0 : 1 + max tset(O,s) )

This provides some information about what assignments have been performed on the field.

smin(Rf(O,v))
= min { s | ∃ op(s,t) ∈ Ψ(Rf(O,v)) }
= min { s | ∃ op(s,t) ∈ Ψ(O) such that t ≥ v(s) }
= min { s | ∃ t ∈ tset(O,s) such that t ≥ v(s) }
= min { s | Tright(tset(O,s),v(s)) ≠ ∅ }
= min { s | vend(O)(s) > v(s) }

Definition: Let predicate Ps(O1,O2) = (smin(Rf(O1,v)) < smin(Rf(O2 ,v)))

Can we use tset(O1,s) and tset(O2,s), to calculate tset(O1R O2)?
tset(O1R O2, s)
= tset( Ps(O1,O2) ? O1 : Lf(O1,v) + Rf(O2,v), s)
= Ps(O1,O2) ? tset(O1, s) : tset(Lf(O1,v) + Rf(O2 ,v), s)
= Ps(O1,O2) ? tset(O1) : tset(Lf(O1,v),s) + tset (Rf(O2,v), s)
= Ps(O1,O2) ? tset(O1) : Tleft( tset(O1,s), v(s) ) + Tright( tset(O2,s), v(s) )

Implementation notes

A set of t values can be stored in increasing order in a vector. Binary search can be used to efficiently find a given t value, or to find the position to split the list into prefix and suffix according to v(s).

Assignable variables are often edited by dragging with the mouse, and therefore there can be many assignments per second (eg 50 Hz) by a given site. This makes some kind of run length encoding highly appropriate for representation of a set of t values, because it is quite likely there will be long runs of sequential t values in a range [t1,t2).

We can store both individual t values as well as pairs [t1,t2) in a single vector<int32> if we use the MSB to distinguish between an individual t values and the t1 of a pair [t1,t2).

With some effort, binary search is compatible with run length encoding!

Proposed test

It is proposed that a self contained unit test be written that simulates n sites that generate and exchange assignment operations on a single assignable field.

An operation records the assigned value, plus a tset indexed by siteid.

struct Op
{
    VectorTime m_v1,m_v2;
    int m_value;
    map<SiteId, set<int> > m_tsets;
};

void Merge(Op& O1, const Op& O2)
{
    VectorTime v = O1.v2   O2.v2;
    for each s
    {
        set<int>& ts = O1.m_tsets[s];
         ...
    }
}

The test uses a single thread and doesn't use any IPC. In fact we don't even model a connection or transient HB-suffixes. Instead we emulate a sending of an operation by picking a sender site s1 and receiver site s2 at random and merging hb1 into hb2. This involves calculating v = vout(hb1) ↓ vout(hb2).

Dom relation on vector times

Definition: Let s0 denote a special site id value (used as a "marker" - and not the identifier for any real site) that compares less to all other site ids. For any given vector time v, we assume v(so) = 0.

Definition: Given vector times v1,v2 we define sm(v1,v2) as follows:

sm(v1,v2) = (v1 = v2) ? s0 : min { s | v1(s) ≠ v2(s) }

Claim: sm(v1,v2) = sm(v2,v1)

Proof:
if v1 = v2
sm(v1,v2) = s0 = sm(v2,v1)
else
sm(v1,v2)
= min { s | v1(s) ≠ v2(s) }
= min { s | v2(s) ≠ v1(s) }
= sm(v2,v1)

Definition: We say v1 dominates v2, written as dom(v1,v2) if

v1(sm(v1,v2)) > v2(sm(v1,v2))

Obviously dom(v1,v2) ⇒ v1 ≠ v2. In other words dom is irreflexive. ie ∀v, ¬dom(v,v)

Claim: dom(v1,v2) ⇔ ∃s' st v1(s') > v2(s') and ∀s < s', v1(s) = v2(s).
Proof:
(⇒)
Suppose dom(v1,v2)
So v1 ≠ v2 and v1(sm(v1,v2)) > v2(sm(v1 ,v2))
Let s' = sm(v1,v2) = min { s | v1(s) ≠ v2(s) }
v1(sm(v1,v2)) > v2(sm(v1,v2))
so v1(s') > v2(s').
s' = min { s | v1(s) ≠ v2(s) }
so ∀s < s', v1(s) = v2(s)
(⇐)
Suppose ∃s' st v1(s') > v2(s') and ∀s < s', v1(s) = v2(s)
v1(s') > v2(s') shows that v1 ≠ v2
It also follows that sm(v1,v2) = s', therefore v1(sm(v1,v2)) > v2(sm(v1,v2)).
Hence dom(v1,v2) is proven.

Claim: dom(v1,v2) ⇔ ∃s' st v1(s') > v2(s') and ∀s < s', v1(s) ≥ v2(s)
Proof:
(⇒)
dom(v1,v2)
⇒ ∃s' st v1(s') > v2(s') and ∀s < s', v1(s) = v2(s)
⇒ ∃s' st v1(s') > v2(s') and ∀s < s', v1(s) ≥ v2(s)
(⇐)
Suppose ∃s' st v1(s') > v2(s') and ∀s < s', v1(s) ≥ v2(s)
if ∀s < s', v1(s) = v2(s)
Then s' satisfies the criteria to deduce dom(v1,v2)
else
∃s'' < s' st v1(s'') > v2(s'')
In fact let s'' be the smallest siteid satisfying v1(s'') > v2(s'')
i.e. ∀s < s'', v1(s) = v2(s)
Then s'' satisfies the criteria to deduce dom(v1,v2)

Claim: v1 ≤ v2 ⇒ ¬dom(v1, v2)
Proof:
v1 ≤ v2
⇒ ∀s, v1(s) ≤ v2(s)
⇒ ¬(∃s st v1(s) > v2(s))
⇒ ¬(∃s' st v1(s') > v2(s') and ∀s < s', v1(s) = v2(s))
⇒ ¬dom(v1, v2)

Claim: dom(v1,v2) ⇒ ¬dom(v2,v1).
Proof:
dom(v1,v2)
⇒ v1 ≠ v2 and v1(sm(v1,v2)) > v2(sm(v1,v2))
⇒ ¬ (v2(sm(v1,v2)) > v1(sm(v1,v2)))
⇒ ¬dom(v2,v1)

The converse of this claim is false.

Claim: (¬dom(v1,v2) ∧ ¬dom(v2,v1)) ⇔ v1 = v2
Proof:
(⇒)
Suppose not,
i.e. ¬dom(v1,v2) ∧ ¬dom(v2,v1) ∧ v1 ≠ v2
given v1 ≠ v2 it follows that { s | v1(s) ≠ v2(s) } is non empty.
Let s' = sm(v1,v2) = sm(v2,v1) = min { s | v1(s) ≠ v2(s) }
It follows that v1(s') ≠ v2(s')
¬dom(v1,v2) ∧ v1 ≠ v2 ⇒ ¬(v1(s') > v2(s')) ⇒ v1(s') ≤ v2(s')
¬dom(v2,v1) ∧ v1 ≠ v2 ⇒ ¬(v2(s') > v1(s')) ⇒ v2(s') ≤ v1(s')
v1(s') ≤ v2(s') ∧ v2(s') ≤ v1(s') ⇒ v1(s') = v2(s') -- contradiction
(⇐)
dom(v1,v2) ⇒ v1 ≠ v2
By contrapositive, v1 = v2 ⇒ ¬dom(v1,v2)
By symmetry, v2 = v1 ⇒ ¬dom(v2,v1)
Therefore v1 = v2 ⇒ (¬dom(v1,v2) ∧ ¬dom(v2,v1))

Claim: (dom(v1,v2) ∧ dom(v2,v3)) ⇒ dom(v1,v3)
Proof:
Let s12 = sm(v1,v2), s23 = sm(v2,v3).
v1(s12) > v2(s12) (because dom(v1,v2))
v2(s23) > v3(s23) (because dom(v2,v3))
∀s < s12, v1(s) = v2(s) (because dom(v1,v2))
∀s < s23, v2(s) = v3(s) (because dom(v2,v3))
Let s' = min(s12,s23)
∀s < s', v1(s) = v2(s) = v3(s)
Claim: v1(s') > v3(s')
if s12 = s23
s' = s12 = s23
v1(s') > v2(s') > v3(s')
else if s12 < s23
s' = s12
v1(s') > v2(s') = v3(s')
else if s23 < s12
s' = s23
v1(s') = v2(s') > v3(s')
dom(v1,v3) (definition)

It follows that dom is a transitive irreflexive relation.

Claim: dom is trichotomous (i.e. exactly one of dom(v1,v2), dom(v2,v1), v1=v2 is true)
Proof:
This follows from these claims which have already been proven:
dom(v1,v2) ⇒ v1 ≠ v2
dom(v1,v2) ⇒ ¬dom(v2,v1)
v1=v2 ⇒ ¬dom(v1,v2)
(¬dom(v1,v2) ∧ ¬dom(v2,v1)) ⇔ v1 = v2

It follows that the dom relation is a strict total order on the set of vector times.

Claim: ¬dom(v1, v1 ↑ v2)
Proof:
v1 ≤ v1 ↑ v2 (property of ↑)
so ¬dom(v1, v1 ↑ v2) (by an earlier claim)

Claim: dom(v1,v3) ⇒ dom(v1↑v2,v3)
Proof:
Suppose dom(v1,v3)
∃s' st v1(s') > v3(s') and ∀s < s', v1(s) = v3(s)
v1(s') > v3(s')
so (v1↑v2)(s') = max(v1(s'), v2(s')) ≥ v1(s) > v3(s')
∀s < s', v1(s) = v3(s)
so ∀s < s', (v1↑v2)(s) = max(v1(s), v2(s)) ≥ v1(s) = v3(s)
∃s' st (v1↑v2)(s') > v3(s') and ∀s < s', (v1↑v2)(s) ≥ v3(s) ⇒ dom(v1 ↑v2,v3)

Claim: dom(v1, v2↑v3) ⇒ dom(v1,v2)
Proof:
Suppose dom(v1, v2↑v3)
∃s' st v1(s') > (v2↑v3)(s') and ∀s < s', v1(s) = (v2↑v3)(s)
so v1(s') > (v2↑v3)(s') = max(v2(s'), v3(s')) ≥ v2(s')
also, ∀s < s', v1(s) = (v2↑v3)(s) = max(v2(s), v3(s)) ≥ v2(s).
Result follows from ∃s' st v1(s') > v2(s') and ∀s < s', v1(s) ≥ v2(s) ⇒ dom(v1,v2)

Claims:

  1. ¬( dom(v3,v1) ∧ dom(v3,v2) ⇒ dom(v3,v1↑v2) )
  2. ¬( v1 ≤ v3 ∧ dom(v3,v2) ⇒ dom(v3, v1↑v2) )
  3. ¬( dom(v1↑v2,v3) ⇒ (dom(v1,v3) Ú dom(v2,v3)) )
Proof:
For a counter example for all these erroneous implications,
let there be three sites and v3 = (1,1,1) dominates both v1 = (1,0,1) and v2 = (0,1,2)
whereas v1↑v2 = (1,1,2) dominates v3.

Even better compression of assignment operations

Consider that we

  1. Avoid any concept of factorisation of the assignment operations because assignment operations O are always assumed to have vin(O) = v.
  2. Don’t store tset(O,s) for each site s, but rather only store vend(O).

An operation on a given assignable field (only) records the following


struct
{
    T value;
    VectorTime vend;
};

where ‘value’ is the state of the field associated with the operation, and ‘vend’ provides some information about what sites have performed an assignment on the field.

Definition: We say operations O1,O2 are equal (written O1 = O2) if both

  1. O1.value = O2.value; and
  2. O1.vend = O2.vend

Definition: We say O1 dominates O2, written as dom(O1,O2) if dom(O1.vend,O2.vend)

Definition: Let X be a set of operations. We say that X satisfies convergence if

∀O1,O2 ∈ X, O1.vend = O2.vend ⇒ O1.value = O2.value

[In other words, O1.vend = O2.vend is sufficient to deduce O1 = O2]

Claim: If X satisfies convergence then

∀O1,O2 ∈ X, (¬dom(O1,O2) ∧ ¬dom(O2,O1)) ⇔ O1 = O2

Claim: If X satisfies convergence, the dom relation is a total ordering on X.

Definition: Given operations O1,O2, the merge O1 ⊕ O2 is an operation defined as follows:

O1 ⊕ O2 = dom(O1,O2) ? O1 : O2

Obviously ∀O, O ⊕ O = O

Claim: ¬dom(O1, O1 ⊕ O2)
Proof:
Suppose dom(O1, O1 ⊕ O2)
if dom (O1,O2)
O1 ⊕ O2 = dom(O1,O2) ? O1 : O2 = O1
dom(O1, O1 ⊕ O2) ⇒ dom(O1, O1) contradiction
else
O1 ⊕ O2 = dom(O1,O2) ? O1 : O2 = O2
dom(O1, O1 ⊕ O2) ⇒ dom(O1, O2) contradiction

Claim: If X satisfies convergence then ∀O1,O2 ∈ X, O1 ⊕ O2 = O2 ⊕ O1
Proof:
if O1 = O2
¬dom(O1,O2) ∧ ¬dom(O2,O1) (X satisfies convergence)
O1 ⊕ O2
= dom(O1,O2) ? O1 : O2
= O2
= O1
= dom(O2,O1) ? O2 : O1
= O2 ⊕ O1
else
O1.vend ≠ O2.vend (X satisfies convergence)
dom(O1,O2) ⇔ ¬dom(O2,O1)
O1 ⊕ O2
= dom(O1,O2) ? O1 : O2
= dom(O2,O1) ? O2 : O1
= O2 ⊕ O1

Claim: O1 ⊕ (O1 ⊕ O2) = O1 ⊕ O2 (absorption property)
Proof:
O1 ⊕ (O1 ⊕ O2)
= dom(O1,O1 ⊕ O2) ? O1 : O1 ⊕ O2
= O1 ⊕ O2 (because ¬dom(O1, (O1 ⊕ O2)))

Claim: If X satisfies convergence then ∀O1,O2,O3 ∈ X, (O1 ⊕ O2) ⊕ O3 = O1 ⊕ (O2 ⊕ O3)
Proof:
if O1 = O2
(O1 ⊕ O2) ⊕ O3
= O2 ⊕ O3 (because O1 ⊕ O2 = O2)
= O1 ⊕ (O2 ⊕ O3) (because of absorption)
else
if O2 = O3
(O1 ⊕ O2) ⊕ O3
= O1 ⊕ O2 (because of absorption)
= O1 ⊕ (O2 ⊕ O3) (because O2 ⊕ O3 = O2)
else
O1.vend ≠ O2.vend (because X satisfies convergence)
dom(O1,O2) ⇔ ¬dom(O2,O1)
O2.vend ≠ O3.vend (because X satisfies convergence)
dom(O2,O3) ⇔ ¬dom(O3,O2)
if dom(O1,O2)
if dom(O2,O3)
dom(O1,O3) (because of transitivity)
(O1 ⊕ O2) ⊕ O3
= O1 ⊕ O3 (because dom(O1,O2))
= O1 (because dom(O1,O3))
= O1 ⊕ O2 (because dom(O1,O2))
= O1 ⊕ (O2 ⊕ O3) (because dom(O2,O3))
else
(O1 ⊕ O2) ⊕ O3
= O1 ⊕ O3 (because dom(O1,O2))
= O1 ⊕ (O2 ⊕ O3) (because ¬dom(O2,O3))
else
if dom(O2,O3)
(O1 ⊕ O2) ⊕ O3
= O2 ⊕ O3 (because ¬dom(O1,O2))
= O2 (because dom(O2,O3))
= O1 ⊕ O2 (because ¬dom(O1,O2))
= O1 ⊕ (O2 ⊕ O3) (because dom(O2,O3))
else
dom(O2,O1) (because O1 ≠ O2 , ¬dom(O1,O2))
dom(O3,O2) (because O2 ≠ O3 , ¬dom(O2,O3))
dom(O3,O1) (because of transitivity)
(O1 ⊕ O2) ⊕ O3
= O2 ⊕ O3 (because ¬dom(O1,O2))
= O3 (because ¬dom(O2,O3))
= O1 ⊕ O3 (because dom(O3,O1))
= O1 ⊕ (O2 ⊕ O3) (because ¬dom(O2,O3))

Proof of convergence at quiescence: This basically follows from the fact that the merge operator ⊕ is commutative and associative. In addition it is obvious from the definition that:

O1,O2 ∈ X ⇒ (O1 ⊕ O2) ∈ X

This shows that the merge operator doesn't add any new operations to X that may upset its ability to satisfy convergence. So as long as new generated operations O that are added to X have a distinct vend, then it follows that X will continue to satisfy convergence.

Only sending deltas

Most generally there will be many identifiable assignable fields, each with their own value and vend.

For efficient bandwidth utilisation it is important to only send small deltas over time between a given pair of sites. Let rhv be an underestimate of what's present in the remote history buffer. Then the sender site can avoid sending the value and vend for an assignable field if it is known that no assignment to the field has occurred outside of X(rhv).

If an assignment has occurred then we must send the entire vend, or else introduce delta vector times for each vend of each field. However that would force the receiver to record a large amount of transient session information, perhaps enough to exhaust memory resources. Therefore we won't try to minimise bandwidth consumption in this way at present.

Session records the set of assignable fields that have changed

A transient session can record a set<FieldId> to keep track of the set of fields that have been assigned since the last operations was sent as part of the session. This set grows over time as local assignment operations are performed. Then, when a new operation needs to be sent it is a simple matter to retrieve the values and vend vector times for all these fields, and send them all as part of one universal operation.

However, this doesn't provide a solution to when a new session has been created, and therefore it is necessary to send all changes to all fields since the collaboration began. It would appear that we need to store a persistent set<FieldId> for the entire working set. This is only cleared after a check-in to the repository.