#!/usr/bin/python
# -*- coding: utf-8 -*-
# ---------------------------------------------------------------------
# Copyright (c) 2012 Michael Hull.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# - Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# - Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# ----------------------------------------------------------------------
import re
from morphforge.traces.tracetypes.tracepiecewise import TracePieceFunctionLinear
from morphforge.traces.tracetypes.tracepiecewise import TracePiecewise
from morphforge.traces.tracetypes.tracepiecewise import TracePieceFunctionFlat
from morphforge.units import qty
from morphforge import units
import itertools
[docs]class LevelToken(object):
[docs] def __init__(self, symbol):
self.symbol = symbol
[docs] def does_consume(self):
return False
[docs] def record_time_in_symbol(self):
return self.symbol
[docs] def does_match(self, level):
return True
[docs]class LevelSelector(object):
[docs] def __init__(self, time_selector, data_selector):
self.time_selector = time_selector
self.data_selector = data_selector
[docs] def does_match(self, level):
return self.time_selector.does_match(level) \
and self.data_selector.does_match(level)
[docs] def does_consume(self):
return True
[docs] def record_time_in_symbol(self):
return None
[docs]class DataSelector(object):
[docs] def __init__(self, minvalue=None, maxvalue=None):
self.minvalue = minvalue
self.maxvalue = maxvalue
[docs] def does_match(self, level):
l_bound_ok = (self.minvalue < level.get_value() if self.minvalue is not None else True)
u_bound_ok = (self.maxvalue > level.get_value() if self.maxvalue is not None else True)
return l_bound_ok and u_bound_ok
[docs]class TimeSelector(object):
[docs] def __init__(self, minduration=None, maxduration=None):
self.minduration = minduration
self.maxduration = maxduration
[docs] def does_match(self, level):
l_bound_ok = (self.minduration < level.get_duration() if self.minduration is not None else True)
u_bound_ok = (self.maxduration > level.get_duration() if self.maxduration is not None else True)
return l_bound_ok and u_bound_ok
[docs]class MatchObject(object):
[docs] def __init__(self):
self.d = {}
[docs] def set_symbol(self, symbol, value):
self.d[symbol] = value
[docs] def __getattr__(self, name):
if name in self.d:
return self.d[name]
[docs] def __str__(self):
return "<MatchObject: "+ ", ".join(["%s=%s"%(k, v) for (k, v) in sorted(self.d.iteritems())]) + ">"
[docs]class LevelSelectorGroup(object):
[docs] def __init__(self, S, xunit, yunit):
# Parse the expression:
self.expr = LevelSelectorGroup.parse_expr(S, xunit, yunit)
self.xunit = xunit
[docs] def matchall(self, level_set):
pieces = level_set.pieces
# From [A, B, C, D, ..]
# return [[A, B, C, D], [B, C, D], [C, D], [D]...]
subgroups = []
for i in range(len(pieces)):
subgroups.append(pieces[i:])
matches = [self.test_match(sg) for sg in subgroups]
return [m for m in matches if m is not None]
[docs] def test_match(self, level_pieces):
# Check the lengths, so I don't need to worry about it later:
if len(level_pieces) < len([t for t in self.expr
if t.does_consume()]):
return None
m = MatchObject()
level_piece_iter = itertools.chain(iter(level_pieces), [None])
level_piece_next = level_piece_iter.next()
current_time = level_piece_next.get_min_time()
for t in self.expr:
# Do we need to record the current time?
rec = t.record_time_in_symbol()
if rec:
m.set_symbol(rec, current_time.rescale(self.xunit))
# Does match?
if not t.does_match(level_piece_next):
return None
# Next term:
if t.does_consume():
level_piece_next = level_piece_iter.next()
if level_piece_next == None:
current_time = level_pieces[-1].get_max_time()
else:
current_time = level_piece_next.get_min_time()
return m
@classmethod
[docs] def parse_expr(cls, s, xunit, yunit):
s = s.replace(' ', '')
return [LevelSelectorGroup.parse_term(t, xunit, yunit) for t in
s.split(',')]
@classmethod
[docs] def parse_term(cls, st, xunit, yunit):
r_marker = re.compile(r"""(?P<name>[a-zA-Z0-9]+)""", re.VERBOSE)
t_marker = \
re.compile(r"""{ (?P<t0>[-]?\d+)? : (?P<t1>[-]?\d+)? @ (?P<d0>[-]?\d+)? : (?P<d1>[-]?\d+)? }"""
, re.VERBOSE)
r_m = r_marker.match(st)
if r_m:
marker = r_m.groupdict()['name']
return LevelToken(symbol=marker)
t_m = t_marker.match(st)
if not t_m:
assert False, "Can't parse: %s" % st
g = t_m.groupdict()
(t0, t1, d0, d1) = (g['t0'], g['t1'], g['d0'], g['d1'])
t0 = (int(t0) * xunit if t0 else None)
t1 = (int(t1) * xunit if t1 else None)
d0 = (int(d0) * yunit if d0 else None)
d1 = (int(d1) * yunit if d1 else None)
return LevelSelector(data_selector=DataSelector(d0, d1),
time_selector=TimeSelector(t0, t1))
[docs]def main():
l1 = TracePiecewise(pieces = [
TracePieceFunctionFlat(time_window=(0, 50)*units.ms, x=qty("0:pA")),
TracePieceFunctionFlat(time_window=(50, 150)*units.ms, x=qty("110:pA")),
TracePieceFunctionFlat(time_window=(150, 350)*units.ms, x=qty("0:pA")),
])
sel1 = LevelSelectorGroup(" A, { 10:65 @ -1:1 }, B, { 10: @ 90:111 }, C", xunit=qty("ms"), yunit=qty("pA"))
matches = sel1.matchall(l1)
for m in matches:
print m
if __name__ == '__main__':
main()