Compare commits
1 Commits
bxinterval
...
bxinterval
Author | SHA1 | Date | |
---|---|---|---|
5f251e59e5 |
@@ -1,7 +1,26 @@
|
|||||||
# cython: profile=False
|
# cython: profile=False
|
||||||
# This is based on bxintersect in bx-python 554:07aca5a9f6fc (BSD licensed);
|
# This is from bx-python 554:07aca5a9f6fc (BSD licensed), modified to
|
||||||
# modified to store interval ranges as doubles rather than 32-bit integers,
|
# store interval ranges as doubles rather than 32-bit integers.
|
||||||
# use fully closed intervals, support deletion, etc.
|
|
||||||
|
"""
|
||||||
|
Data structure for performing intersect queries on a set of intervals which
|
||||||
|
preserves all information about the intervals (unlike bitset projection methods).
|
||||||
|
|
||||||
|
:Authors: James Taylor (james@jamestaylor.org),
|
||||||
|
Ian Schenk (ian.schenck@gmail.com),
|
||||||
|
Brent Pedersen (bpederse@gmail.com)
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Historical note:
|
||||||
|
# This module original contained an implementation based on sorted endpoints
|
||||||
|
# and a binary search, using an idea from Scott Schwartz and Piotr Berman.
|
||||||
|
# Later an interval tree implementation was implemented by Ian for Galaxy's
|
||||||
|
# join tool (see `bx.intervals.operations.quicksect.py`). This was then
|
||||||
|
# converted to Cython by Brent, who also added support for
|
||||||
|
# upstream/downstream/neighbor queries. This was modified by James to
|
||||||
|
# handle half-open intervals strictly, to maintain sort order, and to
|
||||||
|
# implement the same interface as the original Intersecter.
|
||||||
|
|
||||||
#cython: cdivision=True
|
#cython: cdivision=True
|
||||||
|
|
||||||
import operator
|
import operator
|
||||||
@@ -53,7 +72,7 @@ cdef class IntervalNode:
|
|||||||
cdef public object interval
|
cdef public object interval
|
||||||
cdef public double start, end
|
cdef public double start, end
|
||||||
cdef double minend, maxend, minstart
|
cdef double minend, maxend, minstart
|
||||||
cdef IntervalNode cleft, cright, croot
|
cdef public IntervalNode cleft, cright, croot
|
||||||
|
|
||||||
property left_node:
|
property left_node:
|
||||||
def __get__(self):
|
def __get__(self):
|
||||||
@@ -175,6 +194,76 @@ cdef class IntervalNode:
|
|||||||
self.cright._intersect( start, end, results )
|
self.cright._intersect( start, end, results )
|
||||||
|
|
||||||
|
|
||||||
|
cdef void _seek_left(IntervalNode self, double position, list results, int n, double max_dist):
|
||||||
|
# we know we can bail in these 2 cases.
|
||||||
|
if self.maxend + max_dist < position:
|
||||||
|
return
|
||||||
|
if self.minstart > position:
|
||||||
|
return
|
||||||
|
|
||||||
|
# the ordering of these 3 blocks makes it so the results are
|
||||||
|
# ordered nearest to farest from the query position
|
||||||
|
if self.cright is not EmptyNode:
|
||||||
|
self.cright._seek_left(position, results, n, max_dist)
|
||||||
|
|
||||||
|
if -1 < position - self.end < max_dist:
|
||||||
|
results.append(self.interval)
|
||||||
|
|
||||||
|
# TODO: can these conditionals be more stringent?
|
||||||
|
if self.cleft is not EmptyNode:
|
||||||
|
self.cleft._seek_left(position, results, n, max_dist)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
cdef void _seek_right(IntervalNode self, double position, list results, int n, double max_dist):
|
||||||
|
# we know we can bail in these 2 cases.
|
||||||
|
if self.maxend < position: return
|
||||||
|
if self.minstart - max_dist > position: return
|
||||||
|
|
||||||
|
#print "SEEK_RIGHT:",self, self.cleft, self.maxend, self.minstart, position
|
||||||
|
|
||||||
|
# the ordering of these 3 blocks makes it so the results are
|
||||||
|
# ordered nearest to farest from the query position
|
||||||
|
if self.cleft is not EmptyNode:
|
||||||
|
self.cleft._seek_right(position, results, n, max_dist)
|
||||||
|
|
||||||
|
if -1 < self.start - position < max_dist:
|
||||||
|
results.append(self.interval)
|
||||||
|
|
||||||
|
if self.cright is not EmptyNode:
|
||||||
|
self.cright._seek_right(position, results, n, max_dist)
|
||||||
|
|
||||||
|
|
||||||
|
cpdef left(self, position, int n=1, double max_dist=2500):
|
||||||
|
"""
|
||||||
|
find n features with a start > than `position`
|
||||||
|
f: a Interval object (or anything with an `end` attribute)
|
||||||
|
n: the number of features to return
|
||||||
|
max_dist: the maximum distance to look before giving up.
|
||||||
|
"""
|
||||||
|
cdef list results = []
|
||||||
|
# use start - 1 becuase .left() assumes strictly left-of
|
||||||
|
self._seek_left( position - 1, results, n, max_dist )
|
||||||
|
if len(results) == n: return results
|
||||||
|
r = results
|
||||||
|
r.sort(key=operator.attrgetter('end'), reverse=True)
|
||||||
|
return r[:n]
|
||||||
|
|
||||||
|
cpdef right(self, position, int n=1, double max_dist=2500):
|
||||||
|
"""
|
||||||
|
find n features with a end < than position
|
||||||
|
f: a Interval object (or anything with a `start` attribute)
|
||||||
|
n: the number of features to return
|
||||||
|
max_dist: the maximum distance to look before giving up.
|
||||||
|
"""
|
||||||
|
cdef list results = []
|
||||||
|
# use end + 1 becuase .right() assumes strictly right-of
|
||||||
|
self._seek_right(position + 1, results, n, max_dist)
|
||||||
|
if len(results) == n: return results
|
||||||
|
r = results
|
||||||
|
r.sort(key=operator.attrgetter('start'))
|
||||||
|
return r[:n]
|
||||||
|
|
||||||
def traverse(self):
|
def traverse(self):
|
||||||
if self.cleft is not EmptyNode:
|
if self.cleft is not EmptyNode:
|
||||||
for node in self.cleft.traverse():
|
for node in self.cleft.traverse():
|
||||||
@@ -301,9 +390,15 @@ cdef class IntervalTree:
|
|||||||
def __cinit__( self ):
|
def __cinit__( self ):
|
||||||
root = None
|
root = None
|
||||||
|
|
||||||
|
# Helper for plots
|
||||||
|
def emptynode( self ):
|
||||||
|
return EmptyNode
|
||||||
|
|
||||||
|
def rootnode( self ):
|
||||||
|
return self.root
|
||||||
|
|
||||||
# ---- Position based interfaces -----------------------------------------
|
# ---- Position based interfaces -----------------------------------------
|
||||||
|
|
||||||
## KEEP
|
|
||||||
def insert( self, double start, double end, object value=None ):
|
def insert( self, double start, double end, object value=None ):
|
||||||
"""
|
"""
|
||||||
Insert the interval [start,end) associated with value `value`.
|
Insert the interval [start,end) associated with value `value`.
|
||||||
@@ -313,14 +408,8 @@ cdef class IntervalTree:
|
|||||||
else:
|
else:
|
||||||
self.root = self.root.insert( start, end, value )
|
self.root = self.root.insert( start, end, value )
|
||||||
|
|
||||||
def delete( self, double start, double end, object value=None ):
|
add = insert
|
||||||
"""
|
|
||||||
Delete the interval [start,end) associated with value `value`.
|
|
||||||
"""
|
|
||||||
if self.root is None:
|
|
||||||
self.root = IntervalNode( start, end, value )
|
|
||||||
else:
|
|
||||||
self.root = self.root.insert( start, end, value )
|
|
||||||
|
|
||||||
def find( self, start, end ):
|
def find( self, start, end ):
|
||||||
"""
|
"""
|
||||||
@@ -330,9 +419,26 @@ cdef class IntervalTree:
|
|||||||
return []
|
return []
|
||||||
return self.root.find( start, end )
|
return self.root.find( start, end )
|
||||||
|
|
||||||
|
def before( self, position, num_intervals=1, max_dist=2500 ):
|
||||||
|
"""
|
||||||
|
Find `num_intervals` intervals that lie before `position` and are no
|
||||||
|
further than `max_dist` positions away
|
||||||
|
"""
|
||||||
|
if self.root is None:
|
||||||
|
return []
|
||||||
|
return self.root.left( position, num_intervals, max_dist )
|
||||||
|
|
||||||
|
def after( self, position, num_intervals=1, max_dist=2500 ):
|
||||||
|
"""
|
||||||
|
Find `num_intervals` intervals that lie after `position` and are no
|
||||||
|
further than `max_dist` positions away
|
||||||
|
"""
|
||||||
|
if self.root is None:
|
||||||
|
return []
|
||||||
|
return self.root.right( position, num_intervals, max_dist )
|
||||||
|
|
||||||
# ---- Interval-like object based interfaces -----------------------------
|
# ---- Interval-like object based interfaces -----------------------------
|
||||||
|
|
||||||
## KEEP
|
|
||||||
def insert_interval( self, interval ):
|
def insert_interval( self, interval ):
|
||||||
"""
|
"""
|
||||||
Insert an "interval" like object (one with at least start and end
|
Insert an "interval" like object (one with at least start and end
|
||||||
@@ -340,6 +446,50 @@ cdef class IntervalTree:
|
|||||||
"""
|
"""
|
||||||
self.insert( interval.start, interval.end, interval )
|
self.insert( interval.start, interval.end, interval )
|
||||||
|
|
||||||
|
add_interval = insert_interval
|
||||||
|
|
||||||
|
def before_interval( self, interval, num_intervals=1, max_dist=2500 ):
|
||||||
|
"""
|
||||||
|
Find `num_intervals` intervals that lie completely before `interval`
|
||||||
|
and are no further than `max_dist` positions away
|
||||||
|
"""
|
||||||
|
if self.root is None:
|
||||||
|
return []
|
||||||
|
return self.root.left( interval.start, num_intervals, max_dist )
|
||||||
|
|
||||||
|
def after_interval( self, interval, num_intervals=1, max_dist=2500 ):
|
||||||
|
"""
|
||||||
|
Find `num_intervals` intervals that lie completely after `interval` and
|
||||||
|
are no further than `max_dist` positions away
|
||||||
|
"""
|
||||||
|
if self.root is None:
|
||||||
|
return []
|
||||||
|
return self.root.right( interval.end, num_intervals, max_dist )
|
||||||
|
|
||||||
|
def upstream_of_interval( self, interval, num_intervals=1, max_dist=2500 ):
|
||||||
|
"""
|
||||||
|
Find `num_intervals` intervals that lie completely upstream of
|
||||||
|
`interval` and are no further than `max_dist` positions away
|
||||||
|
"""
|
||||||
|
if self.root is None:
|
||||||
|
return []
|
||||||
|
if interval.strand == -1 or interval.strand == "-":
|
||||||
|
return self.root.right( interval.end, num_intervals, max_dist )
|
||||||
|
else:
|
||||||
|
return self.root.left( interval.start, num_intervals, max_dist )
|
||||||
|
|
||||||
|
def downstream_of_interval( self, interval, num_intervals=1, max_dist=2500 ):
|
||||||
|
"""
|
||||||
|
Find `num_intervals` intervals that lie completely downstream of
|
||||||
|
`interval` and are no further than `max_dist` positions away
|
||||||
|
"""
|
||||||
|
if self.root is None:
|
||||||
|
return []
|
||||||
|
if interval.strand == -1 or interval.strand == "-":
|
||||||
|
return self.root.left( interval.start, num_intervals, max_dist )
|
||||||
|
else:
|
||||||
|
return self.root.right( interval.end, num_intervals, max_dist )
|
||||||
|
|
||||||
def traverse(self):
|
def traverse(self):
|
||||||
"""
|
"""
|
||||||
iterator that traverses the tree
|
iterator that traverses the tree
|
||||||
|
@@ -8,25 +8,20 @@ Intervals are closed, ie. they include timestamps [start, end]
|
|||||||
# First implementation kept a sorted list of intervals and used
|
# First implementation kept a sorted list of intervals and used
|
||||||
# biesct() to optimize some operations, but this was too slow.
|
# biesct() to optimize some operations, but this was too slow.
|
||||||
|
|
||||||
# Second version was based on the quicksect implementation from
|
# This version is based on the quicksect implementation from python-bx,
|
||||||
# python-bx, modified slightly to handle floating point intervals.
|
# modified slightly to handle floating point intervals.
|
||||||
# This didn't support deletion.
|
|
||||||
|
|
||||||
# Third version is more similar to the first version, using a rb-tree
|
|
||||||
# instead of a simple sorted list to maintain O(log n) operations.
|
|
||||||
|
|
||||||
# Fourth version is an optimized rb-tree that stores interval starts
|
|
||||||
# and ends directly in the tree, like bxinterval did.
|
|
||||||
|
|
||||||
# Fifth version is back to modified bxintersect...
|
|
||||||
|
|
||||||
|
import pyximport
|
||||||
|
pyximport.install()
|
||||||
import bxintersect
|
import bxintersect
|
||||||
|
|
||||||
|
import bisect
|
||||||
|
|
||||||
class IntervalError(Exception):
|
class IntervalError(Exception):
|
||||||
"""Error due to interval overlap, etc"""
|
"""Error due to interval overlap, etc"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class Interval(object):
|
class Interval(bxintersect.Interval):
|
||||||
"""Represents an interval of time."""
|
"""Represents an interval of time."""
|
||||||
|
|
||||||
def __init__(self, start, end):
|
def __init__(self, start, end):
|
||||||
@@ -35,8 +30,7 @@ class Interval(object):
|
|||||||
"""
|
"""
|
||||||
if start > end:
|
if start > end:
|
||||||
raise IntervalError("start %s must precede end %s" % (start, end))
|
raise IntervalError("start %s must precede end %s" % (start, end))
|
||||||
self.start = float(start)
|
bxintersect.Interval.__init__(self, start, end)
|
||||||
self.end = float(end)
|
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
s = repr(self.start) + ", " + repr(self.end)
|
s = repr(self.start) + ", " + repr(self.end)
|
||||||
@@ -45,20 +39,6 @@ class Interval(object):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "[" + str(self.start) + " -> " + str(self.end) + "]"
|
return "[" + str(self.start) + " -> " + str(self.end) + "]"
|
||||||
|
|
||||||
def __cmp__(self, other):
|
|
||||||
"""Compare two intervals. If non-equal, order by start then end"""
|
|
||||||
if not isinstance(other, Interval):
|
|
||||||
raise TypeError("bad type")
|
|
||||||
if self.start == other.start:
|
|
||||||
if self.end < other.end:
|
|
||||||
return -1
|
|
||||||
if self.end > other.end:
|
|
||||||
return 1
|
|
||||||
return 0
|
|
||||||
if self.start < other.start:
|
|
||||||
return -1
|
|
||||||
return 1
|
|
||||||
|
|
||||||
def intersects(self, other):
|
def intersects(self, other):
|
||||||
"""Return True if two Interval objects intersect"""
|
"""Return True if two Interval objects intersect"""
|
||||||
if (self.end <= other.start or self.start >= other.end):
|
if (self.end <= other.start or self.start >= other.end):
|
||||||
@@ -86,7 +66,6 @@ class DBInterval(Interval):
|
|||||||
end = 150
|
end = 150
|
||||||
db_end = 200, db_endpos = 20000
|
db_end = 200, db_endpos = 20000
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, start, end,
|
def __init__(self, start, end,
|
||||||
db_start, db_end,
|
db_start, db_end,
|
||||||
db_startpos, db_endpos):
|
db_startpos, db_endpos):
|
||||||
@@ -130,14 +109,12 @@ class IntervalSet(object):
|
|||||||
"""
|
"""
|
||||||
'source' is an Interval or IntervalSet to add.
|
'source' is an Interval or IntervalSet to add.
|
||||||
"""
|
"""
|
||||||
self.tree = bxinterval.IntervalTree()
|
self.tree = bxintersect.IntervalTree()
|
||||||
if source is not None:
|
if source is not None:
|
||||||
self += source
|
self += source
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
for node in self.tree:
|
return self.tree.traverse()
|
||||||
if node.obj:
|
|
||||||
yield node.obj
|
|
||||||
|
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
return sum(1 for x in self)
|
return sum(1 for x in self)
|
||||||
@@ -218,17 +195,6 @@ class IntervalSet(object):
|
|||||||
self.__iadd__(x)
|
self.__iadd__(x)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def __isub__(self, other):
|
|
||||||
"""Inplace subtract -- modifies self
|
|
||||||
|
|
||||||
Removes an interval from the set. Must exist exactly
|
|
||||||
as provided -- cannot remove a subset of an existing interval."""
|
|
||||||
i = self.tree.find(other.start, other.end)
|
|
||||||
if i is None:
|
|
||||||
raise IntervalError("interval " + str(other) + " not in tree")
|
|
||||||
self.tree.delete(i)
|
|
||||||
return self
|
|
||||||
|
|
||||||
def __add__(self, other):
|
def __add__(self, other):
|
||||||
"""Add -- returns a new object"""
|
"""Add -- returns a new object"""
|
||||||
new = IntervalSet(self)
|
new = IntervalSet(self)
|
||||||
@@ -245,12 +211,11 @@ class IntervalSet(object):
|
|||||||
out = IntervalSet()
|
out = IntervalSet()
|
||||||
|
|
||||||
if not isinstance(other, IntervalSet):
|
if not isinstance(other, IntervalSet):
|
||||||
for i in self.intersection(other):
|
other = [ other ]
|
||||||
out.tree.insert(rbtree.RBNode(i))
|
|
||||||
else:
|
|
||||||
for x in other:
|
for x in other:
|
||||||
for i in self.intersection(x):
|
for i in self.intersection(x):
|
||||||
out.tree.insert(rbtree.RBNode(i))
|
out.tree.insert_interval(i)
|
||||||
|
|
||||||
return out
|
return out
|
||||||
|
|
||||||
@@ -264,30 +229,13 @@ class IntervalSet(object):
|
|||||||
Output intervals are built as subsets of the intervals in the
|
Output intervals are built as subsets of the intervals in the
|
||||||
first argument (self).
|
first argument (self).
|
||||||
"""
|
"""
|
||||||
if not isinstance(interval, Interval):
|
for i in self.tree.find(interval.start, interval.end):
|
||||||
raise TypeError("bad type")
|
if i.start > interval.start and i.end < interval.end:
|
||||||
for n in self.tree.intersect(interval.start, interval.end):
|
|
||||||
i = n.obj
|
|
||||||
if i:
|
|
||||||
if i.start >= interval.start and i.end <= interval.end:
|
|
||||||
yield i
|
yield i
|
||||||
elif i.start > interval.end:
|
|
||||||
break
|
|
||||||
else:
|
else:
|
||||||
subset = i.subset(max(i.start, interval.start),
|
yield i.subset(max(i.start, interval.start),
|
||||||
min(i.end, interval.end))
|
min(i.end, interval.end))
|
||||||
yield subset
|
|
||||||
|
|
||||||
def intersects(self, other):
|
def intersects(self, other):
|
||||||
### PROBABLY WRONG
|
|
||||||
"""Return True if this IntervalSet intersects another interval"""
|
"""Return True if this IntervalSet intersects another interval"""
|
||||||
node = self.tree.find_left(other.start, other.end)
|
return len(self.tree.find(other.start, other.end)) > 0
|
||||||
if node is None:
|
|
||||||
return False
|
|
||||||
for n in self.tree.inorder(node):
|
|
||||||
if n.obj:
|
|
||||||
if n.obj.intersects(other):
|
|
||||||
return True
|
|
||||||
if n.obj > other:
|
|
||||||
break
|
|
||||||
return False
|
|
||||||
|
392
nilmdb/rbtree.py
392
nilmdb/rbtree.py
@@ -1,392 +0,0 @@
|
|||||||
"""Red-black tree, where keys are stored as start/end timestamps."""
|
|
||||||
|
|
||||||
import sys
|
|
||||||
|
|
||||||
class RBNode(object):
|
|
||||||
"""One node of the Red/Black tree. obj points to any object,
|
|
||||||
'start' and 'end' are timestamps that represent the key."""
|
|
||||||
def __init__(self, obj = None, start = None, end = None):
|
|
||||||
"""If given an object but no start/end times, get the
|
|
||||||
start/end times from the object.
|
|
||||||
|
|
||||||
If given start/end times, obj can be anything, including None."""
|
|
||||||
self.obj = obj
|
|
||||||
if start is None:
|
|
||||||
start = obj.start
|
|
||||||
if end is None:
|
|
||||||
end = obj.end
|
|
||||||
self.start = start
|
|
||||||
self.end = end
|
|
||||||
self.red = False
|
|
||||||
self.left = None
|
|
||||||
self.right = None
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
if self.red:
|
|
||||||
color = "R"
|
|
||||||
else:
|
|
||||||
color = "B"
|
|
||||||
return ("[node "
|
|
||||||
+ str(self.start) + " -> " + str(self.end) + " "
|
|
||||||
+ color + "]")
|
|
||||||
|
|
||||||
class RBTree(object):
|
|
||||||
"""Red/Black tree"""
|
|
||||||
|
|
||||||
# Init
|
|
||||||
def __init__(self):
|
|
||||||
self.nil = RBNode(start = sys.float_info.min,
|
|
||||||
end = sys.float_info.min)
|
|
||||||
self.nil.left = self.nil
|
|
||||||
self.nil.right = self.nil
|
|
||||||
self.nil.parent = self.nil
|
|
||||||
self.nil.nil = True
|
|
||||||
|
|
||||||
self.root = RBNode(start = sys.float_info.max,
|
|
||||||
end = sys.float_info.max)
|
|
||||||
self.root.left = self.nil
|
|
||||||
self.root.right = self.nil
|
|
||||||
self.root.parent = self.nil
|
|
||||||
|
|
||||||
# Rotations and basic operations
|
|
||||||
def __rotate_left(self, x):
|
|
||||||
y = x.right
|
|
||||||
x.right = y.left
|
|
||||||
if y.left is not self.nil:
|
|
||||||
y.left.parent = x
|
|
||||||
y.parent = x.parent
|
|
||||||
if x is x.parent.left:
|
|
||||||
x.parent.left = y
|
|
||||||
else:
|
|
||||||
x.parent.right = y
|
|
||||||
y.left = x
|
|
||||||
x.parent = y
|
|
||||||
|
|
||||||
def __rotate_right(self, y):
|
|
||||||
x = y.left
|
|
||||||
y.left = x.right
|
|
||||||
if x.right is not self.nil:
|
|
||||||
x.right.parent = y
|
|
||||||
x.parent = y.parent
|
|
||||||
if y is y.parent.left:
|
|
||||||
y.parent.left = x
|
|
||||||
else:
|
|
||||||
y.parent.right = x
|
|
||||||
x.right = y
|
|
||||||
y.parent = x
|
|
||||||
|
|
||||||
def __successor(self, x):
|
|
||||||
"""Returns the successor of RBNode x"""
|
|
||||||
y = x.right
|
|
||||||
if y is not self.nil:
|
|
||||||
while y.left is not self.nil:
|
|
||||||
y = y.left
|
|
||||||
else:
|
|
||||||
y = x.parent
|
|
||||||
while x is y.right:
|
|
||||||
x = y
|
|
||||||
y = y.parent
|
|
||||||
if y is self.root:
|
|
||||||
return self.nil
|
|
||||||
return y
|
|
||||||
|
|
||||||
def _predecessor(self, x):
|
|
||||||
"""Returns the predecessor of RBNode x"""
|
|
||||||
y = x.left
|
|
||||||
if y is not self.nil:
|
|
||||||
while y.right is not self.nil:
|
|
||||||
y = y.right
|
|
||||||
else:
|
|
||||||
y = x.parent
|
|
||||||
while x is y.left:
|
|
||||||
if y is self.root:
|
|
||||||
y = self.nil
|
|
||||||
break
|
|
||||||
x = y
|
|
||||||
y = y.parent
|
|
||||||
return y
|
|
||||||
|
|
||||||
# Insertion
|
|
||||||
def insert(self, z):
|
|
||||||
"""Insert RBNode z into RBTree and rebalance as necessary"""
|
|
||||||
z.left = self.nil
|
|
||||||
z.right = self.nil
|
|
||||||
y = self.root
|
|
||||||
x = self.root.left
|
|
||||||
while x is not self.nil:
|
|
||||||
y = x
|
|
||||||
if (x.start > z.start or (x.start == z.start and x.end > z.end)):
|
|
||||||
x = x.left
|
|
||||||
else:
|
|
||||||
x = x.right
|
|
||||||
z.parent = y
|
|
||||||
if (y is self.root or
|
|
||||||
(y.start > z.start or (y.start == z.start and y.end > z.end))):
|
|
||||||
y.left = z
|
|
||||||
else:
|
|
||||||
y.right = z
|
|
||||||
# relabel/rebalance
|
|
||||||
self.__insert_fixup(z)
|
|
||||||
|
|
||||||
def __insert_fixup(self, x):
|
|
||||||
"""Rebalance/fix RBTree after a simple insertion of RBNode x"""
|
|
||||||
x.red = True
|
|
||||||
while x.parent.red:
|
|
||||||
if x.parent is x.parent.parent.left:
|
|
||||||
y = x.parent.parent.right
|
|
||||||
if y.red:
|
|
||||||
x.parent.red = False
|
|
||||||
y.red = False
|
|
||||||
x.parent.parent.red = True
|
|
||||||
x = x.parent.parent
|
|
||||||
else:
|
|
||||||
if x is x.parent.right:
|
|
||||||
x = x.parent
|
|
||||||
self.__rotate_left(x)
|
|
||||||
x.parent.red = False
|
|
||||||
x.parent.parent.red = True
|
|
||||||
self.__rotate_right(x.parent.parent)
|
|
||||||
else: # same as above, left/right switched
|
|
||||||
y = x.parent.parent.left
|
|
||||||
if y.red:
|
|
||||||
x.parent.red = False
|
|
||||||
y.red = False
|
|
||||||
x.parent.parent.red = True
|
|
||||||
x = x.parent.parent
|
|
||||||
else:
|
|
||||||
if x is x.parent.left:
|
|
||||||
x = x.parent
|
|
||||||
self.__rotate_right(x)
|
|
||||||
x.parent.red = False
|
|
||||||
x.parent.parent.red = True
|
|
||||||
self.__rotate_left(x.parent.parent)
|
|
||||||
self.root.left.red = False
|
|
||||||
|
|
||||||
# Deletion
|
|
||||||
def delete(self, z):
|
|
||||||
if z.left is None or z.right is None:
|
|
||||||
raise AttributeError("you can only delete a node object "
|
|
||||||
+ "from the tree; use find() to get one")
|
|
||||||
if z.left is self.nil or z.right is self.nil:
|
|
||||||
y = z
|
|
||||||
else:
|
|
||||||
y = self.__successor(z)
|
|
||||||
if y.left is self.nil:
|
|
||||||
x = y.right
|
|
||||||
else:
|
|
||||||
x = y.left
|
|
||||||
x.parent = y.parent
|
|
||||||
if x.parent is self.root:
|
|
||||||
self.root.left = x
|
|
||||||
else:
|
|
||||||
if y is y.parent.left:
|
|
||||||
y.parent.left = x
|
|
||||||
else:
|
|
||||||
y.parent.right = x
|
|
||||||
if y is not z:
|
|
||||||
# y is the node to splice out, x is its child
|
|
||||||
y.left = z.left
|
|
||||||
y.right = z.right
|
|
||||||
y.parent = z.parent
|
|
||||||
z.left.parent = y
|
|
||||||
z.right.parent = y
|
|
||||||
if z is z.parent.left:
|
|
||||||
z.parent.left = y
|
|
||||||
else:
|
|
||||||
z.parent.right = y
|
|
||||||
if not y.red:
|
|
||||||
y.red = z.red
|
|
||||||
self.__delete_fixup(x)
|
|
||||||
else:
|
|
||||||
y.red = z.red
|
|
||||||
else:
|
|
||||||
if not y.red:
|
|
||||||
self.__delete_fixup(x)
|
|
||||||
|
|
||||||
def __delete_fixup(self, x):
|
|
||||||
"""Rebalance/fix RBTree after a deletion. RBNode x is the
|
|
||||||
child of the spliced out node."""
|
|
||||||
rootLeft = self.root.left
|
|
||||||
while not x.red and x is not rootLeft:
|
|
||||||
if x is x.parent.left:
|
|
||||||
w = x.parent.right
|
|
||||||
if w.red:
|
|
||||||
w.red = False
|
|
||||||
x.parent.red = True
|
|
||||||
self.__rotate_left(x.parent)
|
|
||||||
w = x.parent.right
|
|
||||||
if not w.right.red and not w.left.red:
|
|
||||||
w.red = True
|
|
||||||
x = x.parent
|
|
||||||
else:
|
|
||||||
if not w.right.red:
|
|
||||||
w.left.red = False
|
|
||||||
w.red = True
|
|
||||||
self.__rotate_right(w)
|
|
||||||
w = x.parent.right
|
|
||||||
w.red = x.parent.red
|
|
||||||
x.parent.red = False
|
|
||||||
w.right.red = False
|
|
||||||
self.__rotate_left(x.parent)
|
|
||||||
x = rootLeft # exit loop
|
|
||||||
else: # same as above, left/right switched
|
|
||||||
w = x.parent.left
|
|
||||||
if w.red:
|
|
||||||
w.red = False
|
|
||||||
x.parent.red = True
|
|
||||||
self.__rotate_right(x.parent)
|
|
||||||
w = x.parent.left
|
|
||||||
if not w.left.red and not w.right.red:
|
|
||||||
w.red = True
|
|
||||||
x = x.parent
|
|
||||||
else:
|
|
||||||
if not w.left.red:
|
|
||||||
w.right.red = False
|
|
||||||
w.red = True
|
|
||||||
self.__rotate_left(w)
|
|
||||||
w = x.parent.left
|
|
||||||
w.red = x.parent.red
|
|
||||||
x.parent.red = False
|
|
||||||
w.left.red = False
|
|
||||||
self.__rotate_right(x.parent)
|
|
||||||
x = rootLeft # exit loop
|
|
||||||
x.red = False
|
|
||||||
|
|
||||||
# Rendering
|
|
||||||
def __render_dot_node(self, node, max_depth = 20):
|
|
||||||
from printf import sprintf
|
|
||||||
"""Render a single node and its children into a dot graph fragment"""
|
|
||||||
if max_depth == 0:
|
|
||||||
return ""
|
|
||||||
if node is self.nil:
|
|
||||||
return ""
|
|
||||||
def c(red):
|
|
||||||
if red:
|
|
||||||
return 'color="#ff0000", style=filled, fillcolor="#ffc0c0"'
|
|
||||||
else:
|
|
||||||
return 'color="#000000", style=filled, fillcolor="#c0c0c0"'
|
|
||||||
s = sprintf("%d [label=\"%g\\n%g\", %s];\n",
|
|
||||||
id(node),
|
|
||||||
node.start, node.end,
|
|
||||||
c(node.red))
|
|
||||||
|
|
||||||
if node.left is self.nil:
|
|
||||||
s += sprintf("L%d [label=\"-\", %s];\n", id(node), c(False))
|
|
||||||
s += sprintf("%d -> L%d [label=L];\n", id(node), id(node))
|
|
||||||
else:
|
|
||||||
s += sprintf("%d -> %d [label=L];\n", id(node), id(node.left))
|
|
||||||
if node.right is self.nil:
|
|
||||||
s += sprintf("R%d [label=\"-\", %s];\n", id(node), c(False))
|
|
||||||
s += sprintf("%d -> R%d [label=R];\n", id(node), id(node))
|
|
||||||
else:
|
|
||||||
s += sprintf("%d -> %d [label=R];\n", id(node), id(node.right))
|
|
||||||
s += self.__render_dot_node(node.left, max_depth-1)
|
|
||||||
s += self.__render_dot_node(node.right, max_depth-1)
|
|
||||||
return s
|
|
||||||
|
|
||||||
def render_dot(self, title = "RBTree"):
|
|
||||||
"""Render the entire RBTree as a dot graph"""
|
|
||||||
return ("digraph rbtree {\n"
|
|
||||||
+ self.__render_dot_node(self.root.left)
|
|
||||||
+ "}\n");
|
|
||||||
|
|
||||||
def render_dot_live(self, title = "RBTree"):
|
|
||||||
"""Render the entire RBTree as a dot graph, live GTK view"""
|
|
||||||
import gtk
|
|
||||||
import gtk.gdk
|
|
||||||
sys.path.append("/usr/share/xdot")
|
|
||||||
import xdot
|
|
||||||
xdot.Pen.highlighted = lambda pen: pen
|
|
||||||
s = ("digraph rbtree {\n"
|
|
||||||
+ self.__render_dot_node(self.root)
|
|
||||||
+ "}\n");
|
|
||||||
window = xdot.DotWindow()
|
|
||||||
window.set_dotcode(s)
|
|
||||||
window.set_title(title + " - any key to close")
|
|
||||||
window.connect('destroy', gtk.main_quit)
|
|
||||||
def quit(widget, event):
|
|
||||||
if not event.is_modifier:
|
|
||||||
window.destroy()
|
|
||||||
gtk.main_quit()
|
|
||||||
window.widget.connect('key-press-event', quit)
|
|
||||||
gtk.main()
|
|
||||||
|
|
||||||
# Walking, searching
|
|
||||||
def __iter__(self):
|
|
||||||
return self.inorder(self.root.left)
|
|
||||||
|
|
||||||
def inorder(self, x = None):
|
|
||||||
"""Generator that performs an inorder walk for the tree
|
|
||||||
starting at RBNode x"""
|
|
||||||
if x is None:
|
|
||||||
x = self.root.left
|
|
||||||
while x.left is not self.nil:
|
|
||||||
x = x.left
|
|
||||||
while x is not self.nil:
|
|
||||||
yield x
|
|
||||||
x = self.__successor(x)
|
|
||||||
|
|
||||||
def __find_all(self, start, end, x):
|
|
||||||
"""Find node with the specified (start,end) key.
|
|
||||||
Also returns the largest node less than or equal to key,
|
|
||||||
and the smallest node greater or equal to than key."""
|
|
||||||
if x is None:
|
|
||||||
x = self.root.left
|
|
||||||
largest = self.nil
|
|
||||||
smallest = self.nil
|
|
||||||
while x is not self.nil:
|
|
||||||
if start < x.start:
|
|
||||||
smallest = x
|
|
||||||
x = x.left # start <
|
|
||||||
elif start == x.start:
|
|
||||||
if end < x.end:
|
|
||||||
smallest = x
|
|
||||||
x = x.left # start =, end <
|
|
||||||
elif end == x.end: # found it
|
|
||||||
smallest = x
|
|
||||||
largest = x
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
largest = x
|
|
||||||
x = x.right # start =, end >
|
|
||||||
else:
|
|
||||||
largest = x
|
|
||||||
x = x.right # start >
|
|
||||||
return (x, smallest, largest)
|
|
||||||
|
|
||||||
def find(self, start, end, x = None):
|
|
||||||
"""Find node with the key == (start,end), or None"""
|
|
||||||
y = self.__find_all(start, end, x)[1]
|
|
||||||
return y if y is not self.nil else None
|
|
||||||
|
|
||||||
def find_right(self, start, end, x = None):
|
|
||||||
"""Find node with the smallest key >= (start,end), or None"""
|
|
||||||
y = self.__find_all(start, end, x)[1]
|
|
||||||
return y if y is not self.nil else None
|
|
||||||
|
|
||||||
def find_left(self, start, end, x = None):
|
|
||||||
"""Find node with the largest key <= (start,end), or None"""
|
|
||||||
y = self.__find_all(start, end, x)[2]
|
|
||||||
return y if y is not self.nil else None
|
|
||||||
|
|
||||||
# Intersections
|
|
||||||
def intersect(self, start, end):
|
|
||||||
"""Generator that returns nodes that overlap the given
|
|
||||||
(start,end) range, for the tree rooted at RBNode x.
|
|
||||||
|
|
||||||
NOTE: this assumes non-overlapping intervals."""
|
|
||||||
# Start with the leftmost node before the starting point
|
|
||||||
n = self.find_left(start, start)
|
|
||||||
# If we didn't find one, look for the leftmode node before the
|
|
||||||
# ending point instead.
|
|
||||||
if n is None:
|
|
||||||
n = self.find_left(end, end)
|
|
||||||
# If we still didn't find it, there are no intervals that intersect.
|
|
||||||
if n is None:
|
|
||||||
return none
|
|
||||||
|
|
||||||
# Now yield this node and all successors until their endpoints
|
|
||||||
|
|
||||||
if False:
|
|
||||||
yield
|
|
||||||
return
|
|
@@ -12,7 +12,6 @@ stop=
|
|||||||
verbosity=2
|
verbosity=2
|
||||||
#tests=tests/test_cmdline.py
|
#tests=tests/test_cmdline.py
|
||||||
#tests=tests/test_layout.py
|
#tests=tests/test_layout.py
|
||||||
#tests=tests/test_rbtree.py
|
|
||||||
tests=tests/test_interval.py
|
tests=tests/test_interval.py
|
||||||
#tests=tests/test_client.py
|
#tests=tests/test_client.py
|
||||||
#tests=tests/test_timestamper.py
|
#tests=tests/test_timestamper.py
|
||||||
|
73
tests/renderdot.py
Normal file
73
tests/renderdot.py
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
import sys
|
||||||
|
|
||||||
|
class Renderer(object):
|
||||||
|
|
||||||
|
def __init__(self, getleft, getright,
|
||||||
|
getred, getstart, getend, nil):
|
||||||
|
self.getleft = getleft
|
||||||
|
self.getright = getright
|
||||||
|
self.getred = getred
|
||||||
|
self.getstart = getstart
|
||||||
|
self.getend = getend
|
||||||
|
self.nil = nil
|
||||||
|
|
||||||
|
# Rendering
|
||||||
|
def __render_dot_node(self, node, max_depth = 20):
|
||||||
|
from nilmdb.printf import sprintf
|
||||||
|
"""Render a single node and its children into a dot graph fragment"""
|
||||||
|
if max_depth == 0:
|
||||||
|
return ""
|
||||||
|
if node is self.nil:
|
||||||
|
return ""
|
||||||
|
def c(red):
|
||||||
|
if red:
|
||||||
|
return 'color="#ff0000", style=filled, fillcolor="#ffc0c0"'
|
||||||
|
else:
|
||||||
|
return 'color="#000000", style=filled, fillcolor="#c0c0c0"'
|
||||||
|
s = sprintf("%d [label=\"%g\\n%g\", %s];\n",
|
||||||
|
id(node),
|
||||||
|
self.getstart(node), self.getend(node),
|
||||||
|
c(self.getred(node)))
|
||||||
|
|
||||||
|
if self.getleft(node) is self.nil:
|
||||||
|
s += sprintf("L%d [label=\"-\", %s];\n", id(node), c(False))
|
||||||
|
s += sprintf("%d -> L%d [label=L];\n", id(node), id(node))
|
||||||
|
else:
|
||||||
|
s += sprintf("%d -> %d [label=L];\n",
|
||||||
|
id(node),id(self.getleft(node)))
|
||||||
|
if self.getright(node) is self.nil:
|
||||||
|
s += sprintf("R%d [label=\"-\", %s];\n", id(node), c(False))
|
||||||
|
s += sprintf("%d -> R%d [label=R];\n", id(node), id(node))
|
||||||
|
else:
|
||||||
|
s += sprintf("%d -> %d [label=R];\n",
|
||||||
|
id(node), id(self.getright(node)))
|
||||||
|
s += self.__render_dot_node(self.getleft(node), max_depth-1)
|
||||||
|
s += self.__render_dot_node(self.getright(node), max_depth-1)
|
||||||
|
return s
|
||||||
|
|
||||||
|
def render_dot(self, rootnode, title = "Tree"):
|
||||||
|
"""Render the entire tree as a dot graph"""
|
||||||
|
return ("digraph rbtree {\n"
|
||||||
|
+ self.__render_dot_node(rootnode)
|
||||||
|
+ "}\n");
|
||||||
|
|
||||||
|
def render_dot_live(self, rootnode, title = "Tree"):
|
||||||
|
"""Render the entiretree as a dot graph, live GTK view"""
|
||||||
|
import gtk
|
||||||
|
import gtk.gdk
|
||||||
|
sys.path.append("/usr/share/xdot")
|
||||||
|
import xdot
|
||||||
|
xdot.Pen.highlighted = lambda pen: pen
|
||||||
|
s = ("digraph rbtree {\n"
|
||||||
|
+ self.__render_dot_node(rootnode)
|
||||||
|
+ "}\n");
|
||||||
|
window = xdot.DotWindow()
|
||||||
|
window.set_dotcode(s)
|
||||||
|
window.set_title(title + " - any key to close")
|
||||||
|
window.connect('destroy', gtk.main_quit)
|
||||||
|
def quit(widget, event):
|
||||||
|
if not event.is_modifier:
|
||||||
|
window.destroy()
|
||||||
|
gtk.main_quit()
|
||||||
|
window.widget.connect('key-press-event', quit)
|
||||||
|
gtk.main()
|
@@ -20,7 +20,6 @@ def makeset(string):
|
|||||||
[ = interval start
|
[ = interval start
|
||||||
| = interval end + adjacent start
|
| = interval end + adjacent start
|
||||||
] = interval end
|
] = interval end
|
||||||
. = zero-width interval (identical start and end)
|
|
||||||
anything else is ignored
|
anything else is ignored
|
||||||
"""
|
"""
|
||||||
iset = IntervalSet()
|
iset = IntervalSet()
|
||||||
@@ -34,8 +33,6 @@ def makeset(string):
|
|||||||
elif (c == "]"):
|
elif (c == "]"):
|
||||||
iset += Interval(start, day)
|
iset += Interval(start, day)
|
||||||
del start
|
del start
|
||||||
elif (c == "."):
|
|
||||||
iset += Interval(day, day)
|
|
||||||
return iset
|
return iset
|
||||||
|
|
||||||
class TestInterval:
|
class TestInterval:
|
||||||
@@ -71,7 +68,7 @@ class TestInterval:
|
|||||||
assert(Interval(d1, d3) < Interval(d2, d3))
|
assert(Interval(d1, d3) < Interval(d2, d3))
|
||||||
assert(Interval(d2, d2) > Interval(d1, d3))
|
assert(Interval(d2, d2) > Interval(d1, d3))
|
||||||
assert(Interval(d3, d3) == Interval(d3, d3))
|
assert(Interval(d3, d3) == Interval(d3, d3))
|
||||||
with assert_raises(TypeError): # was AttributeError, that's wrong
|
with assert_raises(AttributeError):
|
||||||
x = (i == 123)
|
x = (i == 123)
|
||||||
|
|
||||||
# subset
|
# subset
|
||||||
@@ -185,7 +182,7 @@ class TestInterval:
|
|||||||
|
|
||||||
def test_intervalset_intersect(self):
|
def test_intervalset_intersect(self):
|
||||||
# Test intersection (&)
|
# Test intersection (&)
|
||||||
with assert_raises(TypeError): # was AttributeError
|
with assert_raises(AttributeError):
|
||||||
x = makeset("[--]") & 1234
|
x = makeset("[--]") & 1234
|
||||||
|
|
||||||
assert(makeset("[---------]") &
|
assert(makeset("[---------]") &
|
||||||
@@ -200,18 +197,10 @@ class TestInterval:
|
|||||||
makeset(" [-----] ") ==
|
makeset(" [-----] ") ==
|
||||||
makeset(" [--] "))
|
makeset(" [--] "))
|
||||||
|
|
||||||
assert(makeset(" [--] [--]") &
|
|
||||||
makeset(" [------] ") ==
|
|
||||||
makeset(" [-] [-] "))
|
|
||||||
|
|
||||||
assert(makeset(" [---]") &
|
assert(makeset(" [---]") &
|
||||||
makeset(" [--] ") ==
|
makeset(" [--] ") ==
|
||||||
makeset(" "))
|
makeset(" "))
|
||||||
|
|
||||||
assert(makeset(" [---]") &
|
|
||||||
makeset(" [----] ") ==
|
|
||||||
makeset(" . "))
|
|
||||||
|
|
||||||
assert(makeset(" [-|---]") &
|
assert(makeset(" [-|---]") &
|
||||||
makeset(" [-----|-] ") ==
|
makeset(" [-----|-] ") ==
|
||||||
makeset(" [----] "))
|
makeset(" [----] "))
|
||||||
@@ -222,9 +211,8 @@ class TestInterval:
|
|||||||
|
|
||||||
assert(makeset(" [----][--]") &
|
assert(makeset(" [----][--]") &
|
||||||
makeset("[-] [--] []") ==
|
makeset("[-] [--] []") ==
|
||||||
makeset(" [] [-]. []"))
|
makeset(" [] [-] []"))
|
||||||
|
|
||||||
class TestIntervalDB:
|
|
||||||
def test_dbinterval(self):
|
def test_dbinterval(self):
|
||||||
# Test DBInterval class
|
# Test DBInterval class
|
||||||
i = DBInterval(100, 200, 100, 200, 10000, 20000)
|
i = DBInterval(100, 200, 100, 200, 10000, 20000)
|
||||||
@@ -267,9 +255,8 @@ class TestIntervalDB:
|
|||||||
for i in IntervalSet(iseta.intersection(Interval(125,250))):
|
for i in IntervalSet(iseta.intersection(Interval(125,250))):
|
||||||
assert(isinstance(i, DBInterval))
|
assert(isinstance(i, DBInterval))
|
||||||
|
|
||||||
class TestIntervalTree:
|
class TestIntervalShape:
|
||||||
|
def test_interval_shape(self):
|
||||||
def test_interval_tree(self):
|
|
||||||
import random
|
import random
|
||||||
random.seed(1234)
|
random.seed(1234)
|
||||||
|
|
||||||
@@ -280,34 +267,49 @@ class TestIntervalTree:
|
|||||||
interval = Interval(i, i+1)
|
interval = Interval(i, i+1)
|
||||||
iset += interval
|
iset += interval
|
||||||
|
|
||||||
# remove about half of them
|
# Plot it
|
||||||
for i in random.sample(xrange(j),j):
|
import renderdot
|
||||||
if random.randint(0,1):
|
r = renderdot.Renderer(lambda node: node.cleft,
|
||||||
iset -= Interval(i, i+1)
|
lambda node: node.cright,
|
||||||
|
lambda node: False,
|
||||||
|
lambda node: node.start,
|
||||||
|
lambda node: node.end,
|
||||||
|
iset.tree.emptynode())
|
||||||
|
r.render_dot_live(iset.tree.rootnode(), "Random")
|
||||||
|
|
||||||
# try removing an interval that doesn't exist
|
# make a set of 500 intervals, inserted in order
|
||||||
with assert_raises(IntervalError):
|
iset = IntervalSet()
|
||||||
iset -= Interval(1234,5678)
|
j = 500
|
||||||
|
for i in xrange(j):
|
||||||
|
interval = Interval(i, i+1)
|
||||||
|
iset += interval
|
||||||
|
|
||||||
# show the graph
|
# Plot it
|
||||||
if False:
|
import renderdot
|
||||||
iset.tree.render_dot_live()
|
r = renderdot.Renderer(lambda node: node.cleft,
|
||||||
|
lambda node: node.cright,
|
||||||
|
lambda node: False,
|
||||||
|
lambda node: node.start,
|
||||||
|
lambda node: node.end,
|
||||||
|
iset.tree.emptynode())
|
||||||
|
r.render_dot_live(iset.tree.rootnode(), "In-order")
|
||||||
|
|
||||||
|
assert(False)
|
||||||
|
|
||||||
class TestIntervalSpeed:
|
class TestIntervalSpeed:
|
||||||
@unittest.skip("this is slow")
|
#@unittest.skip("this is slow")
|
||||||
def test_interval_speed(self):
|
def test_interval_speed(self):
|
||||||
import yappi
|
import yappi
|
||||||
import time
|
import time
|
||||||
import aplotter
|
import aplotter
|
||||||
import random
|
|
||||||
|
|
||||||
print
|
print
|
||||||
yappi.start()
|
yappi.start()
|
||||||
speeds = {}
|
speeds = {}
|
||||||
for j in [ 2**x for x in range(5,18) ]:
|
for j in [ 2**x for x in range(5,22) ]:
|
||||||
start = time.time()
|
start = time.time()
|
||||||
iset = IntervalSet()
|
iset = IntervalSet()
|
||||||
for i in random.sample(xrange(j),j):
|
for i in xrange(j):
|
||||||
interval = Interval(i, i+1)
|
interval = Interval(i, i+1)
|
||||||
iset += interval
|
iset += interval
|
||||||
speed = (time.time() - start) * 1000000.0
|
speed = (time.time() - start) * 1000000.0
|
||||||
@@ -316,4 +318,3 @@ class TestIntervalSpeed:
|
|||||||
aplotter.plot(speeds.keys(), speeds.values(), plot_slope=True)
|
aplotter.plot(speeds.keys(), speeds.values(), plot_slope=True)
|
||||||
yappi.stop()
|
yappi.stop()
|
||||||
yappi.print_stats(sort_type=yappi.SORTTYPE_TTOT, limit=10)
|
yappi.print_stats(sort_type=yappi.SORTTYPE_TTOT, limit=10)
|
||||||
|
|
||||||
|
@@ -1,75 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
import nilmdb
|
|
||||||
from nilmdb.printf import *
|
|
||||||
|
|
||||||
from nose.tools import *
|
|
||||||
from nose.tools import assert_raises
|
|
||||||
|
|
||||||
from nilmdb.rbtree import RBTree, RBNode
|
|
||||||
|
|
||||||
from test_helpers import *
|
|
||||||
import unittest
|
|
||||||
|
|
||||||
render = False
|
|
||||||
|
|
||||||
class TestRBTree:
|
|
||||||
def test_rbtree(self):
|
|
||||||
rb = RBTree()
|
|
||||||
rb.insert(RBNode(None, 10000, 10001))
|
|
||||||
rb.insert(RBNode(None, 10004, 10007))
|
|
||||||
rb.insert(RBNode(None, 10001, 10002))
|
|
||||||
s = rb.render_dot()
|
|
||||||
# There was a typo that gave the RBTree a loop in this case.
|
|
||||||
# Verify that the dot isn't too big.
|
|
||||||
assert(len(s.splitlines()) < 30)
|
|
||||||
|
|
||||||
def test_rbtree_big(self):
|
|
||||||
import random
|
|
||||||
random.seed(1234)
|
|
||||||
|
|
||||||
# make a set of 500 intervals, inserted in order
|
|
||||||
rb = RBTree()
|
|
||||||
j = 500
|
|
||||||
for i in xrange(j):
|
|
||||||
rb.insert(RBNode(None, i, i+1))
|
|
||||||
|
|
||||||
# show the graph
|
|
||||||
if render:
|
|
||||||
rb.render_dot_live("in-order insert")
|
|
||||||
|
|
||||||
# remove about half of them
|
|
||||||
for i in random.sample(xrange(j),j):
|
|
||||||
if random.randint(0,1):
|
|
||||||
rb.delete(rb.find(i, i+1))
|
|
||||||
|
|
||||||
# show the graph
|
|
||||||
if render:
|
|
||||||
rb.render_dot_live("in-order insert, random delete")
|
|
||||||
|
|
||||||
# make a set of 500 intervals, inserted at random
|
|
||||||
rb = RBTree()
|
|
||||||
j = 500
|
|
||||||
for i in random.sample(xrange(j),j):
|
|
||||||
rb.insert(RBNode(None, i, i+1))
|
|
||||||
|
|
||||||
# show the graph
|
|
||||||
if render:
|
|
||||||
rb.render_dot_live("random insert")
|
|
||||||
|
|
||||||
# remove about half of them
|
|
||||||
for i in random.sample(xrange(j),j):
|
|
||||||
if random.randint(0,1):
|
|
||||||
rb.delete(rb.find(i, i+1))
|
|
||||||
|
|
||||||
# show the graph
|
|
||||||
if render:
|
|
||||||
rb.render_dot_live("random insert, random delete")
|
|
||||||
|
|
||||||
# in-order insert of 250 more
|
|
||||||
for i in xrange(250):
|
|
||||||
rb.insert(RBNode(None, i+500, i+501))
|
|
||||||
|
|
||||||
# show the graph
|
|
||||||
if render:
|
|
||||||
rb.render_dot_live("random insert, random delete, in-order insert")
|
|
@@ -1,54 +0,0 @@
|
|||||||
nosetests
|
|
||||||
|
|
||||||
32: 386 μs (12.0625 μs each)
|
|
||||||
64: 672.102 μs (10.5016 μs each)
|
|
||||||
128: 1510.86 μs (11.8036 μs each)
|
|
||||||
256: 2782.11 μs (10.8676 μs each)
|
|
||||||
512: 5591.87 μs (10.9216 μs each)
|
|
||||||
1024: 12812.1 μs (12.5119 μs each)
|
|
||||||
2048: 21835.1 μs (10.6617 μs each)
|
|
||||||
4096: 46059.1 μs (11.2449 μs each)
|
|
||||||
8192: 114127 μs (13.9315 μs each)
|
|
||||||
16384: 181217 μs (11.0606 μs each)
|
|
||||||
32768: 419649 μs (12.8067 μs each)
|
|
||||||
65536: 804320 μs (12.2729 μs each)
|
|
||||||
131072: 1.73534e+06 μs (13.2396 μs each)
|
|
||||||
262144: 3.74451e+06 μs (14.2842 μs each)
|
|
||||||
524288: 8.8694e+06 μs (16.917 μs each)
|
|
||||||
1048576: 1.69993e+07 μs (16.2118 μs each)
|
|
||||||
2097152: 3.29387e+07 μs (15.7064 μs each)
|
|
||||||
|
|
|
||||||
+3.29387e+07 *
|
|
||||||
| ----
|
|
||||||
| -----
|
|
||||||
| ----
|
|
||||||
| -----
|
|
||||||
| -----
|
|
||||||
| ----
|
|
||||||
| -----
|
|
||||||
| -----
|
|
||||||
| ----
|
|
||||||
| -----
|
|
||||||
| ----
|
|
||||||
| -----
|
|
||||||
| ---
|
|
||||||
| ---
|
|
||||||
| ---
|
|
||||||
| -------
|
|
||||||
---+386---------------------------------------------------------------------+---
|
|
||||||
+32 +2.09715e+06
|
|
||||||
|
|
||||||
name #n tsub ttot tavg
|
|
||||||
..vl/lees/bucket/nilm/nilmdb/nilmdb/interval.py.__iadd__:184 4194272 10.025323 30.262723 0.000007
|
|
||||||
..evl/lees/bucket/nilm/nilmdb/nilmdb/interval.py.__init__:27 4194272 24.715377 24.715377 0.000006
|
|
||||||
../lees/bucket/nilm/nilmdb/nilmdb/interval.py.intersects:239 4194272 6.705053 12.577620 0.000003
|
|
||||||
..im/devl/lees/bucket/nilm/nilmdb/tests/aplotter.py.plot:404 1 0.000048 0.001412 0.001412
|
|
||||||
../lees/bucket/nilm/nilmdb/tests/aplotter.py.plot_double:311 1 0.000106 0.001346 0.001346
|
|
||||||
..vl/lees/bucket/nilm/nilmdb/tests/aplotter.py.plot_data:201 1 0.000098 0.000672 0.000672
|
|
||||||
..vl/lees/bucket/nilm/nilmdb/tests/aplotter.py.plot_line:241 16 0.000298 0.000496 0.000031
|
|
||||||
..jim/devl/lees/bucket/nilm/nilmdb/nilmdb/printf.py.printf:4 17 0.000252 0.000334 0.000020
|
|
||||||
..vl/lees/bucket/nilm/nilmdb/tests/aplotter.py.transposed:39 1 0.000229 0.000235 0.000235
|
|
||||||
..vl/lees/bucket/nilm/nilmdb/tests/aplotter.py.y_reversed:45 1 0.000151 0.000174 0.000174
|
|
||||||
|
|
||||||
name tid fname ttot scnt
|
|
||||||
_MainThread 47269783682784 ..b/python2.7/threading.py.setprofile:88 64.746000 1
|
|
Reference in New Issue
Block a user