[SciPy-user] Python Spreadsheet with Python as Core Macro Language
Paul Kienzle
pkienzle at nist.gov
Wed Mar 12 17:59:32 EDT 2008
On Wed, Mar 12, 2008 at 09:40:09PM +0100, Sebastian Haase wrote:
> Since the GUI part is practically given with some simple wxPython
> code, one could instead ask for a "spreadsheed engine" !! This would
> be a (small) set of functions which can analysis the dependencies a
> given spreadsheet and evaluate the cells in a (non-circular) sequence
> taking the found dependencies into account. Might already exist ...
Indeed. See attached. Not the highest performance code, but it should be
enough to get one started. All you need is to parse the expressions
to identify cell references for building the dependency graph.
- Paul
-------------- next part --------------
# This program is public domain
# Author: Paul Kienzle
"""
Helper classes for implementing constraint dependency ordering.
Constraints must be evaluated in the correct order. If parameter A
depends on parameter B, then parameter B must be evaluated first.
This ConstraintsDAG class allows you to specify the links between
the constraints, and from there calculate the extract an order in
which to evaluate them.
"""
# TODO: the current algorithm is recursive, and is subject to max recursion
# depth errors if too many constraints are used. Need to construct a
# non-recursive algorithm for ordering constraints.
class Error(Exception):
"""Exception raised for errors in the constraint module."""
pass
class CyclicError(Exception):
"""
The constraint expressions have a cycle.
"""
pass
class ParsingError(Exception):
"""
A constraint expression is invalid.
"""
pass
def _order_children(depends_on, target, visited, stack):
"""
Recursion for dependency ordering algorithm.
"""
#print target,visited,stack
if target in stack:
raise CyclicError, "Cyclic constraint containing parameter %s"%(target)
if target not in depends_on or target in visited: return []
stack += [target]
visited.add(target)
order = []
for child in depends_on[target]:
#print "checking",target,"->",child
order += _order_children(depends_on, child, visited, stack)
return order+[target]
class ConstraintDAG(object):
"""
Structure to store constraint dependencies.
"""
def __init__(self, pairs=[]):
self.depends_on = {}
for a,b in pairs: self.link(a,b)
def link(self,a,b):
"""
Make a depend on b.
"""
if a not in self.depends_on: self.depends_on[a] = []
self.depends_on[a].append(b)
def links(self,a,b):
"""
Set all dependencies of a.
"""
if b == []:
if a in self.depends_on: del self.depends_on[a]
else:
self.depends_on[a] = b
def order(self):
"""
Return the set of nodes A in the order they need to be
evaluated, raising CyclicError if their are circular
dependencies.
"""
visited = set()
order = []
for target in self.depends_on.iterkeys():
#print "checking tree",target
order += _order_children(self.depends_on, target, visited, [])
return order
# ========= Test code ========
def check(msg,n,items,partialorder):
"""
Verify that the list n contains the given items, and that the list
satisfies the partial ordering given by the pairs in partial order.
"""
success = True # optimism
if set(n) != set(items) or len(n) != len(items):
success = False
print msg,"expect",n,"to contain only",items
for lo,hi in partialorder:
if lo in n and hi in n and n.index(hi) <= n.index(lo):
print msg,"expect",lo,"before",hi
success = False
if success: print msg,"passes"
def test():
import numpy
DAG = ConstraintDAG([(7,2),(5,1),(4,1),(1,2),(1,3),(6,5)])
n = DAG.order()
check("test1",n,[1,4,5,6,7],[(1,3),(1,4),(1,5),(5,6)])
DAG = ConstraintDAG([(1,6),(3,7),(4,7),(7,6),(7,5),(2,3)])
n = DAG.order()
check("test1-renumbered",n,[1,2,3,4,7],[(7,5),(7,4),(7,3),(3,2)])
DAG = ConstraintDAG(numpy.array([(7,2),(5,1),(4,1),(1,2),(1,3),(6,5)]))
n = DAG.order()
check("test1-numpy",n,[1,4,5,6,7],[(1,3),(1,4),(1,5),(5,6)])
DAG = ConstraintDAG([(1,4),(2,3),(8,4)])
n = DAG.order()
check("test2",n,[1,2,8],[])
DAG = ConstraintDAG([(1,4),(4,3),(4,5),(5,1)])
try:
n = DAG.order()
print "test3 expected CyclicError but got",n
except CyclicError,msg:
print "test3 correctly fails - error message:",msg
# large test for gross speed check
A = numpy.random.randint(4000,size=(10000,2))
A[:,1] += 4000 # Avoid cycles
DAG = ConstraintDAG(A)
n = DAG.order()
# depth test
k = 100
A = numpy.array([range(0,k),range(1,k+1)]).T
DAG = ConstraintDAG(A)
n = DAG.order()
A = numpy.array([range(1,k+1),range(0,k)]).T
DAG = ConstraintDAG(A)
n = DAG.order()
if __name__ == "__main__":
test()
More information about the SciPy-User
mailing list