2012-04-11 18:05:27 -04:00
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
2012-03-27 19:19:08 -04:00
|
|
|
import nilmdb
|
2012-12-31 15:52:28 -05:00
|
|
|
from nilmdb.utils.printf import *
|
2013-01-30 19:03:42 -05:00
|
|
|
from nilmdb.utils import datetime_tz
|
2012-03-27 19:19:08 -04:00
|
|
|
|
|
|
|
from nose.tools import *
|
|
|
|
from nose.tools import assert_raises
|
|
|
|
import itertools
|
|
|
|
|
2013-01-28 19:04:52 -05:00
|
|
|
from nilmdb.server.interval import (Interval, DBInterval,
|
|
|
|
IntervalSet, IntervalError)
|
2012-03-28 15:25:02 -04:00
|
|
|
|
2013-01-05 15:00:34 -05:00
|
|
|
from testutil.helpers import *
|
2012-04-11 18:05:27 -04:00
|
|
|
import unittest
|
2012-03-29 17:43:05 -04:00
|
|
|
|
2012-11-29 00:42:50 -05:00
|
|
|
# set to False to skip live renders
|
|
|
|
do_live_renders = False
|
|
|
|
def render(iset, description = "", live = True):
|
2013-01-05 15:00:34 -05:00
|
|
|
import testutil.renderdot as renderdot
|
2012-11-29 00:42:50 -05:00
|
|
|
r = renderdot.RBTreeRenderer(iset.tree)
|
|
|
|
return r.render(description, live and do_live_renders)
|
|
|
|
|
2012-03-29 17:43:05 -04:00
|
|
|
def makeset(string):
|
2012-03-27 19:19:08 -04:00
|
|
|
"""Build an IntervalSet from a string, for testing purposes
|
|
|
|
|
2012-03-28 15:25:02 -04:00
|
|
|
Each character is 1 second
|
2012-03-27 19:19:08 -04:00
|
|
|
[ = interval start
|
2012-11-29 15:35:11 -05:00
|
|
|
| = interval end + next start
|
2012-03-27 19:19:08 -04:00
|
|
|
] = interval end
|
2012-11-08 18:08:01 -05:00
|
|
|
. = zero-width interval (identical start and end)
|
2012-03-27 19:19:08 -04:00
|
|
|
anything else is ignored
|
|
|
|
"""
|
|
|
|
iset = IntervalSet()
|
|
|
|
for i, c in enumerate(string):
|
2012-03-28 15:25:02 -04:00
|
|
|
day = i + 10000
|
2012-03-27 19:19:08 -04:00
|
|
|
if (c == "["):
|
|
|
|
start = day
|
|
|
|
elif (c == "|"):
|
|
|
|
iset += Interval(start, day)
|
|
|
|
start = day
|
2012-11-29 15:35:11 -05:00
|
|
|
elif (c == ")"):
|
2012-03-27 19:19:08 -04:00
|
|
|
iset += Interval(start, day)
|
|
|
|
del start
|
2012-11-08 18:08:01 -05:00
|
|
|
elif (c == "."):
|
|
|
|
iset += Interval(day, day)
|
2012-03-27 19:19:08 -04:00
|
|
|
return iset
|
|
|
|
|
2012-03-29 17:43:05 -04:00
|
|
|
class TestInterval:
|
|
|
|
def test_interval(self):
|
|
|
|
# Test Interval class
|
2012-05-07 18:32:02 -04:00
|
|
|
os.environ['TZ'] = "America/New_York"
|
|
|
|
datetime_tz._localtz = None
|
2012-03-29 17:43:05 -04:00
|
|
|
(d1, d2, d3) = [ datetime_tz.datetime_tz.smartparse(x).totimestamp()
|
|
|
|
for x in [ "03/24/2012", "03/25/2012", "03/26/2012" ] ]
|
|
|
|
|
|
|
|
# basic construction
|
2013-02-21 14:06:40 -05:00
|
|
|
i = Interval(d1, d2)
|
2012-03-29 17:43:05 -04:00
|
|
|
i = Interval(d1, d3)
|
2012-05-25 17:45:05 -04:00
|
|
|
eq_(i.start, d1)
|
|
|
|
eq_(i.end, d3)
|
2012-03-29 17:43:05 -04:00
|
|
|
|
|
|
|
# assignment is allowed, but not verified
|
|
|
|
i.start = d2
|
|
|
|
#with assert_raises(IntervalError):
|
|
|
|
# i.end = d1
|
|
|
|
i.start = d1
|
|
|
|
i.end = d2
|
|
|
|
|
|
|
|
# end before start
|
|
|
|
with assert_raises(IntervalError):
|
|
|
|
i = Interval(d3, d1)
|
|
|
|
|
|
|
|
# compare
|
|
|
|
assert(Interval(d1, d2) == Interval(d1, d2))
|
|
|
|
assert(Interval(d1, d2) < Interval(d1, d3))
|
|
|
|
assert(Interval(d1, d3) > Interval(d1, d2))
|
|
|
|
assert(Interval(d1, d2) < Interval(d2, d3))
|
|
|
|
assert(Interval(d1, d3) < Interval(d2, d3))
|
2013-02-21 14:06:40 -05:00
|
|
|
assert(Interval(d2, d2+0.01) > Interval(d1, d3))
|
|
|
|
assert(Interval(d3, d3+0.01) == Interval(d3, d3+0.01))
|
2012-11-29 15:13:09 -05:00
|
|
|
#with assert_raises(TypeError): # was AttributeError, that's wrong
|
|
|
|
# x = (i == 123)
|
2012-03-29 17:43:05 -04:00
|
|
|
|
|
|
|
# subset
|
2012-11-29 00:42:50 -05:00
|
|
|
eq_(Interval(d1, d3).subset(d1, d2), Interval(d1, d2))
|
2012-03-29 17:43:05 -04:00
|
|
|
with assert_raises(IntervalError):
|
|
|
|
x = Interval(d2, d3).subset(d1, d2)
|
|
|
|
|
2012-04-12 17:05:54 -04:00
|
|
|
# big integers and floats
|
|
|
|
x = Interval(5000111222, 6000111222)
|
2012-11-29 15:35:11 -05:00
|
|
|
eq_(str(x), "[5000111222.0 -> 6000111222.0)")
|
2012-04-12 17:05:54 -04:00
|
|
|
x = Interval(123.45, 234.56)
|
2012-11-29 15:35:11 -05:00
|
|
|
eq_(str(x), "[123.45 -> 234.56)")
|
2012-04-12 17:05:54 -04:00
|
|
|
|
2012-03-29 17:43:05 -04:00
|
|
|
# misc
|
|
|
|
i = Interval(d1, d2)
|
|
|
|
eq_(repr(i), repr(eval(repr(i))))
|
2012-11-29 15:35:11 -05:00
|
|
|
eq_(str(i), "[1332561600.0 -> 1332648000.0)")
|
2012-03-29 17:43:05 -04:00
|
|
|
|
|
|
|
def test_interval_intersect(self):
|
|
|
|
# Test Interval intersections
|
|
|
|
dates = [ 100, 200, 300, 400 ]
|
|
|
|
perm = list(itertools.permutations(dates, 2))
|
|
|
|
prod = list(itertools.product(perm, perm))
|
|
|
|
should_intersect = {
|
|
|
|
False: [4, 5, 8, 20, 48, 56, 60, 96, 97, 100],
|
|
|
|
True: [0, 1, 2, 12, 13, 14, 16, 17, 24, 25, 26, 28, 29,
|
|
|
|
32, 49, 50, 52, 53, 61, 62, 64, 65, 68, 98, 101, 104]
|
|
|
|
}
|
|
|
|
for i,((a,b),(c,d)) in enumerate(prod):
|
|
|
|
try:
|
|
|
|
i1 = Interval(a, b)
|
|
|
|
i2 = Interval(c, d)
|
2012-05-25 17:45:05 -04:00
|
|
|
eq_(i1.intersects(i2), i2.intersects(i1))
|
|
|
|
in_(i, should_intersect[i1.intersects(i2)])
|
2012-03-29 17:43:05 -04:00
|
|
|
except IntervalError:
|
|
|
|
assert(i not in should_intersect[True] and
|
|
|
|
i not in should_intersect[False])
|
2012-11-29 15:13:09 -05:00
|
|
|
with assert_raises(TypeError):
|
2012-03-29 17:43:05 -04:00
|
|
|
x = i1.intersects(1234)
|
|
|
|
|
|
|
|
def test_intervalset_construct(self):
|
|
|
|
# Test IntervalSet construction
|
|
|
|
dates = [ 100, 200, 300, 400 ]
|
|
|
|
|
|
|
|
a = Interval(dates[0], dates[1])
|
|
|
|
b = Interval(dates[1], dates[2])
|
|
|
|
c = Interval(dates[0], dates[2])
|
|
|
|
d = Interval(dates[2], dates[3])
|
|
|
|
|
|
|
|
iseta = IntervalSet(a)
|
|
|
|
isetb = IntervalSet([a, b])
|
|
|
|
isetc = IntervalSet([a])
|
|
|
|
ne_(iseta, isetb)
|
|
|
|
eq_(iseta, isetc)
|
|
|
|
with assert_raises(TypeError):
|
|
|
|
x = iseta != 3
|
|
|
|
ne_(IntervalSet(a), IntervalSet(b))
|
|
|
|
|
2012-12-11 23:31:55 -05:00
|
|
|
# Note that assignment makes a new reference (not a copy)
|
|
|
|
isetd = IntervalSet(isetb)
|
|
|
|
isete = isetd
|
|
|
|
eq_(isetd, isetb)
|
|
|
|
eq_(isetd, isete)
|
|
|
|
isetd -= a
|
|
|
|
ne_(isetd, isetb)
|
|
|
|
eq_(isetd, isete)
|
|
|
|
|
2012-04-12 18:39:16 -04:00
|
|
|
# test iterator
|
|
|
|
for interval in iseta:
|
|
|
|
pass
|
|
|
|
|
2012-03-29 17:43:05 -04:00
|
|
|
# overlap
|
|
|
|
with assert_raises(IntervalError):
|
|
|
|
x = IntervalSet([a, b, c])
|
|
|
|
|
|
|
|
# bad types
|
2012-04-12 18:39:16 -04:00
|
|
|
with assert_raises(Exception):
|
2012-03-29 17:43:05 -04:00
|
|
|
x = IntervalSet([1, 2])
|
|
|
|
|
|
|
|
iset = IntervalSet(isetb) # test iterator
|
2012-05-25 17:45:05 -04:00
|
|
|
eq_(iset, isetb)
|
|
|
|
eq_(len(iset), 2)
|
|
|
|
eq_(len(IntervalSet()), 0)
|
2012-03-29 17:43:05 -04:00
|
|
|
|
|
|
|
# Test adding
|
|
|
|
iset = IntervalSet(a)
|
|
|
|
iset += IntervalSet(b)
|
2012-05-25 17:45:05 -04:00
|
|
|
eq_(iset, IntervalSet([a, b]))
|
2012-12-11 23:31:55 -05:00
|
|
|
|
2012-03-29 17:43:05 -04:00
|
|
|
iset = IntervalSet(a)
|
|
|
|
iset += b
|
2012-05-25 17:45:05 -04:00
|
|
|
eq_(iset, IntervalSet([a, b]))
|
2012-12-11 23:31:55 -05:00
|
|
|
|
|
|
|
iset = IntervalSet(a)
|
2012-12-12 19:25:27 -05:00
|
|
|
iset.iadd_nocheck(b)
|
2012-12-11 23:31:55 -05:00
|
|
|
eq_(iset, IntervalSet([a, b]))
|
|
|
|
|
2012-03-29 17:43:05 -04:00
|
|
|
iset = IntervalSet(a) + IntervalSet(b)
|
2012-05-25 17:45:05 -04:00
|
|
|
eq_(iset, IntervalSet([a, b]))
|
2012-12-11 23:31:55 -05:00
|
|
|
|
2012-03-29 17:43:05 -04:00
|
|
|
iset = IntervalSet(b) + a
|
2012-05-25 17:45:05 -04:00
|
|
|
eq_(iset, IntervalSet([a, b]))
|
2012-03-29 17:43:05 -04:00
|
|
|
|
|
|
|
# A set consisting of [0-1],[1-2] should match a set consisting of [0-2]
|
2012-05-25 17:45:05 -04:00
|
|
|
eq_(IntervalSet([a,b]), IntervalSet([c]))
|
2012-03-29 17:43:05 -04:00
|
|
|
# Etc
|
2012-05-25 17:45:05 -04:00
|
|
|
ne_(IntervalSet([a,d]), IntervalSet([c]))
|
|
|
|
ne_(IntervalSet([c]), IntervalSet([a,d]))
|
|
|
|
ne_(IntervalSet([c,d]), IntervalSet([b,d]))
|
2012-03-29 17:43:05 -04:00
|
|
|
|
|
|
|
# misc
|
2012-05-25 17:45:05 -04:00
|
|
|
eq_(repr(iset), repr(eval(repr(iset))))
|
2012-11-29 15:35:11 -05:00
|
|
|
eq_(str(iset), "[[100.0 -> 200.0), [200.0 -> 300.0)]")
|
2012-03-29 17:43:05 -04:00
|
|
|
|
|
|
|
def test_intervalset_geniset(self):
|
|
|
|
# Test basic iset construction
|
2012-11-29 15:35:11 -05:00
|
|
|
eq_(makeset(" [----) "),
|
|
|
|
makeset(" [-|--) "))
|
2012-03-29 17:43:05 -04:00
|
|
|
|
2012-11-29 15:35:11 -05:00
|
|
|
eq_(makeset("[) [--) ") +
|
|
|
|
makeset(" [) [--)"),
|
|
|
|
makeset("[|) [-----)"))
|
2012-03-29 17:43:05 -04:00
|
|
|
|
2012-11-29 15:35:11 -05:00
|
|
|
eq_(makeset(" [-------)"),
|
2012-11-29 00:42:50 -05:00
|
|
|
makeset(" [-|-----|"))
|
2012-03-29 17:43:05 -04:00
|
|
|
|
|
|
|
|
|
|
|
def test_intervalset_intersect(self):
|
|
|
|
# Test intersection (&)
|
2012-11-08 17:56:05 -05:00
|
|
|
with assert_raises(TypeError): # was AttributeError
|
2012-11-29 15:35:11 -05:00
|
|
|
x = makeset("[--)") & 1234
|
2012-03-29 17:43:05 -04:00
|
|
|
|
2012-11-29 00:42:50 -05:00
|
|
|
# Intersection with interval
|
2012-11-29 15:35:11 -05:00
|
|
|
eq_(makeset("[---|---)[)") &
|
|
|
|
list(makeset(" [------) "))[0],
|
|
|
|
makeset(" [-----) "))
|
2012-11-29 00:42:50 -05:00
|
|
|
|
|
|
|
# Intersection with sets
|
2012-11-29 15:35:11 -05:00
|
|
|
eq_(makeset("[---------)") &
|
|
|
|
makeset(" [---) "),
|
|
|
|
makeset(" [---) "))
|
2012-11-29 00:42:50 -05:00
|
|
|
|
2012-11-29 15:35:11 -05:00
|
|
|
eq_(makeset(" [---) ") &
|
|
|
|
makeset("[---------)"),
|
|
|
|
makeset(" [---) "))
|
2012-11-29 00:42:50 -05:00
|
|
|
|
2012-11-29 15:35:11 -05:00
|
|
|
eq_(makeset(" [-----)") &
|
|
|
|
makeset(" [-----) "),
|
|
|
|
makeset(" [--) "))
|
2012-11-29 00:42:50 -05:00
|
|
|
|
2012-11-29 15:35:11 -05:00
|
|
|
eq_(makeset(" [--) [--)") &
|
|
|
|
makeset(" [------) "),
|
|
|
|
makeset(" [-) [-) "))
|
2012-11-29 00:42:50 -05:00
|
|
|
|
2012-11-29 15:35:11 -05:00
|
|
|
eq_(makeset(" [---)") &
|
|
|
|
makeset(" [--) "),
|
2012-11-29 00:42:50 -05:00
|
|
|
makeset(" "))
|
|
|
|
|
2012-11-29 15:35:11 -05:00
|
|
|
eq_(makeset(" [-|---)") &
|
|
|
|
makeset(" [-----|-) "),
|
|
|
|
makeset(" [----) "))
|
2012-11-29 00:42:50 -05:00
|
|
|
|
2012-11-29 15:35:11 -05:00
|
|
|
eq_(makeset(" [-|-) ") &
|
|
|
|
makeset(" [-|--|--) "),
|
|
|
|
makeset(" [---) "))
|
2012-11-29 00:42:50 -05:00
|
|
|
|
|
|
|
# Border cases -- will give different results if intervals are
|
|
|
|
# half open or fully closed. Right now, they are half open,
|
|
|
|
# although that's a little messy since the database intervals
|
|
|
|
# often contain a data point at the endpoint.
|
|
|
|
half_open = True
|
|
|
|
if half_open:
|
2012-11-29 15:35:11 -05:00
|
|
|
eq_(makeset(" [---)") &
|
|
|
|
makeset(" [----) "),
|
2012-11-29 00:42:50 -05:00
|
|
|
makeset(" "))
|
2012-11-29 15:35:11 -05:00
|
|
|
eq_(makeset(" [----)[--)") &
|
|
|
|
makeset("[-) [--) [)"),
|
|
|
|
makeset(" [) [-) [)"))
|
2012-11-29 00:42:50 -05:00
|
|
|
else:
|
2012-11-29 15:35:11 -05:00
|
|
|
eq_(makeset(" [---)") &
|
|
|
|
makeset(" [----) "),
|
2012-11-29 00:42:50 -05:00
|
|
|
makeset(" . "))
|
2012-11-29 15:35:11 -05:00
|
|
|
eq_(makeset(" [----)[--)") &
|
|
|
|
makeset("[-) [--) [)"),
|
|
|
|
makeset(" [) [-). [)"))
|
2012-04-11 18:05:27 -04:00
|
|
|
|
2012-11-08 17:56:05 -05:00
|
|
|
class TestIntervalDB:
|
2012-05-25 17:45:05 -04:00
|
|
|
def test_dbinterval(self):
|
|
|
|
# Test DBInterval class
|
|
|
|
i = DBInterval(100, 200, 100, 200, 10000, 20000)
|
|
|
|
eq_(i.start, 100)
|
|
|
|
eq_(i.end, 200)
|
|
|
|
eq_(i.db_start, 100)
|
|
|
|
eq_(i.db_end, 200)
|
|
|
|
eq_(i.db_startpos, 10000)
|
|
|
|
eq_(i.db_endpos, 20000)
|
|
|
|
eq_(repr(i), repr(eval(repr(i))))
|
|
|
|
|
|
|
|
# end before start
|
|
|
|
with assert_raises(IntervalError):
|
|
|
|
i = DBInterval(200, 100, 100, 200, 10000, 20000)
|
|
|
|
|
|
|
|
# db_start too late
|
|
|
|
with assert_raises(IntervalError):
|
|
|
|
i = DBInterval(100, 200, 150, 200, 10000, 20000)
|
|
|
|
|
|
|
|
# db_end too soon
|
|
|
|
with assert_raises(IntervalError):
|
|
|
|
i = DBInterval(100, 200, 100, 150, 10000, 20000)
|
|
|
|
|
|
|
|
# actual start, end can be a subset
|
|
|
|
a = DBInterval(150, 200, 100, 200, 10000, 20000)
|
|
|
|
b = DBInterval(100, 150, 100, 200, 10000, 20000)
|
2013-02-21 14:06:40 -05:00
|
|
|
c = DBInterval(150, 160, 100, 200, 10000, 20000)
|
2012-05-25 17:45:05 -04:00
|
|
|
|
|
|
|
# Make a set of DBIntervals
|
|
|
|
iseta = IntervalSet([a, b])
|
|
|
|
isetc = IntervalSet(c)
|
|
|
|
assert(iseta.intersects(a))
|
|
|
|
assert(iseta.intersects(b))
|
|
|
|
|
|
|
|
# Test subset
|
|
|
|
with assert_raises(IntervalError):
|
|
|
|
x = a.subset(150, 250)
|
|
|
|
|
|
|
|
# Subset of those IntervalSets should still contain DBIntervals
|
|
|
|
for i in IntervalSet(iseta.intersection(Interval(125,250))):
|
|
|
|
assert(isinstance(i, DBInterval))
|
|
|
|
|
2012-11-13 22:55:37 -05:00
|
|
|
class TestIntervalTree:
|
|
|
|
|
|
|
|
def test_interval_tree(self):
|
|
|
|
import random
|
2012-11-15 13:55:56 -05:00
|
|
|
random.seed(1234)
|
|
|
|
|
2012-11-29 00:42:50 -05:00
|
|
|
# make a set of 100 intervals
|
2012-11-13 22:55:37 -05:00
|
|
|
iset = IntervalSet()
|
2012-11-29 00:42:50 -05:00
|
|
|
j = 100
|
2012-11-13 22:55:37 -05:00
|
|
|
for i in random.sample(xrange(j),j):
|
|
|
|
interval = Interval(i, i+1)
|
|
|
|
iset += interval
|
2012-11-29 00:42:50 -05:00
|
|
|
render(iset, "Random Insertion")
|
2012-11-28 18:34:51 -05:00
|
|
|
|
2012-11-13 22:55:37 -05:00
|
|
|
# remove about half of them
|
|
|
|
for i in random.sample(xrange(j),j):
|
|
|
|
if random.randint(0,1):
|
|
|
|
iset -= Interval(i, i+1)
|
|
|
|
|
2012-11-15 13:55:56 -05:00
|
|
|
# try removing an interval that doesn't exist
|
|
|
|
with assert_raises(IntervalError):
|
|
|
|
iset -= Interval(1234,5678)
|
2012-11-29 00:42:50 -05:00
|
|
|
render(iset, "Random Insertion, deletion")
|
2012-11-15 13:55:56 -05:00
|
|
|
|
2012-11-29 00:42:50 -05:00
|
|
|
# make a set of 100 intervals, inserted in order
|
2012-11-28 18:34:51 -05:00
|
|
|
iset = IntervalSet()
|
2012-11-29 00:42:50 -05:00
|
|
|
j = 100
|
2012-11-28 18:34:51 -05:00
|
|
|
for i in xrange(j):
|
|
|
|
interval = Interval(i, i+1)
|
|
|
|
iset += interval
|
2012-11-29 00:42:50 -05:00
|
|
|
render(iset, "In-order insertion")
|
2012-11-13 22:55:37 -05:00
|
|
|
|
2012-04-11 18:05:27 -04:00
|
|
|
class TestIntervalSpeed:
|
2013-01-05 15:05:42 -05:00
|
|
|
@unittest.skip("this is slow")
|
2012-04-11 18:05:27 -04:00
|
|
|
def test_interval_speed(self):
|
|
|
|
import yappi
|
|
|
|
import time
|
2013-01-05 15:00:34 -05:00
|
|
|
import testutil.aplotter as aplotter
|
2012-11-08 18:50:23 -05:00
|
|
|
import random
|
2012-11-29 01:00:54 -05:00
|
|
|
import math
|
2012-04-11 18:05:27 -04:00
|
|
|
|
|
|
|
print
|
|
|
|
yappi.start()
|
|
|
|
speeds = {}
|
2013-01-05 15:00:34 -05:00
|
|
|
limit = 10 # was 20
|
|
|
|
for j in [ 2**x for x in range(5,limit) ]:
|
2012-04-11 18:05:27 -04:00
|
|
|
start = time.time()
|
|
|
|
iset = IntervalSet()
|
2012-11-08 18:50:23 -05:00
|
|
|
for i in random.sample(xrange(j),j):
|
2012-04-11 18:05:27 -04:00
|
|
|
interval = Interval(i, i+1)
|
|
|
|
iset += interval
|
2012-04-12 18:39:16 -04:00
|
|
|
speed = (time.time() - start) * 1000000.0
|
2012-11-29 01:00:54 -05:00
|
|
|
printf("%d: %g μs (%g μs each, O(n log n) ratio %g)\n",
|
|
|
|
j,
|
|
|
|
speed,
|
|
|
|
speed/j,
|
|
|
|
speed / (j*math.log(j))) # should be constant
|
2012-04-11 18:05:27 -04:00
|
|
|
speeds[j] = speed
|
|
|
|
aplotter.plot(speeds.keys(), speeds.values(), plot_slope=True)
|
|
|
|
yappi.stop()
|
2012-05-04 12:08:32 -04:00
|
|
|
yappi.print_stats(sort_type=yappi.SORTTYPE_TTOT, limit=10)
|
2012-11-13 22:55:37 -05:00
|
|
|
|