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:
- vin(Q(v)) = v∅ and vout(Q(v)) = v
- Q(v) only contains a subset of the atomic assignments op(s,t) ∈ 𝜒(v) on the field.
- Q(v) is causally ordered.
- 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
= (smin(L2) < smin(L1)) ? L2 : L1
= L2 ⊕ L1
Claim: (L1 ⊕ L2) ⊕ L3 = L1 ⊕ (L2 ⊕ L3)
Then Li ⊕ Lj = (si < sj ? Li : Lj) and smin(Li ⊕ Lj) = min {si,sj }
(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)).
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 O1 ⊕C O2 = Lf(O1,v) + (Rf(O1,v) ⊕|| Rf(O2,v)) where v = vout(O1) ↓ vout(O2).
Claim: O1 ⊕C O2 = O2 ⊕C 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 O1 ⊕R 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: O1 ⊕R O2 = smin (Rf(O1,v)) < smin(Rf(O2,v)) ? O1 : Lf(O1,v) + Rf(O2,v) where + denotes list concatenation.
Proof:
Otherwise O1 ⊕R 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: O1 ⊕R O2 = Lf(O1,v) + F(O1,O2) + F(O2,O1) where + denotes list concatenation.
Claim: O1, O2 causally ordered ⇒ O1 ⊕R O2 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) )
= { 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.
= 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)))
= 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)
= 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)
Proof:
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)
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.
Proof:
⇒ ∃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)
if ∀s < s', 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)
Proof:
⇒ ¬(∃s st v1(s) > v2(s))
⇒ ¬(∃s' st v1(s') > v2(s') and ∀s < s', v1(s) = v2(s))
⇒ ¬dom(v1, v2)
Proof:
⇒ ¬ (v2(sm(v1,v2)) > v1(sm(v1,v2)))
⇒ ¬dom(v2,v1)
The converse of this claim is false.
Proof:
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
By contrapositive, v1 = v2 ⇒ ¬dom(v1,v2)
By symmetry, v2 = v1 ⇒ ¬dom(v2,v1)
Therefore v1 = v2 ⇒ (¬dom(v1,v2) ∧ ¬dom(v2,v1))
Proof:
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')
v1(s') > v2(s') > v3(s')
v1(s') > v2(s') = v3(s')
v1(s') = v2(s') > v3(s')
It follows that dom is a transitive irreflexive relation.
Proof:
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.
Proof:
so ¬dom(v1, v1 ↑ v2) (by an earlier claim)
Proof:
∃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)
Proof:
∃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:
- ¬( dom(v3,v1) ∧ dom(v3,v2) ⇒ dom(v3,v1↑v2) )
- ¬( v1 ≤ v3 ∧ dom(v3,v2) ⇒ dom(v3, v1↑v2) )
- ¬( dom(v1↑v2,v3) ⇒ (dom(v1,v3) Ú dom(v2,v3)) )
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
- Avoid any concept of factorisation of the assignment operations because assignment operations O are always assumed to have vin(O) = v∅.
- 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
- O1.value = O2.value; and
- 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
Proof:
if dom (O1,O2)
dom(O1, O1 ⊕ O2) ⇒ dom(O1, O1) contradiction
dom(O1, O1 ⊕ O2) ⇒ dom(O1, O2) contradiction
Proof:
O1 ⊕ O2
= O2
= O1
= dom(O2,O1) ? O2 : O1
= O2 ⊕ O1
dom(O1,O2) ⇔ ¬dom(O2,O1)
O1 ⊕ O2
= dom(O2,O1) ? O2 : O1
= O2 ⊕ O1
Proof:
= O1 ⊕ O2 (because ¬dom(O1, (O1 ⊕ O2)))
Proof:
= O1 ⊕ (O2 ⊕ O3) (because of absorption)
= O1 ⊕ (O2 ⊕ O3) (because O2 ⊕ O3 = O2)
dom(O1,O2) ⇔ ¬dom(O2,O1)
O2.vend ≠ O3.vend (because X satisfies convergence)
dom(O2,O3) ⇔ ¬dom(O3,O2)
if dom(O1,O2)
(O1 ⊕ O2) ⊕ O3
= O1 (because dom(O1,O3))
= O1 ⊕ O2 (because dom(O1,O2))
= O1 ⊕ (O2 ⊕ O3) (because dom(O2,O3))
= O1 ⊕ (O2 ⊕ O3) (because ¬dom(O2,O3))
= O2 (because dom(O2,O3))
= O1 ⊕ O2 (because ¬dom(O1,O2))
= O1 ⊕ (O2 ⊕ O3) (because dom(O2,O3))
dom(O3,O2) (because O2 ≠ O3 , ¬dom(O2,O3))
dom(O3,O1) (because of transitivity)
(O1 ⊕ O2) ⊕ O3
= 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.