Commit 9eca74ec authored by Kirill Smelkov's avatar Kirill Smelkov

X Teach AllStructs to emit topologies with values

parent 3568e415
......@@ -532,13 +532,14 @@ def Restructure(ztree, newStructure):
# specified keys and btree depth up-to maxdepth. Each tree node is split by
# up-to maxsplit points.
#
# Generated trees contains key-only Buckets.
# kv is {} that defines values to be linked from buckets.
# By default kv=None and generated trees contains key-only Buckets.
#
# If allowEmptyBuckets=y tree structures with empty buckets are also generated.
# By default, except for empty-tree case, only tree structures with non-empty
# buckets are generated, because ZODB BTree package misbehaves if it finds an
# empty bucket in a trees. ZODB.BTree._check also asserts if bucket is empty.
def AllStructs(keys, maxdepth, maxsplit, allowEmptyBuckets=False): # -> i[] of Tree
def AllStructs(keys, maxdepth, maxsplit, allowEmptyBuckets=False, kv=None): # -> i[] of Tree
assert isinstance(maxdepth, int); assert maxdepth >= 0
assert isinstance(maxsplit, int); assert maxsplit >= 0
ks = set(keys)
......@@ -556,18 +557,18 @@ def AllStructs(keys, maxdepth, maxsplit, allowEmptyBuckets=False): # -> i[] of T
else:
# the only possible case for empty tree is T/B
if not allowEmptyBuckets:
yield Tree([], Bucket([], None))
yield Tree([], Bucket([], None if kv is None else []))
return
# XXX ok? (ideally should be -inf,+inf)
klo = 0
khi = 0
for tree in _allStructs(klo, khi, keyv, maxdepth, maxsplit, allowEmptyBuckets):
for tree in _allStructs(klo, khi, keyv, maxdepth, maxsplit, allowEmptyBuckets, kv):
yield tree
def _allStructs(klo, khi, keyv, maxdepth, maxsplit, allowEmptyBuckets):
def _allStructs(klo, khi, keyv, maxdepth, maxsplit, allowEmptyBuckets, kv):
assert klo <= khi
_assertIncv(keyv)
if len(keyv) > 0:
......@@ -593,7 +594,10 @@ def _allStructs(klo, khi, keyv, maxdepth, maxsplit, allowEmptyBuckets):
bkeyv = _keyvSliceBy(keyv, xlo, xhi)
if not allowEmptyBuckets:
assert len(bkeyv) > 0
children.append(Bucket(bkeyv, None))
valuev = None
if kv is not None:
valuev = [kv[k] for k in bkeyv]
children.append(Bucket(bkeyv, valuev))
else:
yield Tree(ksplitv[1:-1], *children) # (s1, s2, ..., sN)
......@@ -606,7 +610,7 @@ def _allStructs(klo, khi, keyv, maxdepth, maxsplit, allowEmptyBuckets):
if not allowEmptyBuckets:
assert len(ckeyv) > 0
ichildrenv.append( _allStructs(
xlo, xhi, ckeyv, maxdepth - 1, maxsplit, allowEmptyBuckets))
xlo, xhi, ckeyv, maxdepth - 1, maxsplit, allowEmptyBuckets, kv))
else:
for children in itertools.product(*ichildrenv):
yield Tree(ksplitv[1:-1], *children) # (s1, s2, ..., sN)
......
......@@ -124,13 +124,13 @@ def test_allStructs():
# X = AllStructs(..., allowEmptyBuckets=True)
# Y = AllStructs(..., allowEmptyBuckets=False)
# XY = X = Y + assert X == Y
def X(keys, maxdepth, maxsplit, allowEmptyBuckets=True):
return list(xbtree.AllStructs(keys, maxdepth, maxsplit, allowEmptyBuckets))
def Y(keys, maxdepth, maxsplit):
return X(keys, maxdepth, maxsplit, allowEmptyBuckets=False)
def XY(keys, maxdepth, maxsplit):
x = X(keys, maxdepth, maxsplit)
y = Y(keys, maxdepth, maxsplit)
def X(keys, maxdepth, maxsplit, allowEmptyBuckets=True, kv=None):
return list(xbtree.AllStructs(keys, maxdepth, maxsplit, allowEmptyBuckets, kv))
def Y(keys, maxdepth, maxsplit, kv=None):
return X(keys, maxdepth, maxsplit, allowEmptyBuckets=False, kv=kv)
def XY(keys, maxdepth, maxsplit, kv=None):
x = X(keys, maxdepth, maxsplit, kv=kv)
y = Y(keys, maxdepth, maxsplit, kv=kv)
assert x == y
return x
......@@ -355,9 +355,9 @@ def test_allStructs():
# TODO test for maxsplit=2 / maxdepth=2 vvv
def TY(keys, maxdepth, maxsplit):
yv = Y(keys, maxdepth, maxsplit)
return list([xbtree.TopoEncode(_) for _ in yv])
def TY(keys, maxdepth, maxsplit, kv=None):
yv = Y(keys, maxdepth, maxsplit, kv=kv)
return list([xbtree.TopoEncode(_, vencode=lambda v: v) for _ in yv])
assert TY([1,3], 1, 1) == [
'T/B1,3',
......@@ -370,6 +370,18 @@ def test_allStructs():
'T3/T-T/B1-B3',
]
# with values
assert TY([1,3], 1,1, kv={1:'a',3:'c'}) == [
'T/B1:a,3:c',
'T/T/B1:a,3:c',
'T/T2/B1:a-B3:c',
'T/T3/B1:a-B3:c',
'T2/B1:a-B3:c',
'T2/T-T/B1:a-B3:c',
'T3/B1:a-B3:c',
'T3/T-T/B1:a-B3:c',
]
# XBlk simulates ZBlk without xbtree_test.py depending on file_zodb.py
class XBlk(Persistent):
......
......@@ -79,6 +79,7 @@ from golang import func, defer, panic
from golang import time
from ZODB import DB
from ZODB.Connection import Connection
from ZODB.MappingStorage import MappingStorage
import transaction
import random
......@@ -190,12 +191,10 @@ def Trees(zstor, r):
xbtree.Restructure(ztree, tree)
head = commit("treegen/trees: %s" % treetxt)
# XXX print more details?
# XXX print just tid (as `zodb commit` does) ?
#xprint("txn %s -> tree(%s) %s" % (ashex(tid), ashex(ztree._p_oid), treetxt))
xprint("%s" % ashex(head))
"""
# AllStructs generates subset of all possible tree changes in
# between kv1 and kv2. See top-level documentation for details.
@func
......@@ -270,6 +269,62 @@ def AllStructs(zstor, kv1txt, kv2txt, n, seed=None):
assert tstruct in t2structv
emit(delta, verify, tstruct)
"""
# AllStructs generates topologies for subset of all possible tree changes in
# between kv1 and kv2. See top-level documentation for details.
@func
def AllStructs(kv1txt, kv2txt, n, seed=None):
zstor = MappingStorage() # in RAM storage to ... XXX writy why we need it
zctx = ZCtx(zstor)
defer(zctx.close)
kv1 = kvDecode(kv1txt, zctx.vdecode)
kv2 = kvDecode(kv2txt, zctx.vdecode)
print("# allstructs %s %s" % (kv1txt, kv2txt))
#print("# n=%d kv1=%s kv2=%s" % (n, kv1txt, kv2txt))
# create the tree
ztree = zctx.root['ztree'] = XLOTree()
commit('init')
# initial kv1 and kv2 states with topologies prepared as ZODB would do natively
patch(ztree, diff({}, kv1), verify=kv1)
commit('kv1')
t1struct0 = xbtree.StructureOf(ztree)
patch(ztree, diff(kv1, kv2), verify=kv2)
commit('kv2')
t2struct0 = xbtree.StructureOf(ztree)
# all tree topologies that can represent kv1 and kv2
maxdepth=2 # XXX -> 3?
maxsplit=1 # XXX -> 2?
t1AllStructs = list(xbtree.AllStructs(kv1.keys(), maxdepth, maxsplit, kv=kv1))
t2AllStructs = list(xbtree.AllStructs(kv2.keys(), maxdepth, maxsplit, kv=kv2))
# seed
if seed is None:
seed = int(time.now())
random.seed(seed)
print("# n=%d seed=%d" % (n, seed))
# all tree1 and tree2 topologies jumps in between we are going to emit:
# native + n random ones.
t1structv = [t1struct0] + random.sample(t1AllStructs, min(n, len(t1AllStructs)))
t2structv = [t2struct0] + random.sample(t2AllStructs, min(n, len(t2AllStructs)))
# emit topologies for tree1->tree2 and tree1<-tree2 transitions for all
# combinations of tree1 and tree2.
t12travel = list(bitravel2Way(t1structv, t2structv))
for i,tstruct in enumerate(t12travel):
if i%2 == 0:
assert tstruct in t1structv
else:
assert tstruct in t2structv
print(zctx.TopoEncode(tstruct))
# bitravel2Way generates travel path through all A<->B edges such
......@@ -391,18 +446,13 @@ def TopoDecode(zctx, text):
@func
def cmd_allstructs(argv):
if len(argv) != 4:
print("Usage: treegen allstructs <zurl> <n> <kv1> <kv2>", file=sys.stderr)
if len(argv) != 3:
print("Usage: treegen allstructs <n> <kv1> <kv2>", file=sys.stderr)
sys.exit(1)
zurl = argv[0]
n = int(argv[1])
kv1, kv2 = argv[2:]
zstor = storageFromURL(zurl)
defer(zstor.close)
AllStructs(zstor, kv1, kv2, n)
n = int(argv[0])
kv1, kv2 = argv[1:]
AllStructs(kv1, kv2, n)
@func
def cmd_trees(argv):
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment