A Python-based Tree Definition Language

Since Python is no nicely extensible, a few cleverly-written classes would allow for trees to be defined at a very high level. I can see the following benefits:

Some code examples to focus the discussion follow. A more complete working example can be found here: TreeDefinitionLanguage/InitialWorkingExample. A prototype can currently be found in cvs as Timba/TreeGen/src/TreeGen.py, this contains the python code needed to implement the language, plus the working example (after the if __name_ == 'main': statement.)

Here's a "peeling unit" implementing a shift/solve/subtract sequence:

   1 def peelUnit (datasource,solve_sources,predict_sources=[],ns=GlobalScope):
   2   """this function defines a peeling unit for a list of solve sources and, optionally, a list of
   3   auxiliary predict sources""";
   4   # ns is a node scope: this object is responsible for auto-generating names
   5   # We create all our nodes in whichever scope has been passed in.
   6   for (s1,s2) in IFRS:
   7     for (icorr,corr) in enumerate(CORRELATIONS):
   8       # create condeq branch
   9       ns.condeq(s1=s1,s2=s2,corr=corr) << MeqCondeq(children=[
  10         ns.measured(s1=s1,s2=s2,corr=corr) << MeqSelector(index=icorr,children=[
  11           ns.phaseshifter(s1=s1,s2=s2) << MeqPhaseShift(...,children=datasource(s1=s1,s2=s2))]),
  12         ns.predicted(s1=s1,s2=s2,corr=corr) << MeqAdd(children=
  13           [ gen(s1=s1,s2=s2,corr=corr) for gen in solve_sources + predict_sources ]);
  14       ]);
  15     # create subtract branch
  16     ns.subtract(s1=s1,s2=s2) << MeqSubtract(children=[
  17       ns.phaseshifter(s1=s1,s2=s2),
  18       ns.predpeel(s1=s1,s2=s2) <<= MeqAdd(children=[
  19           ns.predcollect(s1=s1,s2=s2) << MeqCollector(children=[gen(s1=s1,s2=s2,corr=corr) for corr in CORRS])
  20         for gen in solve_sources ]);
  21     ]);
  22   # creates solver and sequencers
  23   ns.solver() = MeqSolver(...,children =
  24       [ unit.condeq(s1=s1,s2=s2,corr=corr) for (s1,s2) in IFRS for corr in CORRS ]);
  25   for (s1,s2) in IFRS:
  26     ns.reqseq(s1=s1,s2=s2) << MeqReqSeq(...,children=[ns.solver(),ns.subtract(s1,s2)]);
  27   # returns root nodes of unit
  28   return ns.reqseq;

Now here's a top-level script to chain multiple peeling units together:

   1 # create spigots
   2 for (s1,s2) in IFRS:
   3   node.spigot(s1,s2) << MeqSpigot(...);
   4 
   5 # create & chain together peel units
   6 datasource = node.spigot;
   7 for (q,name) in enumerate(SOURCES):
   8   # get subtrees for this source from LSM
   9   predicter = lsm.getSourcePredicter(name,scope=tree.scope('predict',src=q));
  10   # create peel unit
  11   datasource = peelUnit(datasource,predicter,scope=tree.scope('peelunit',src=q));
  12 
  13 # attach sinks to last unit in chain
  14 for (s1,s2) in IFRS:
  15   node.sink(s1,s2) << MeqSink(...,children=datasource(s1=s1,s2=s2));

And here's what an LSM tree might look like:

   1 def unpolarizedPointSource(ra=...,dec=...,sti=...,ns=None):
   2   """defines an unpolarized point source""";
   3   ns.lmn << MeqLMN(children = {
   4     ra:   ns.ra()  << MeqParm(...),
   5     dec:  ns.dec() << MeqParm(...),
   6     ra0:  GLOBALS.PhaseCenter.ra,
   7     dec0: GLOBALS.PhaseCenter.dec });
   8   for (s1,s2) in IFRS:
   9     ns.predict_i(s1=s1,s2=s2,stk='I') = MeqMultiply(children=[
  10       ns.flux(stk='I') << MeqParm(...),
  11       ns.dft(s1=s1,s2=s2) << MeqPSDFT(children=[
  12         ns.stdft(s=s1) << MeqPSStDFT(children=[GLOBALS.UVW(s=s1),ns.lmn()]),
  13         ns.stdft(s=s2) << MeqPSStDFT(children=[GLOBALS.UVW(s=s2),ns.lmn()]),
  14         ns.n() <<= MeqSelector(index=2,children=ns.lmn());
  15       ])
  16     ]);
  17   for c in ('XX','YY'):
  18     ns.predict(s1=s1,s2=s2,corr=c) = MeqMultiply(children=(ns.predict(s1=s1,s2=s2,stk='I'),MeqConst(value='0.5')));
  19   for c in ('XY','YX'):
  20     ns.predict(s1=s1,s2=s2,corr=c) = MeqConst(value='0');
  21   return ns.predict;

Tony's Thoughts

This a useful start - at least we are writing programs in Python rather than Glish, and if useful 'standard layout' functions and definitions are available, then some of the programming burden would be removed. However, when I heard that Oleg was working on a tree definition language I had hoped for something more.

What I would like to see is something like the following:

This language allows me to lay out the tree in a text file. Note that the text file describes a tree layout, and is not a program. I don't necessarily want to have to write a program in order to design a tree.

The processor takes the tree as defined above and turns this tree into something that can be executed by the computer. This output could indeed be a piece of python code, or maybe even a compiled executable directly interfaced to the kernel.

In the Graphviz package ([WWW] http://www.graphviz.org) we describe the graph layout in a tree definition language called 'dot'. Various viewers etc, can then process the 'dot' layout code and produce a meaningful drawing.

GUIs are basically defined by a tree structure. In Qt ([WWW] http://www.trolltech.com/) we have a main widget which can have children, etc. Qt comes with Qt Designer. The Designer allows you to lay out a widget, set up all the locations and connections of the children etc and then stores the widget design in a text file (in XML if I remember correctly).

Then a processor program takes this design desciption in XML and generates the python or C++ code which will actually be used to implement the GUI.

Obviously a fair amount of work would be required to implement such an entire system for MeqTrees and I'm not sure I'd give it a high priority, but ...

If MeqTrees provided such a system, we would be off to the races!

Oleg's Responce

Tony, I don't think I've conveyed the main principle right. Python role's here is not that of a quick kludge at all, there's huge added value in it. Your idea of a custom TDL is roughly where I started off with my thinking, but I was troubled by the ensuing complexity. Keep in mind that:

Finally, if you look at the provided code examples, they do nothing but describe trees, with minimal synctatic overhead. If we design our own language, it won't look all that different from the Python code you see here, simply because it will need to do roughly similar stuff. So is there really a point in creating a language?

TreeDefinitionLanguage/InitialDiscussion (last edited 2005-06-20 13:21:19 by OlegSmirnov)