|
[yoda-svn] r514 - in trunk: . bin include/YODA pyext pyext/yoda pyext/yoda/includeblackhole at projects.hepforge.org blackhole at projects.hepforge.orgFri Aug 3 03:14:45 BST 2012
Author: davemallows Date: Fri Aug 3 03:14:44 2012 New Revision: 514 Log: Rewrite and restructuring of python extension. Numerous improvements, including the removal of operator shims; increased inheritance and vastly improved memory handling. There are a few regressions, notably some missing docstrings and the removal of 2D/3D AnalysisObjects. There have been some tidyings of the interface to provide a more Pythonic style. Added: trunk/pyext/Makefile.am trunk/pyext/setup.py.in trunk/pyext/template.py trunk/pyext/yoda/ trunk/pyext/yoda/core.pyx trunk/pyext/yoda/declarations.pxd trunk/pyext/yoda/errors.cpp trunk/pyext/yoda/errors.hh trunk/pyext/yoda/include/ trunk/pyext/yoda/include/AnalysisObject.pyx trunk/pyext/yoda/include/Annotations.pyx trunk/pyext/yoda/include/Axis1D_BIN1D_DBN.pyx trunk/pyext/yoda/include/Axis2D_BIN2D_DBN.pyx trunk/pyext/yoda/include/Bin.pyx trunk/pyext/yoda/include/Bin1D_DBN.pyx trunk/pyext/yoda/include/Bin2D_DBN.pyx trunk/pyext/yoda/include/Dbn1D.pyx trunk/pyext/yoda/include/Dbn2D.pyx trunk/pyext/yoda/include/Dbn3D.pyx trunk/pyext/yoda/include/Errors.pyx trunk/pyext/yoda/include/Histo1D.pyx trunk/pyext/yoda/include/Histo2D.pyx trunk/pyext/yoda/include/HistoBin1D.pyx trunk/pyext/yoda/include/HistoBin2D.pyx trunk/pyext/yoda/include/IO.pyx trunk/pyext/yoda/include/Point2D.pyx trunk/pyext/yoda/include/Point3D.pyx trunk/pyext/yoda/include/Profile1D.pyx trunk/pyext/yoda/include/Profile2D.pyx trunk/pyext/yoda/include/ProfileBin1D.pyx trunk/pyext/yoda/include/ProfileBin2D.pyx trunk/pyext/yoda/include/Scatter2D.pyx trunk/pyext/yoda/util.pxd trunk/pyext/yoda/util.pyx Replaced: trunk/pyext/ Modified: trunk/ChangeLog trunk/TODO trunk/bin/yoda2aida trunk/configure.ac trunk/include/YODA/Profile1D.h trunk/include/YODA/ProfileBin2D.h Modified: trunk/ChangeLog ============================================================================== --- trunk/ChangeLog Tue Jul 31 16:06:27 2012 (r513) +++ trunk/ChangeLog Fri Aug 3 03:14:44 2012 (r514) @@ -1,3 +1,10 @@ +2012-08-02 Dave Mallows <dave.mallows at gmail.com> + * Heavily refactored Cython bindings + + * HistoBin1D, ProfileBin1D etc. now inherit from Bin1D[DBN] + + * Temporarily removed Histo2D, Profile2D and Scatter3D mappings. + 2012-07-23 Andy Buckley <andy.buckley at cern.ch> * Installing scripts from bin dir, and making the yoda2aida interface nicer. Modified: trunk/TODO ============================================================================== --- trunk/TODO Tue Jul 31 16:06:27 2012 (r513) +++ trunk/TODO Fri Aug 3 03:14:44 2012 (r514) @@ -10,10 +10,6 @@ Ignore anything after "#" on data lines -- use this to write convenience height & error info for each bin since not obvious from sumWX2 etc. -* Remove nasty Cython shims (DM) - Does inheritance work automatically in Cython now? Add operator support - everywhere it's needed. - * Add Axis2D -> Histo2D/Profile2D bin adding and erasing. (AB) * Make Python interface test scripts (DM) @@ -22,6 +18,8 @@ * Docstrings for Points, Dbns and Bins (AB) +* Reintroduce missing docstrings (DM) + * Add stdErr to HistoBinXD (AB) * Add Dbn3D, ProfileBin2D, Profile2D and Scatter3D in Cython (AB) Modified: trunk/bin/yoda2aida ============================================================================== --- trunk/bin/yoda2aida Tue Jul 31 16:06:27 2012 (r513) +++ trunk/bin/yoda2aida Fri Aug 3 03:14:44 2012 (r514) @@ -25,5 +25,5 @@ sys.stderr.write("You must specify the YODA and AIDA file names\n") sys.exit(1) -analysisobjects = yoda.ReaderYODA().read(INFILE) -yoda.WriterAIDA().write(OUTFILE, analysisobjects) +analysisobjects = yoda.readYODA(INFILE) +yoda.WriteAIDA(analysisobjects, OUTFILE) Modified: trunk/configure.ac ============================================================================== --- trunk/configure.ac Tue Jul 31 16:06:27 2012 (r513) +++ trunk/configure.ac Fri Aug 3 03:14:44 2012 (r514) @@ -48,7 +48,7 @@ ## Basic Python checks if test x$enable_pyext == xyes; then AZ_PYTHON_PATH - AZ_PYTHON_VERSION_ENSURE([2.4]) + AZ_PYTHON_VERSION_ENSURE([2.6]) PYTHON_VERSION=`$PYTHON -c "import sys; print '.'.join(map(str, sys.version_info@<:@:2@:>@));"` AC_SUBST(PYTHON_VERSION) RIVET_PYTHONPATH=`$PYTHON -c "import distutils.sysconfig; print distutils.sysconfig.get_python_lib(prefix='$prefix', plat_specific=True);"` @@ -74,10 +74,9 @@ ## Cython checks (Probably need some help...) if test x$enable_pyext == xyes; then - cython_required_version=0.13 - AM_CHECK_CYTHON([$cython_required_version], [:], [:]) - if test "x$CYTHON_FOUND" != "xyes"; then - AC_MSG_ERROR([Can't build Python extension since Cython >= 0.13 could not be found]) + AM_CHECK_CYTHON([0.16], [:], [:]) + if test x$CYTHON_FOUND != xyes; then + AC_MSG_ERROR([Can't build Python extension since Cython >= 0.16 could not be found]) enable_pyext=no else cython_compiler=$CXX @@ -142,7 +141,7 @@ AC_CONFIG_FILES([src/Makefile src/tinyxml/Makefile]) AC_CONFIG_FILES([tests/Makefile]) AC_CONFIG_FILES([bin/Makefile]) -AC_CONFIG_FILES([pyext/Makefile pyext/yoda/Makefile pyext/setup.py]) +AC_CONFIG_FILES([pyext/Makefile pyext/setup.py]) #AC_CONFIG_FILES([bin/Makefile bin/yoda-config]) AC_OUTPUT Modified: trunk/include/YODA/Profile1D.h ============================================================================== --- trunk/include/YODA/Profile1D.h Tue Jul 31 16:06:27 2012 (r513) +++ trunk/include/YODA/Profile1D.h Fri Aug 3 03:14:44 2012 (r514) @@ -147,8 +147,8 @@ } /// Bin addition operator - void addBin(size_t from, size_t to) { - _axis.addBin(from, to); + void addBin(double xlow, double xhigh) { + _axis.addBin(xlow, xhigh); } /// Bin addition operator Modified: trunk/include/YODA/ProfileBin2D.h ============================================================================== --- trunk/include/YODA/ProfileBin2D.h Tue Jul 31 16:06:27 2012 (r513) +++ trunk/include/YODA/ProfileBin2D.h Fri Aug 3 03:14:44 2012 (r514) @@ -106,6 +106,10 @@ return _dbn.zStdErr(); } + double rms() const { + return _dbn.zRMS(); + } + //@} /// @name Raw z distribution statistics Added: trunk/pyext/Makefile.am ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ trunk/pyext/Makefile.am Fri Aug 3 03:14:44 2012 (r514) @@ -0,0 +1,25 @@ +if ENABLE_PYEXT + +SUBDIRS = . +CYTHONFLAGS = @CYTHONFLAGS@ + +all-local: + $(PYTHON) setup.py build + $(PYTHON) setup.py install --install-lib=build/ + +install-exec-local: + $(PYTHON) setup.py install --prefix=$(DESTDIR)$(prefix) + +uninstall-local: + rm -rf $(DESTDIR)$(YODA_PYTHONPATH)/yoda + +clean-local: + $(PYTHON) setup.py clean --all + @rm -f $(top_builddir)/*.pyc + @rm -rf $(builddir)/build + @rm -rf dist + +distclean-local: + @rm -rf yoda.egg-info + +endif Added: trunk/pyext/setup.py.in ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ trunk/pyext/setup.py.in Fri Aug 3 03:14:44 2012 (r514) @@ -0,0 +1,53 @@ +# setup.py + +from distutils.core import setup +from distutils.extension import Extension +from Cython.Distutils import build_ext +from glob import glob +from template import make_templates + +PKGNAME = 'yoda' + +## Extension definition +import os.path +incdir = os.path.abspath('@top_srcdir@/include') +boostdir = os.path.abspath('@BOOSTINCPATH@') +srcdir = os.path.abspath('@top_srcdir@/src') + +statics = os.path.join('yoda', 'errors.cpp') + +# A helper function (since we have two modules now...) +def ext(name, depends=[], statics=[]): + return Extension( + '%s.%s' % (PKGNAME, name), + ['%s/%s.cpp' % (PKGNAME, name)] + statics, + language='c++', + depends=depends, + include_dirs=[incdir, PKGNAME, boostdir], + library_dirs=[srcdir, os.path.join(srcdir, '.libs')], + libraries=['stdc++', 'YODA']) + +extns = [ext('util'), + ext('core', statics=[statics], + depends=glob('yoda/include/*.pyx') + ), + ] + +# Make the templates +make_templates('Axis1D_BIN1D_DBN', + dict(DBN='Dbn2D', BIN1D='ProfileBin1D'), + dict(DBN='Dbn1D', BIN1D='HistoBin1D')) + +make_templates('Axis2D_BIN2D_DBN', + dict(DBN='Dbn3D', BIN2D='ProfileBin2D'), + dict(DBN='Dbn2D', BIN2D='HistoBin2D')) + +make_templates('Bin1D_DBN', DBN=('Dbn1D', 'Dbn2D')) +make_templates('Bin2D_DBN', DBN=('Dbn2D', 'Dbn3D')) + +setup( + name = PKGNAME, + ext_modules = extns, + cmdclass = dict(build_ext=build_ext), + packages = [PKGNAME] +) Added: trunk/pyext/template.py ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ trunk/pyext/template.py Fri Aug 3 03:14:44 2012 (r514) @@ -0,0 +1,67 @@ +from string import Template +from itertools import product, izip + +# A total mess. Uses python's string.Template to substitute values in various +# templated C++ wrappers, as Cython has no support for templates (and fused +# types are currently useless). + +import os.path +INCLUDE_DIR = 'include' +GENERATED_DIR = os.path.join(INCLUDE_DIR, 'generated') +BASE_DIR = 'yoda' + +def path(path): + return os.path.join(BASE_DIR, path) + +# Try and make the template (output) directory +try: + os.mkdir(path(GENERATED_DIR)) +except OSError: + pass + +GENERATED_HEADER = Template(""" + +################################################# +############# WARNING ################ +################################################# +# This file has been automatically generated. +# Any changes you make here will get overridden. +# Instead, make your changes in +# ${filename}.pyx +################################################# + +""".lstrip()) + +def make_templates(filename, *args, **kwargs): + individuals = args or [dict(izip(kwargs, line)) for line in + product(*kwargs.itervalues())] + + full_filename = os.path.join(INCLUDE_DIR, filename + '.pyx') + with open(path(full_filename)) as f: + template = Template(f.read()) + + plain_name = filename + for i in individuals[0]: + plain_name = plain_name.replace(i, '') + plain_name = plain_name.rstrip('_') + #print plain_name + + includes = [] + for a in individuals: + + newname = filename + for i, j in a.iteritems(): + newname = newname.replace(i, j) + + out_filename = os.path.join(GENERATED_DIR, newname + '.pyx') + with open(path(out_filename), 'w') as f: + f.write(GENERATED_HEADER.substitute(filename=filename)) + f.write(template.substitute(**a)) + includes.append(out_filename) + + inc_filename = os.path.join(INCLUDE_DIR, plain_name + '.pxi') + + with open(path(inc_filename), 'w') as f: + f.write(GENERATED_HEADER.substitute(filename=filename)) + f.write("\n".join('include "%s"' % i for i in includes)) + Added: trunk/pyext/yoda/core.pyx ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ trunk/pyext/yoda/core.pyx Fri Aug 3 03:14:44 2012 (r514) @@ -0,0 +1,49 @@ +cimport yoda.declarations as c +cimport yoda.util as util +import yoda.util as util +from cython.operator cimport dereference as deref, preincrement as preinc +from libcpp.string cimport string +from libcpp.vector cimport vector +from libcpp.pair cimport pair +from libcpp.map cimport map + +# Pure python imports +from itertools import repeat, imap +from operator import attrgetter + + +include "include/Errors.pyx" +include "include/Dbn1D.pyx" +include "include/Dbn2D.pyx" +include "include/Dbn3D.pyx" +include "include/Point2D.pyx" +include "include/Point3D.pyx" +include "include/Bin.pyx" + +include "include/Axis1D.pxi" + +include "include/Bin1D.pxi" + +include "include/HistoBin1D.pyx" + +include "include/ProfileBin1D.pyx" + +include "include/Annotations.pyx" + +include "include/AnalysisObject.pyx" + +include "include/Histo1D.pyx" + +include "include/Profile1D.pyx" + +include "include/IO.pyx" + +include "include/Scatter2D.pyx" + +#include "include/Bin2D.pxi" +#include "include/ProfileBin2D.pyx" +#include "include/HistoBin2D.pyx" +#include "include/Histo2D.pyx" +#include "include/Profile2D.pyx" + +#include "include/Axis2D.pxi" Added: trunk/pyext/yoda/declarations.pxd ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ trunk/pyext/yoda/declarations.pxd Fri Aug 3 03:14:44 2012 (r514) @@ -0,0 +1,840 @@ +from libcpp.map cimport map +from libcpp.pair cimport pair +from libcpp.vector cimport vector +from libcpp cimport bool +from libcpp.string cimport string +from cython.operator cimport dereference as deref + +# Import the error handling C++ routine +cdef extern from "errors.hh": + # Have a look in errors.cpp for implementation specifics + void err "translate_yoda_error" () + +ctypedef map[string, string] Annotations + + +# Distributions + +# Dbn1D {{{ +cdef extern from "YODA/Dbn1D.h" namespace "YODA": + cdef cppclass Dbn1D: + Dbn1D () + Dbn1D (Dbn1D) + + void fill(double val, double weight) + void reset() + void scaleW(double) + void scaleX(double) + + + double xMean() except+ err + double xVariance() except+ err + double xStdDev() except+ err + double xStdErr() except+ err + double xRMS() except+ err + + # Raw distribution running sums + unsigned long numEntries() except+ err + unsigned long effNumEntries() except+ err + double sumW() except+ err + double sumW2() except+ err + double sumWX() except+ err + double sumWX2() except+ err + + # Operators: TODO when Cython supports += and -= etc. + Dbn1D operator+ (Dbn1D) except+ err + Dbn1D operator- (Dbn1D) except+ err +#}}} Dbn1D + +# Dbn2D {{{ +cdef extern from "YODA/Dbn2D.h" namespace "YODA": + cdef cppclass Dbn2D: + Dbn2D () + Dbn2D (Dbn2D) + + void fill(double x, double y, double weight) except+ err + void reset() except+ err + void scaleW(double) except+ err + void scaleX(double) except+ err + void scaleY(double) except+ err + void scaleXY(double, double) except+ err + + double xMean() except+ err + double xVariance() except+ err + double xStdDev() except+ err + double xStdErr() except+ err + double xRMS() except+ err + + double yMean() except+ err + double yVariance() except+ err + double yStdDev() except+ err + double yStdErr() except+ err + double yRMS() except+ err + + # Raw distribution running sums + unsigned long numEntries() except+ err + unsigned long effNumEntries() except+ err + double sumW() except+ err + double sumW2() except+ err + double sumWX() except+ err + double sumWX2() except+ err + double sumWY() except+ err + double sumWY2() except+ err + double sumWXY() except+ err + + # Operators + void flipXY() except+ err + Dbn1D transformX() except+ err + Dbn1D transformY() except+ err + + Dbn2D operator + (Dbn2D) except+ err + Dbn2D operator - (Dbn2D) except+ err + # TODO: += and -= operators in Cython (maybe 0.17?) +#}}} Dbn2D + +# Dbn3D {{{ +cdef extern from "YODA/Dbn3D.h" namespace "YODA": + cdef cppclass Dbn3D: + Dbn3D () + Dbn3D (Dbn3D) + void fill(double x, double y, double z, double weight) + void reset() + + void scaleW(double) + void scaleX(double) + void scaleY(double) + void scaleZ(double) + void scaleXY(double, double) + void scaleXYZ(double, double, double) + + # Each of these statistics can return a 3D named tuple + double xMean() + double xVariance() + double xStdDev() + double xStdErr() + double xRMS() + + double yMean() + double yVariance() + double yStdDev() + double yStdErr() + double yRMS() + + double zMean() + double zVariance() + double zStdDev() + double zStdErr() + double zRMS() + + + # Raw distribution running sums + unsigned long numEntries() + unsigned long effNumEntries() + double sumW() + double sumW2() + + double sumWX() + double sumWX2() + + double sumWY() + double sumWY2() + + double sumWZ() + double sumWZ2() + + double sumWXY() + double sumWXZ() + double sumWYZ() + + double sumWXYZ() + + # Operators + void flipXY() + void flipXZ() + void flipYZ() + + Dbn1D transformX() + Dbn1D transformY() + Dbn1D transformZ() + + Dbn3D operator + (Dbn3D) + Dbn3D operator - (Dbn3D) + # TODO: += and -= operators in Cython (maybe 0.17?) +#}}} Dbn3D + +# Points + +# Point2D {{{ +cdef extern from "YODA/Point2D.h" namespace "YODA": + cdef cppclass Point2D: + Point2D () except+ err + Point2D (Point2D p) except+ err + + Point2D (double x, double y, + double exminus, double explus, + double eyminus, double eyplus) except+ err + + double x() except+ err + double y() except+ err + void setX(double x) except+ err + void setY(double y) except+ err + double xErrAvg() except+ err + double yErrAvg() except+ err + pair[double,double] xErrs() except+ err + pair[double,double] yErrs() except+ err + void setXErr(pair[double, double]) except+ err + void setYErr(pair[double, double]) except+ err + double xMin() except+ err + double xMax() except+ err + void scale(double x, double y) except+ err + bool operator == (Point2D) except+ err + bool operator != (Point2D b) except+ err + bool operator < (Point2D b) except+ err + bool operator <= (Point2D b) except+ err + bool operator > (Point2D b) except+ err + bool operator >= (Point2D b) except+ err +# }}} Point2D + +# Point3D {{{ +cdef extern from "YODA/Point3D.h" namespace "YODA": + cdef cppclass Point3D: + Point3D () except+ err + Point3D (Point3D &p) except+ err + + Point3D (double x, double y, double z, + double exminus, double explus, + double eyminus, double eyplus, + double ezminus, double ezplus) except+ err + + double x() except+ err + double y() except+ err + double z() except+ err + void setX(double x) except+ err + void setY(double y) except+ err + void setZ(double z) except+ err + double xMin() except+ err + double xMax() except+ err + double yMin() except+ err + double yMax() except+ err + double zMin() except+ err + double zMax() except+ err + pair[double,double] xErrs() except+ err + pair[double,double] yErrs() except+ err + pair[double,double] zErrs() except+ err + void setXErr(pair[double, double]) except+ err + void setYErr(pair[double, double]) except+ err + void setZErr(pair[double, double]) except+ err + double xErrAvg() + double yErrAvg() + double zErrAvg() + void scale(double x, double y, double z) except+ err + + bool operator == (Point3D b) + bool operator != (Point3D b) + bool operator < (Point3D b) + bool operator <= (Point3D b) + bool operator > (Point3D b) + bool operator >= (Point3D b) +#}}} Point3D + +# Bins + +# Bin {{{ +cdef extern from "YODA/Bin.h" namespace "YODA": + cdef cppclass Bin: + pass +# }}} Bin + +#Bin1D {{{ +cdef extern from "YODA/Bin1D.h" namespace "YODA": + cdef cppclass Bin1D[DBN](Bin): + Bin1D(pair[double, double] edges) except+ err + Bin1D(pair[double, double] edges, DBN dbn) except+ err + Bin1D(Bin1D) except+ err + + # THIS IS A CYTHON LIMITATION... DO NOT CALL THIS + Bin1D() # (DO NOT CALL THIS DO NOT CALL THIS) ### + ################################################# + + #We're fine as long as we don't try to instantiate these from Python + + void scaleW(double scale) except+ err + void scaleX(double scale) except+ err + void reset() except+ err + + double lowEdge() except+ err + double highEdge() except+ err + pair[double, double] edges() except+ err + double width() except+ err + double focus() except+ err + double midpoint() except+ err + + # x statistics + double xMean() except+ err + double xVariance() except+ err + double xStdDev() except+ err + double xStdErr() except+ err + double xRMS() except+ err + + # raw statistics + unsigned long numEntries() except+ err + unsigned long effNumEntries() except+ err + double sumW() except+ err + double sumW2() except+ err + double sumWX() except+ err + double sumWX2() except+ err + + void merge (Bin1D &) except+ err + Bin1D operator + (Bin1D &) except+ err + Bin1D operator - (Bin1D &) except+ err + +ctypedef Bin1D[Dbn1D] Bin1D_Dbn1D +ctypedef Bin1D[Dbn2D] Bin1D_Dbn2D +ctypedef Bin1D[Dbn3D] Bin1D_Dbn3D +#}}} Bin1D + +# Bin2D {{{ +cdef extern from "YODA/Bin2D.h" namespace "YODA": + cdef cppclass Bin2D[DBN](Bin): + Bin2D(pair[double, double] xedges, + pair[double, double] yedges) except+ + Bin2D(Bin2D bin) except+ err + + # CYTHON HACK DO NOT CALL THIS IT DOES NOT EXIST + Bin2D() # (DO NOT CALL DO NOT CALL) + ################################################ + + void scaleW(double scale) except+ err + void scaleXY(double, double) except+ err + void reset() except+ err + + # Also xMin, xMax etc... + pair[double, double] xedges() except+ err + pair[double, double] yedges() except+ err + + double widthX() except+ err + double widthY() except+ err + + pair[double, double] focus() except+ err + pair[double, double] midpoint() except+ err + + # x statistics + double xMean() except+ err + double xVariance() except+ err + double xStdDev() except+ err + double xStdErr() except+ err + double xRMS() except+ err + + double yMean() except+ err + double yVariance() except+ err + double yStdDev() except+ err + double yStdErr() except+ err + double yRMS() except+ err + + # raw statistics + unsigned long numEntries() except+ err + unsigned long effNumEntries() except+ err + double sumW() except+ err + double sumW2() except+ err + double sumWX() except+ err + double sumWY() except+ err + double sumWXY() except+ err + double sumWX2() except+ err + double sumWY2() except+ err + + #void merge(Bin2D) except+ err + Bin2D operator + (Bin2D) except+ err + Bin2D operator - (Bin2D) except+ err + +ctypedef Bin2D[Dbn2D] Bin2D_Dbn2D +ctypedef Bin2D[Dbn3D] Bin2D_Dbn3D +# }}} Bin2D + +# ProfileBin1D {{{ +cdef extern from "YODA/ProfileBin1D.h" namespace "YODA": + + cdef cppclass ProfileBin1D(Bin1D_Dbn2D): + ProfileBin1D(ProfileBin1D) except +err + ProfileBin1D(double, double) except +err + void fill(double x, double y, double weight) except+ err + void fillBin(double y, double weight) except+ err + void reset() except+ err + + double mean() except+ err + double stdDev() except+ err + double variance() except+ err + double stdErr() except+ err + double rms() except+ err + + double sumWY() except+ err + double sumWY2() except+ err + ProfileBin1D operator + (ProfileBin1D) except+ err + ProfileBin1D operator - (ProfileBin1D) except+ err + +# }}} ProfileBin1D + + +# ProfileBin2D {{{ +cdef extern from "YODA/ProfileBin2D.h" namespace "YODA": + + cdef cppclass ProfileBin2D(Bin2D_Dbn3D): + ProfileBin2D (ProfileBin2D h) except+ err + ProfileBin2D (double, double, double, double) except+ err + void fill(double x, double y, double z, double weight) except+ err + void fillBin(double z, double weight) except+ err + + double mean() except+ err + double stdDev() except+ err + double variance() except+ err + double stdErr() except+ err + double rms() except+ err + + double sumWZ() except+ err + double sumWZ2() except+ err + ProfileBin2D operator + (ProfileBin2D) except+ err + ProfileBin2D operator - (ProfileBin2D) except+ err + +# }}} ProfileBin2D + + +# HistoBin1D {{{ +cdef extern from "YODA/HistoBin1D.h" namespace "YODA": + cdef cppclass HistoBin1D(Bin1D_Dbn1D): + HistoBin1D(double lowedge, double highedge) except +err + HistoBin1D(HistoBin1D) except +err + void fill(double x, double weight) except +err + void fillBin(double weight) except +err + + double area() except +err + double height() except +err + double areaErr() except +err + double heightErr() except +err + + HistoBin1D operator+(HistoBin1D) except +err + HistoBin1D operator-(HistoBin1D) except +err + + +#}}} HistoBin1D + + +# HistoBin2D {{{ +cdef extern from "YODA/HistoBin2D.h" namespace "YODA": + cdef cppclass HistoBin2D(Bin2D_Dbn2D): + HistoBin2D(double xmin, double xmax, + double ymin, double ymax) except +err + HistoBin2D(HistoBin2D) except +err + + void fill(double x, double y, double weight) except +err + void fillBin(double weight) except +err + void reset() + + # Accessors + double volume() except +err + double volumeErr() except +err + double height() except +err + double heightErr() except +err + + HistoBin2D operator+(HistoBin2D) except +err + HistoBin2D operator-(HistoBin2D) except +err + + #Bin2D_Dbn2D merge(HistoBin2D b) +#}}} HistoBin2D + +# Analaysis Objects + +# AnalysisObject {{{ +cdef extern from "YODA/AnalysisObject.h" namespace "YODA": + cdef cppclass AnalysisObject: + # Constructors + AnalysisObject(string type, + string path, + string title) except+ err + AnalysisObject(string type, + string path, + AnalysisObject ao, + string title) except+ err + AnalysisObject() + + # Annotations + map[string, string] annotations() except+ err + + bool hasAnnotation(string key) except+ err + string annotation(string key) except+ err + string annotation(string key, string default) except+ err + void setAnnotation(string, string) except+ err + void rmAnnotation(string name) except+ err + void clearAnnotations() except+ err + + # Standard annotations + string title() except+ err + void setTitle(string title) except+ err + string path() except+ err + void setPath(string title) except+ err + string type() except +err +# }}} AnalysisObject + +cdef extern from "YODA/Utils/sortedvector.h" namespace "YODA::Utils": + cdef cppclass sortedvector[T](vector): + sortedvector(vector[T]) except +err + void insert(T) except +err + +# TODO: forward declarations for bin-copying constructors + +# Scatter2D {{{ +cdef extern from "YODA/Scatter2D.h" namespace "YODA": + cdef cppclass Scatter2D(AnalysisObject): + Scatter2D(string path, string title) except+ err + + Scatter2D(sortedvector[Point2D], + string path, + string title) except+ err + + Scatter2D(vector[double], vector[double], + vector[pair[double, double]], + vector[pair[double, double]]) except+ err + + Scatter2D(Scatter2D p, string path) + + void reset() except+ err + + size_t numPoints() except+ err + Point2D &point(size_t index) + + Scatter2D addPoint(Point2D&) + Scatter2D addPoint(double, double) + Scatter2D addPoint(double, double, + pair[double, double], pair[double, double]) except+ err + + Scatter2D addPoints(sortedvector[Point2D]) + + Scatter2D combineWith(Scatter2D) + Scatter2D combineWith(vector[Scatter2D]) + +#}}} Scatter2D + +# Scatter3D {{{ +cdef extern from "YODA/Scatter3D.h" namespace "YODA": + cdef cppclass Scatter3D(AnalysisObject): + Scatter3D(string path, string title) except+ err + Scatter3D(sortedvector[Point3D], + string path, + string title) except+ err + + Scatter3D(vector[double], vector[double], + vector[pair[double, double]], + vector[pair[double, double]], + vector[pair[double, double]]) except+ err + + Scatter3D(Scatter3D p, string path) + + void reset() except+ err + + size_t numPoints() except+ err + sortedvector[Point3D] points() except+ err + Point3D point(size_t index) except+ err + + Scatter3D addPoint(Point3D) + Scatter3D addPoint(double, double, + pair[double, double], pair[double, double]) except+ err + + Scatter3D addPoints(sortedvector[Point3D]) + + Scatter3D combineWith(Scatter3D) + Scatter3D combineWith(vector[Scatter3D]) +#}}} Scatter3D + +# Profile1D {{{ +cdef extern from "YODA/Profile1D.h" namespace "YODA": + cdef cppclass Profile1D(AnalysisObject): + Profile1D(string path, string title) except+ err + Profile1D(size_t nxbins, + double xlower, + double xupper, + string path, + string title) except+ err + Profile1D(vector[double] xbinedges, + string path, + string title) except+ err + Profile1D(Profile1D p, string path) except+ err + Profile1D(Scatter2D s, string path) except+ err + #Profile1D(Histo1D p, string path) + + void fill(double x, double y, double weight) except+ err + + void reset() except+ err + + void scaleW(double s) except+ err + + void mergeBins(size_t, size_t) except+ err + void rebin(int n) except+ err + + void addBin(double, double) except+ err + void addBins(vector[double] edges) except+ err + # void eraseBin(size_t index) except+ err + + size_t numBins() except+ err + double lowEdge() except+ err + double highEdge() except+ err + + vector[ProfileBin1D] bins() + ProfileBin1D bin(size_t ix) except+ err + ProfileBin1D binByCoord(double x) except+ err + + # The trick here is to treat these not as references. + # I suppose when you think about it, it makes sense + Dbn2D &totalDbn() + Dbn2D &underflow() + Dbn2D &overflow() + + double sumW(bool) + double sumW2(bool) + + operator + (Profile1D) + operator - (Profile1D) + operator / (Profile1D) + +#}}} Profile1D + +# Profile2D {{{ +cdef extern from "YODA/Profile2D.h" namespace "YODA": + cdef cppclass Profile2D(AnalysisObject): + Profile2D(string path, string title) except+ err + Profile2D(size_t nbinsX, double lowerX, double upperX, + size_t nbinsY, double lowerY, double upperY, + string path, string title) except+ err + + Profile2D(vector[double] xedges, vector[double] yedges, + string path, string title) except+ err + + Profile2D(Profile2D p, string path) except+ err + #Profile2D(Scatter3D s, string path) except+ err + #Profile2D(Histo2D p, string path) + + void fill(double x, double y, double z, double weight) except+ err + + void reset() except+ err + + void scaleW(double s) except+ err + + #void mergeBins(size_t, size_t) except+ err + #void rebin(int n) except+ err + + # void addBin(double, double) except+ err + # void addBins(vector[double] edges) except+ err + # void eraseBin(size_t index) except+ err + + size_t numBins() except+ err + double lowEdge() except+ err + double highEdge() except+ err + + vector[ProfileBin2D] bins() + ProfileBin2D bin(size_t ix) except+ err + ProfileBin2D binByCoord(double x) except+ err + int findBinIndex(double, double) + + # The trick here is to treat these not as references. + # I suppose when you think about it, it makes sense + # -- Cython is a code generator + Dbn3D totalDbn() except+ err + Dbn3D outflow(int, int) except+ err + + double sumW(bool) + double sumW2(bool) + + operator + (Profile2D) + operator - (Profile2D) + operator / (Profile2D) + +#}}} Profile2D + +# Histo1D#{{{ +cdef extern from "YODA/Histo1D.h" namespace "YODA": + cdef cppclass Histo1D(AnalysisObject): + Histo1D() except +err + Histo1D(string path, string title) except+ err + Histo1D(size_t nbins, + double lower, + double upper, + string path, + string title) except+ err + Histo1D(vector[double] binedges, + string path, + string title) except+ err + Histo1D(vector[Bin] bins, string path, string title) except+ err + Histo1D(Histo1D h, string path) except+ err + #Histo1D(Profile1D p, string path) + #Histo1D(Scatter2D p, string path) + + void fill(double x, double weight) except+ err + void reset() except+ err + + void scaleW(double s) except+ err + void normalize(double normto, bool includeoverflows) except+ err + void mergeBins(size_t, size_t) except+ err + void rebin(int n) except+ err + size_t numBins() except+ err + double lowEdge() except+ err + double highEdge() except+ err + + vector[HistoBin1D] &bins() + HistoBin1D &bin(size_t ix) + HistoBin1D binByCoord(double x) except+ err + + # The trick here is to treat these not as references. + # I suppose when you think about it, it makes sense + Dbn1D &totalDbn() + Dbn1D &underflow() + Dbn1D &overflow() + + # NB, this is bugged and does not correctly report identical bins. + void addBin(double, double) except+ err + void addBins(vector[double] edges) except+ err + void eraseBin(size_t index) except+ err +#}}} Histo1D + +# Histo2D {{{ +cdef extern from "YODA/Histo2D.h" namespace "YODA": + cdef cppclass Histo2D(AnalysisObject): + Histo2D(string path, string title) except+ err + + Histo2D(size_t nBinsX, double lowerX, double upperX, + size_t nBinsY, double lowerY, double upperY, + string path, string title) except+ err + + Histo2D(vector[double] xedges, vector[double] yedges, + string path, string title) except+ err + + Histo2D(Histo2D, string path) + #Histo2D(Profile1D p, string path) + #Histo2D(Scatter2D p, string path) + + void fill(double x, double y, double weight) except+ err + void reset() except+ err + + void scaleW(double scalefactor) except+ err + void normalize(double normto, bool includeoverflows) except+ err + void scaleXY(double, double) + + void mergeBins(size_t, size_t) except+ err + void rebin(int n) except+ err + size_t numBins() except+ err + + vector[HistoBin1D] bins() + HistoBin2D bin(size_t ix) except+ err + HistoBin2D binByCoord(double x) except+ err + + # These must be treated as references or the semantics is wrong. + # However, these can also throw exceptions! But the two cannot mix, or + # Cython puts out rubbish C++. Since this *is* a reported 'major' bug, + # we should expect it to be fixed sometime in the future. + Dbn2D totalDbn() except +err + Dbn2D outflow(int, int) except +err + + # Bin accessors + #void addBin(double, double) except+ err + #void addBins(vector[double] edges) except+ err + void eraseBin(size_t index) except+ err + + double lowEdgeX() except+ err + double lowEdgeY() except+ err + + double highEdgeX() except+ err + double highEdgeY() except+ err + + int findBinIndex(double, double) + size_t numBinsX() + size_t numBinsY() + + # Whole histo data + double integral(bool) + double sumW(bool) + double sumW2(bool) + double xMean(bool) + double yMean(bool) + double xVariance(bool) + double yVariance(bool) + double xStdDev(bool) + double yStdDev(bool) + double xStdErr(bool) + double yStdErr(bool) + + operator == (Histo2D) + operator != (Histo2D) + operator + (Histo2D) + operator - (Histo2D) + operator / (Histo2D) +# Histo2D }}} + +"""INTERNAL CLASSES -- FOR TESTING PURPOSES""" + +# Axis1D {{{ +cdef extern from "YODA/Axis1D.h" namespace "YODA": + cdef cppclass Axis1D[BIN1D, DBN]: + Axis1D() except+ err + Axis1D(vector[double] binedges) except+ err + Axis1D(size_t, double, double) except+ err + Axis1D(vector[BIN1D] bins) except+ err + size_t numBins() except+ err + vector[BIN1D] &bins() + double lowEdge() except+ err + double highEdge() except+ err + long getBinIndex(double) + void reset() + DBN &totalDbn() + DBN &underflow() + DBN &overflow() +# Axis1D }}} + +# Axis2D {{{ +cdef extern from "YODA/Axis2D.h" namespace "YODA": + cdef cppclass Axis2D[BIN2D, DBN]: + Axis2D() except+ err + Axis2D(vector[double] xedges, vector[double] yedges) except+ err + Axis2D(size_t, pair[double, double], + size_t, pair[double, double]) except+ err + Axis2D(vector[BIN2D] bins) except+ err + size_t numBins() except+ err + vector[BIN2D] &bins() + double lowEdgeX() except+ err + double highEdgeX() except+ err + double lowEdgeY() except+ err + double highEdgeY() except+ err + long getBinIndex(double, double) + void reset() + DBN totalDbn() except +err + DBN outflow(int, int) except +err + +# Axis2D }}} + +# Streams {{{ +cdef extern from "<sstream>" namespace "std": + cdef cppclass ostringstream: + ostringstream() + string& str() + +cdef extern from "<sstream>" namespace "std": + cdef cppclass istringstream: + istringstream() + string& str(string &) + +cdef extern from "YODA/Reader.h" namespace "YODA": + cdef cppclass Reader: + void read(istringstream &, vector[AnalysisObject*] &) except +err + +cdef extern from "YODA/ReaderYODA.h" namespace "YODA": + Reader& ReaderYODA_create "YODA::ReaderYODA::create" () + +cdef extern from "YODA/ReaderAIDA.h" namespace "YODA": + Reader& ReaderAIDA_create "YODA::ReaderAIDA::create" () + + +cdef extern from "YODA/Writer.h" namespace "YODA": + cdef cppclass Writer: + void write(ostringstream &, vector[AnalysisObject*] &) + +cdef extern from "YODA/WriterYODA.h" namespace "YODA": + Writer& WriterYODA_create "YODA::WriterYODA::create" () + +cdef extern from "YODA/WriterAIDA.h" namespace "YODA": + Writer& WriterAIDA_create "YODA::WriterAIDA::create" () +# Streams }}} Added: trunk/pyext/yoda/errors.cpp ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ trunk/pyext/yoda/errors.cpp Fri Aug 3 03:14:44 2012 (r514) @@ -0,0 +1,61 @@ +#include "Python.h" + +#include <exception> +#include <stdexcept> +#include <iostream> +#include <typeinfo> + +#include "core.h" +#include "YODA/Exceptions.h" +#include "pyerrors.h" + +void translate_yoda_error() { + try { + if (PyErr_Occurred()) + ; // let the latest Python exn pass through and ignore the current one + else + throw; + } catch (const std::out_of_range& exn) { + PyErr_SetString(PyExc_IndexError, exn.what()); + } catch (const std::bad_cast& exn) { + PyErr_SetString(PyExc_TypeError, exn.what()); + } catch (const std::domain_error& exn) { + PyErr_SetString(PyExc_ValueError, exn.what()); + } catch (const std::invalid_argument& exn) { + PyErr_SetString(PyExc_ValueError, exn.what()); + } catch (const std::ios_base::failure& exn) { + PyErr_SetString(PyExc_IOError, exn.what()); + } catch (const std::overflow_error& exn) { + PyErr_SetString(PyExc_OverflowError, exn.what()); + } catch (const std::range_error& exn) { + PyErr_SetString(PyExc_ArithmeticError, exn.what()); + } catch (const std::underflow_error& exn) { + PyErr_SetString(PyExc_ArithmeticError, exn.what()); + } catch (const std::bad_alloc& exn) { + PyErr_SetString(PyExc_MemoryError, exn.what()); + } catch (const YODA::BinningError& exn) { + PyErr_SetString(YodaExc_BinningError, exn.what()); + } catch (const YODA::RangeError& exn) { + PyErr_SetString(YodaExc_RangeError, exn.what()); + } catch (const YODA::LockError& exn) { + PyErr_SetString(YodaExc_LockError, exn.what()); + } catch (const YODA::GridError& exn) { + PyErr_SetString(YodaExc_GridError, exn.what()); + } catch (const YODA::LogicError& exn) { + PyErr_SetString(YodaExc_LogicError, exn.what()); + } catch (const YODA::WeightError& exn) { + PyErr_SetString(YodaExc_WeightError, exn.what()); + } catch (const YODA::LowStatsError& exn) { + PyErr_SetString(YodaExc_LowStatsError, exn.what()); + } catch (const YODA::AnnotationError& exn) { + PyErr_SetString(YodaExc_AnnotationError, exn.what()); + } catch (const YODA::ReadError& exn) { + PyErr_SetString(YodaExc_ReadError, exn.what()); + } catch (const YODA::UserError& exn) { + PyErr_SetString(YodaExc_UserError, exn.what()); + } catch (const YODA::Exception& exn) { + PyErr_SetString(YodaExc_Exception, exn.what()); + } catch (...) { + PyErr_SetString(PyExc_RuntimeError, "Unknown exception"); + } +} Added: trunk/pyext/yoda/errors.hh ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ trunk/pyext/yoda/errors.hh Fri Aug 3 03:14:44 2012 (r514) @@ -0,0 +1,6 @@ +// Not sure how this works. Isn't that nice, familiar territory? :) I think what +// actually happens is that when a C++ exception happens, the exception is +// somehow handed over to this function (probably by stack magic) and allows us +// to do our thing. + +void translate_yoda_error(); Added: trunk/pyext/yoda/include/AnalysisObject.pyx ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ trunk/pyext/yoda/include/AnalysisObject.pyx Fri Aug 3 03:14:44 2012 (r514) @@ -0,0 +1,92 @@ +from cython.operator cimport dereference as deref, preincrement as preinc +from cStringIO import StringIO + +cdef void set_ann(c.AnalysisObject *ana, char *k, char *v): + ana.setAnnotation(string(k), string(v)) + +# AnalysisObject is the base class of most of the user facing objects +cdef class AnalysisObject(util.Base): + + # Pointer upcasting mechanism + cdef inline c.AnalysisObject *_AnalysisObject(self) except NULL: + return <c.AnalysisObject *> self.ptr() + + # Deallocator (only needed as a base class) + def __dealloc__(self): + cdef c.AnalysisObject *p = self._AnalysisObject() + if self._deallocate: + del p + + def annotations(self): + """ + Key value pairs of metadata, returned as a dictionary. + + """ + ana = self._AnalysisObject().annotations() + it = ana.begin() + out_dict = {} + while it != ana.end(): + out_dict[deref(it).first.c_str()] = deref(it).second.c_str() + preinc(it) + return out_dict + + def string(self): + """ + A human readable representation of this object as it would be + stored in a YODA file. + + """ + f = StringIO() + writeYODA([self], f) + f.seek(0) + return f.read().strip() + + + def updateAnnotations(self, E=None, **F): + """ + AO.update([E, ]**F) -> None. Update annotations of AO from + dict/iterable E and F. + + If E present and has a .keys() method: + for k in E: AO[k] = E[k] + If E present and lacks .keys() method: + for (k, v) in E: AO[k] = v + In either case, this is followed by: + for k in F: AO[k] = F[k] + + """ + AO = self._AnalysisObject() + if E is not None: + if hasattr(E, 'keys'): + for k in E: + set_ann(AO, k, E[k]) + else: + for k, v in E: + set_ann(AO, k, v) + + for k in F: + set_ann(AO, k, F[k]) + + property path: + """ + Used for persistence and as a unique identifier. Must begin with + a '/' if not the empty string. + + """ + + def __get__(self): + return self._AnalysisObject().path().c_str() + + def __set__(self, char *path): + self._AnalysisObject().setPath(string(path)) + + property title: + def __get__(self): + return self._AnalysisObject().title().c_str() + + def __set__(self, char *title): + self._AnalysisObject().setTitle(string(title)) + + + def __repr__(self): + return "<%s '%s'>" % (self.__class__.__name__, self.path) Added: trunk/pyext/yoda/include/Annotations.pyx ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ trunk/pyext/yoda/include/Annotations.pyx Fri Aug 3 03:14:44 2012 (r514) @@ -0,0 +1,40 @@ +# +# Most definitely not a good idea, but useful as a warning +# +#from libc.stdlib cimport malloc +# + + +#ctypedef map[string,string].iterator mapiter +# +#cdef class Annotations_itemiterator(): +# cdef mapiter *it +# +# def __cinit__(self): +# self.it = <mapiter *> malloc(sizeof(mapiter)) +# +# +#cdef class Annotations(util.Base): +# cdef c.AnalysisObject *_AnalysisObject(self) except NULL: +# return <c.AnalysisObject *> self.ptr() +# +# +# def __getitem__(self, char *key): +# return self._AnalysisObject().annotation(string(key)).c_str() +# +# def __setitem__(self, char *key, char *value): +# self._AnalysisObject().setAnnotation(string(key), string(value)) +# +# # Doesn't actually iterate... +# def iteritems(self): +# it = self.it +# it[0] = self._AnalysisObject().annotations().begin() +# +# while True: +# yield deref(it[0]).first.c_str(), deref(it[0]).second.c_str() +# preinc(it[0]) +# if it[0] == self._AnalysisObject().annotations().end(): +# return +# +# def __repr__(self): +# return '{%s}' % ', '.join('%r: %r' % i for i in self.iteritems()) Added: trunk/pyext/yoda/include/Axis1D_BIN1D_DBN.pyx ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ trunk/pyext/yoda/include/Axis1D_BIN1D_DBN.pyx Fri Aug 3 03:14:44 2012 (r514) @@ -0,0 +1,47 @@ +cdef class Axis1D_${BIN1D}_${DBN}(util.Base): + + def __init__(self, size_t nbins, double lower, double upper): + util.set_owned_ptr( + self, new c.Axis1D[c.${BIN1D}, c.${DBN}]( + nbins, lower, upper)) + + def __len__(self): + return self._Axis1D().bins().size() + + def __getitem__(self, py_ix): + cdef size_t i = util.pythonic_index(py_ix, self._Axis1D().bins().size()) + return util.new_borrowed_cls( + ${BIN1D}, & self._Axis1D().bins().at(i), self) + + def __repr__(self): + return "<Axis1D>" + + @property + def totalDbn(self): + return util.new_borrowed_cls( + ${DBN}, &self._Axis1D().totalDbn(), self) + + @property + def underflow(self): + return util.new_borrowed_cls( + ${DBN}, &self._Axis1D().underflow(), self) + + @property + def overflow(self): + return util.new_borrowed_cls( + ${DBN}, &self._Axis1D().overflow(), self) + + def reset(self): + self._Axis1D().reset() + + def bin_at(self, x): + return self[self._Axis1D().getBinIndex(x)] + + # BOILERPLATE STUFF + cdef inline c.Axis1D[c.${BIN1D}, c.${DBN}] *_Axis1D(self) except NULL: + return <c.Axis1D[c.${BIN1D}, c.${DBN}]*> self.ptr() + + def __dealloc__(self): + cdef c.Axis1D[c.${BIN1D}, c.${DBN}] *p = self._Axis1D() + if self._deallocate: + del p Added: trunk/pyext/yoda/include/Axis2D_BIN2D_DBN.pyx ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ trunk/pyext/yoda/include/Axis2D_BIN2D_DBN.pyx Fri Aug 3 03:14:44 2012 (r514) @@ -0,0 +1,53 @@ +cdef class Axis2D_${BIN2D}_${DBN}(util.Base): + + def __init__(self, nx, xl, xu, ny, yl, yu): + util.set_owned_ptr( + self, new c.Axis2D[c.${BIN2D}, c.${DBN}]( + nx, pair[double, double](xl, xu), + ny, pair[double, double](yl, yu))) + + def __len__(self): + return self._Axis2D().bins().size() + + def __getitem__(self, py_ix): + cdef size_t i = util.pythonic_index(py_ix, self._Axis2D().bins().size()) + return util.new_borrowed_cls( + ${BIN2D}, & self._Axis2D().bins().at(i), self) + + def __repr__(self): + return "<Axis2D>" + + @property + def totalDbn(self): + return util.new_owned_cls( + ${DBN}, new c.${DBN}(self._Axis2D().totalDbn())) + + @property + def outflow(self, ix, iy): + return util.new_owned_cls( + ${DBN}, new c.${DBN}(self._Axis2D().outflow(ix, iy))) + + @property + def edges(self): + return XY( + Edge(self._Axis2D().lowEdgeX(), self._Axis2D().highEdgeX()), + Edge(self._Axis2D().lowEdgeY(), self._Axis2D().highEdgeY()) + ) + + def reset(self): + self._Axis2D().reset() + + def binByCoord(self, x, y): + cdef int ix = self._Axis2D().getBinIndex(x, y) + if ix < 0: + raise YodaExc_RangeError('No bin found!') + return self[ix] + + # BOILERPLATE STUFF + cdef inline c.Axis2D[c.${BIN2D}, c.${DBN}] *_Axis2D(self) except NULL: + return <c.Axis2D[c.${BIN2D}, c.${DBN}]*> self.ptr() + + def __dealloc__(self): + cdef c.Axis2D[c.${BIN2D}, c.${DBN}] *p = self._Axis2D() + if self._deallocate: + del p Added: trunk/pyext/yoda/include/Bin.pyx ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ trunk/pyext/yoda/include/Bin.pyx Fri Aug 3 03:14:44 2012 (r514) @@ -0,0 +1,8 @@ +cdef class Bin(util.Base): + cdef inline c.Bin *_Bin(self) except NULL: + return <c.Bin *> self.ptr() + + def __dealloc__(self): + cdef c.Bin *p = self._Bin() + if self._deallocate: + del p Added: trunk/pyext/yoda/include/Bin1D_DBN.pyx ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ trunk/pyext/yoda/include/Bin1D_DBN.pyx Fri Aug 3 03:14:44 2012 (r514) @@ -0,0 +1,157 @@ +cdef class Bin1D_${DBN}(Bin): + """ + 1D Bin based on an underlying ${DBN}. + + ProfileBin1D(xlow, xhigh) + + """ + + def __init__(self, xlow, xhigh): + util.set_owned_ptr( + self, new c.Bin1D_${DBN}(pair[double, double](xlow, xhigh))) + + def scale(self, x=1.0, w=1.0): + """ + scale(x=1.0, w=1.0) -> None. Scale this bin's arguments by + their respective scalefactors. + + """ + if w != 1.0: + self._Bin1D().scaleW(w) + if x != 1.0: + self._Bin1D().scaleX(x) + + @property + def edges(self): + """The lower and upper edges.""" + return util.Edges(self._Bin1D().lowEdge(), + self._Bin1D().highEdge()) + + @property + def width(self): + """The width of the bin.""" + return self._Bin1D().width() + + @property + def focus(self): + """ + The focus of the bin. If the bin has been filled, then this + is the mean fill on this bin. If the bin has not been filled, + then the focus is the midpoint of the bin. + + """ + return self._Bin1D().focus() + + @property + def midpoint(self): + """The point half-way between the two bin edges.""" + return self._Bin1D().midpoint() + + @property + def mean(self): + """The mean of the x-values that have filled the bin.""" + return self._Bin1D().xMean() + + @property + def stdDev(self): + """ + The standard deviation of the x-values that have filled the bin. + + """ + return self._Bin1D().xStdDev() + + @property + def stdErr(self): + """ + The standard error of the x-values that have filled the bin. + + """ + return self._Bin1D().xStdErr() + + @property + def rms(self): + """ + The root-mean-square of the x-values that have filled the bin. + + """ + return self._Bin1D().xRMS() + + ## + ## Raw statistics + ## + + @property + def numEntries(self): + """ + The number of entries that have filled the bin. + + """ + return self._Bin1D().numEntries() + + @property + def effNumEntries(self): + """ + The effective number of entries in the bin. + + s.effNumEntries <==> (s.sumW ** 2) / s.sumW2 + + """ + return self._Bin1D().effNumEntries() + + @property + def sumW(self): + """ + The sum of weights: sum(weights). + + """ + return self._Bin1D().sumW() + + @property + def sumW2(self): + """ + The sum of weights-squared: sum(weights * weights) + + """ + return self._Bin1D().sumW2() + + @property + def sumWX(self): + """ + The sum of weights-times-x: sum(weights * x) + + """ + return self._Bin1D().sumWX() + + @property + def sumWX2(self): + """ + The sum of weights-times-x-squared: sum(weights * x * x) + + """ + return self._Bin1D().sumWX2() + + def merge(Bin1D_${DBN} self, Bin1D_${DBN} other): + """ + merge(other) -> Bin1D_${DBN}. Merge this bin with another of the + same type. Only directly adjacent bins, i.e. those sharing a + common edge, can be merged. + + """ + self._Bin1D().merge(deref(other._Bin1D())) + return self + + def __repr__(self): + return '<%s[%g, %g)>' % ((self.__class__.__name__,) + self.edges) + + def __add__(Bin1D_${DBN} self, Bin1D_${DBN} other): + return util.new_owned_cls( + Bin1D_${DBN}, + new c.Bin1D_${DBN}(deref(self._Bin1D()) + deref(other._Bin1D()))) + + def __sub__(Bin1D_${DBN} self, Bin1D_${DBN} other): + return util.new_owned_cls( + Bin1D_${DBN}, + new c.Bin1D_${DBN}(deref(self._Bin1D()) - deref(other._Bin1D()))) + + cdef inline c.Bin1D_${DBN} *_Bin1D(self) except NULL: + return <c.Bin1D_${DBN} *> self.ptr() Added: trunk/pyext/yoda/include/Bin2D_DBN.pyx ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ trunk/pyext/yoda/include/Bin2D_DBN.pyx Fri Aug 3 03:14:44 2012 (r514) @@ -0,0 +1,112 @@ + +cdef class Bin2D_${DBN}(Bin): + """1D Bin class templated on a ${DBN}""" + + def __init__(self, xlow, xhigh, ylow, yhigh): + util.set_owned_ptr( + self, new c.Bin2D_${DBN}( + pair[double, double](xlow, xhigh), + pair[double, double](ylow, yhigh), + )) + + def scale(self, x=1.0, y=1.0, w=1.0): + if x != 1.0 or y != 1.0: + self.scaleXY(x, y) + + if w != 1.0: + self.scaleW(w) + + @property + def edges(self): + """The lower and upper edges.""" + cdef pair[double, double] x = self._Bin2D().xedges() + cdef pair[double, double] y = self._Bin2D().yedges() + return XY(util.Edges(x.first, x.second), + util.Edges(y.first, y.second)) + + @property + def widths(self): + """The widths of this bin in the x- and y-dimensions.""" + return XY(self._Bin2D().widthX(), self._Bin2D().widthY()) + + @property + def focus(self): + """The focus of the bin in the x- and y-dimensions""" + cdef pair[double, double] f = self._Bin2D().focus() + return XY(f.first, f.second) + + @property + def midpoint(self): + cdef pair[double, double] f = self._Bin2D().midpoint() + return XY(f.first, f.second) + + @property + def mean(self): + return XY(self._Bin2D().xMean(), self._Bin2D().yMean()) + + @property + def std_dev(self): + return XY(self._Bin2D().xStdDev(), self._Bin2D().yStdDev()) + + @property + def std_err(self): + return XY(self._Bin2D().xStdErr(), self._Bin2D().yStdErr()) + + @property + def rms(self): + return XY(self._Bin2D().xRMS(), self._Bin2D().yRMS()) + + # Raw statistics # + ################## + @property + def count(self): + return self._Bin2D().numEntries() + + @property + def effective_count(self): + return self._Bin2D().effNumEntries() + + @property + def sum_w(self): + return self._Bin2D().sumW() + + @property + def sum_w2(self): + return self._Bin2D().sumW2() + + @property + def sum_wx(self): + return self._Bin2D().sumWX() + + @property + def sum_wy(self): + return self._Bin2D().sumWY() + + @property + def sum_wxy(self): + return self._Bin2D().sumWXY() + + @property + def sum_wx2(self): + return self._Bin2D().sumWX2() + + @property + def sum_wy2(self): + return self._Bin2D().sumWY2() + + #def merge(Bin2D_${DBN} self, Bin2D_${DBN} other): + # self._Bin2D().merge(deref(other._Bin2D())) + # return self + + def __add__(Bin2D_${DBN} self, Bin2D_${DBN} other): + return util.new_owned_cls( + Bin2D_${DBN}, + new c.Bin2D_${DBN}(deref(self._Bin2D()) + deref(other._Bin2D()))) + + def __sub__(Bin2D_${DBN} self, Bin2D_${DBN} other): + return util.new_owned_cls( + Bin2D_${DBN}, + new c.Bin2D_${DBN}(deref(self._Bin2D()) - deref(other._Bin2D()))) + + cdef inline c.Bin2D_${DBN} *_Bin2D(self) except NULL: + return <c.Bin2D_${DBN} *> self.ptr() Added: trunk/pyext/yoda/include/Dbn1D.pyx ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ trunk/pyext/yoda/include/Dbn1D.pyx Fri Aug 3 03:14:44 2012 (r514) @@ -0,0 +1,116 @@ +cdef class Dbn1D(util.Base): + """ + A 1D distribution 'counter', used and exposed by 1D histograms and their bins. + + """ + + def __init__(self): + util.set_owned_ptr(self, new c.Dbn1D()) + + def copy(self): + return util.set_owned_ptr(self, new c.Dbn1D(deref(self._Dbn1D()))) + + def fill(self, x, weight=1.0): + """ + (float x, float weight=1.0) -> None + + Fills the distribution with the given weight at given x. + + """ + self._Dbn1D().fill(x, weight) + + def reset(self): + """ + () -> None + + Reset the distribution counters to the unfilled state.""" + self._Dbn1D().reset() + + + def scale(self, x=1.0, w=1.0): + """ + (x=1.0, w=1.0) -> None + + Scale the Dbn's variables by the given factors + + """ + if x != 1.0: + self._Dbn1D().scaleX(x) + if w != 1.0: + self._Dbn1D().scaleW(w) + + @property + def mean(self): + """Weighted mean of x""" + return (self._Dbn1D().xMean()) + + @property + def variance(self): + """Weighted variance of x""" + return (self._Dbn1D().xVariance()) + + @property + def std_dev(self): + """Weighted standard deviation of x""" + return (self._Dbn1D().xStdDev()) + + @property + def std_err(self): + """Weighted standard error on <x>""" + return (self._Dbn1D().xStdErr()) + + @property + def rms(self): + """Weighted root mean squared (RMS) of x""" + return (self._Dbn1D().xRMS()) + + @property + def count(self): + """The number of entries""" + return self._Dbn1D().numEntries() + + @property + def effective_count(self): + """Effective number of entries (for weighted events)""" + return self._Dbn1D().numEntries() + + @property + def sum_w(self): + """sum(weights)""" + return self._Dbn1D().sumW() + + @property + def sum_w2(self): + """sum(weights**2)""" + return self._Dbn1D().sumW2() + + @property + def sum_wx(self): + """sum(weights * xs)""" + return self._Dbn1D().sumWX() + + @property + def sum_wx2(self): + """sum(weights * xs**2)""" + return self._Dbn1D().sumWX2() + + def __add__(Dbn1D self, Dbn1D other): + return util.new_owned_cls(Dbn1D, new c.Dbn1D( + deref(self._Dbn1D()) + deref(other._Dbn1D()))) + + def __sub__(Dbn1D self, Dbn1D other): + return util.new_owned_cls(Dbn1D, new c.Dbn1D( + deref(self._Dbn1D()) - deref(other._Dbn1D()))) + + def __repr__(self): + return 'Dbn1D(mean=%g, stdDev=%g)' % (self.mean, self.stdDev) + + # Magic stuff + cdef c.Dbn1D *_Dbn1D(self) except NULL: + return <c.Dbn1D *> self.ptr() + + def __dealloc__(self): + cdef c.Dbn1D *p = self._Dbn1D() + if self._deallocate: + del p + Added: trunk/pyext/yoda/include/Dbn2D.pyx ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ trunk/pyext/yoda/include/Dbn2D.pyx Fri Aug 3 03:14:44 2012 (r514) @@ -0,0 +1,155 @@ + +cdef class Dbn2D(util.Base): + """ + A 2D distribution 'counter', used and exposed by 2D histograms and + 1D profiles and their bins. + + """ + + def __init__(self): + util.set_owned_ptr(self, new c.Dbn2D()) + + def copy(self): + return util.new_owned_cls(Dbn2D, new c.Dbn2D(deref(self._Dbn2D()))) + + def fill(self, x, y, weight=1.0): + """ + (x, y, weight=1.0) -> None + + Fills the distribution with the given weight at given (x, y). + + """ + self._Dbn2D().fill(x, y, weight) + + def fill_many(self, xs, ys, ws=repeat(1)): + """ + (xs, ys, ws=repeat(1.0)) -> None + + Fills the distribution from iterables xs, ys and ws. + + """ + cdef c.Dbn2D *ptr = self._Dbn2D() + itx = iter(xs) + ity = iter(ys) + itw = iter(ws) + while True: + x = next(xs, None) + y = next(ys, None) + w = next(ws, None) + if x is None or y is None or w is None: + break + ptr.fill(x, y, w) + + def reset(self): + """ + () -> None + + Reset the distribution counters to the unfilled state.""" + self._Dbn2D().reset() + + def scale(self, x=1.0, y=1.0, w=1.0): + """ + (x=1.0, y=1.0, w=1.0) -> None + + Scale Dbn2D's parameters + + """ + if x != 1.0 and y != 1.0: + self._Dbn2D().scaleXY(x, y) + elif x != 1.0: + self._Dbn2D().scaleX(x) + elif y != 1.0: + self._Dbn2D().scaleY(y) + if w != 1.0: + self._Dbn2D().scaleW(w) + + @property + def mean(self): + """Weighted mean of x""" + return util.XY(self._Dbn2D().xMean(), self._Dbn2D().yMean()) + + @property + def variance(self): + """Weighted variance of x""" + return util.XY(self._Dbn2D().xVariance(), self._Dbn2D().yVariance()) + + @property + def std_dev(self): + """Weighted standard deviation of x""" + return util.XY(self._Dbn2D().xStdDev(), self._Dbn2D().yStdDev()) + + @property + def std_err(self): + """Weighted standard error on <x>""" + return util.XY(self._Dbn2D().xStdErr(), self._Dbn2D().yStdErr()) + + @property + def rms(self): + """Weighted root mean squared (RMS) of x""" + return util.XY(self._Dbn2D().xRMS(), self._Dbn2D().yRMS()) + + @property + def count(self): + """The number of entries""" + return self._Dbn2D().numEntries() + + @property + def effective_count(self): + """Effective number of entries (for weighted events)""" + return self._Dbn2D().effNumEntries() + + @property + def sum_w(self): + """sum(weights)""" + return self._Dbn2D().sumW() + + @property + def sum_w2(self): + """sum(weights**2)""" + return self._Dbn2D().sumW2() + + @property + def sum_wx(self): + """sum(weights * xs)""" + return self._Dbn2D().sumWX() + + @property + def sum_wy(self): + """sum(weights * ys)""" + return self._Dbn2D().sumWY() + + @property + def sum_wx2(self): + """sum(weights * xs**2)""" + return self._Dbn2D().sumWX2() + + @property + def sum_wy2(self): + """sum(weights * ys**2)""" + return self._Dbn2D().sumWY2() + + @property + def sum_wxy(self): + """sum(weights xs * ys)""" + return self._Dbn2D().sumWXY() + + def __add__(Dbn2D self, Dbn2D other): + return util.new_owned_cls(Dbn2D, new c.Dbn2D( + deref(self._Dbn2D()) + deref(other._Dbn2D()))) + + def __sub__(Dbn2D self, Dbn2D other): + return util.new_owned_cls(Dbn2D, new c.Dbn2D( + deref(self._Dbn2D()) - deref(other._Dbn2D()))) + + def __repr__(self): + return 'Dbn2D(mean=(%g, %g), stdDev=(%g, %g))' % (self.mean + self.std_dev) + + # Magic stuff + cdef c.Dbn2D *_Dbn2D(self) except NULL: + return <c.Dbn2D *> self.ptr() + + def __dealloc__(self): + cdef c.Dbn2D *p = self._Dbn2D() + if self._deallocate: + del p + Added: trunk/pyext/yoda/include/Dbn3D.pyx ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ trunk/pyext/yoda/include/Dbn3D.pyx Fri Aug 3 03:14:44 2012 (r514) @@ -0,0 +1,198 @@ +from collections import namedtuple +from itertools import izip, repeat + +cdef class Dbn3D(util.Base): + """ + A 2D distribution 'counter', used and exposed by 2D histograms and + 1D profiles and their bins. + + """ + def __init__(self): + util.set_owned_ptr(self, new c.Dbn3D()) + + def copy(self): + return util.new_owned_cls(Dbn3D, new c.Dbn3D(deref(self._Dbn3D()))) + + def fill(self, x, y, z, weight=1.0): + """ + (x, y, z, weight=1.0) -> None + + Fills the distribution with the given weight at given (x, y). + + """ + self._Dbn3D().fill(x, y, z, weight) + + def fill_many(self, xs, ys, zs, ws=repeat(1)): + """ + (xs, ys, ws=repeat(1.0)) -> None + + Fills the distribution from iterables xs, ys and ws. + + """ + cdef c.Dbn3D *ptr = self._Dbn3D() + itx = iter(xs) + ity = iter(ys) + itz = iter(zs) + itw = iter(ws) + while True: + x = next(xs, None) + y = next(ys, None) + z = next(zs, None) + w = next(ws, None) + if x is None or y is None or w is None: + break + ptr.fill(x, y, z, w) + + + def reset(self): + """ + () -> None + + Reset the distribution counters to the unfilled state.""" + self._Dbn3D().reset() + + def scale(self, x=1.0, y=1.0, z=1.0, w=1.0): + """ + (x=1.0, y=1.0, w=1.0) -> None + + Scale Dbn3D's parameters + + """ + if x != 1.0 and y != 1.0 and z != 1.0: + self._Dbn3D().scaleXYZ(x, y, z) + else: + if x != 1.0: + self._Dbn3D().scaleX(x) + if y != 1.0: + self._Dbn3D().scaleY(x) + if z != 1.0: + self._Dbn3D().scaleZ(x) + + if w != 1.0: + self._Dbn3D().scaleW(w) + + @property + def mean(self): + """Weighted mean of x""" + return util.XYZ(self._Dbn3D().xMean(), + self._Dbn3D().yMean(), + self._Dbn3D().zMean()) + + @property + def variance(self): + """Weighted variance of x""" + return util.XYZ(self._Dbn3D().xVariance(), + self._Dbn3D().yVariance(), + self._Dbn3D().zVariance()) + + @property + def std_dev(self): + """Weighted standard deviation of x""" + return util.XYZ(self._Dbn3D().xStdDev(), + self._Dbn3D().yStdDev(), + self._Dbn3D().zStdDev()) + + @property + def std_err(self): + """Weighted standard error on <x>""" + return util.XYZ(self._Dbn3D().xStdErr(), + self._Dbn3D().yStdErr(), + self._Dbn3D().zStdErr()) + + @property + def rms(self): + """Weighted root mean squared (RMS) of x""" + return util.XYZ(self._Dbn3D().xRMS(), + self._Dbn3D().yRMS(), + self._Dbn3D().zRMS()) + + @property + def count(self): + """The number of entries""" + return self._Dbn3D().numEntries() + + @property + def effective_count(self): + """Effective number of entries (for weighted events)""" + return self._Dbn3D().effNumEntries() + + @property + def sum_w(self): + """sum(weights)""" + return self._Dbn3D().sumW() + + @property + def sum_w2(self): + """sum(weights**2)""" + return self._Dbn3D().sumW2() + + @property + def sum_wx(self): + """sum(weights * xs)""" + return self._Dbn3D().sumWX() + + @property + def sum_wy(self): + """sum(weights * ys)""" + return self._Dbn3D().sumWY() + + @property + def sum_wz(self): + """sum(weights * ys)""" + return self._Dbn3D().sumWZ() + + @property + def sum_wx2(self): + """sum(weights * xs**2)""" + return self._Dbn3D().sumWX2() + + @property + def sum_wy2(self): + """sum(weights * ys**2)""" + return self._Dbn3D().sumWY2() + + @property + def sum_wz2(self): + """sum(weights * ys)""" + return self._Dbn3D().sumWZ2() + + @property + def sum_wxy(self): + """sum(weights xs * ys)""" + return self._Dbn3D().sumWXY() + + @property + def sum_wxz(self): + """sum(weights xs * ys)""" + return self._Dbn3D().sumWXZ() + + @property + def sum_wyz(self): + """sum(weights xs * ys)""" + return self._Dbn3D().sumWYZ() + + @property + def sum_wxyz(self): + """sum(weights xs * ys)""" + return self._Dbn3D().sumWXYZ() + + def __add__(Dbn3D self, Dbn3D other): + return util.new_owned_cls(Dbn3D, new c.Dbn3D( + deref(self._Dbn3D()) + deref(other._Dbn3D()))) + + def __sub__(Dbn3D self, Dbn3D other): + return util.new_owned_cls(Dbn3D, new c.Dbn3D( + deref(self._Dbn3D()) - deref(other._Dbn3D()))) + + def __repr__(self): + return 'Dbn3D(mean=(%g, %g), stdDev=(%g, %g))' % (self.mean + self.std_dev) + + # Magic stuff + cdef c.Dbn3D *_Dbn3D(self) except NULL: + return <c.Dbn3D *> self.ptr() + + def __dealloc__(self): + cdef c.Dbn3D *p = self._Dbn3D() + if self._deallocate: + del p + Added: trunk/pyext/yoda/include/Errors.pyx ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ trunk/pyext/yoda/include/Errors.pyx Fri Aug 3 03:14:44 2012 (r514) @@ -0,0 +1,37 @@ +from cpython.exc cimport PyErr_NewException + +# Create public exceptions -- these will be available for inclusion from an +# external C++ module using 'core.h' or similar, and also be available from +# within Cython. + +# These are called from translate_yoda_error in errors.cpp + +cpdef public: + # Master exception + object YodaExc_Exception = PyErr_NewException( + "yoda.Exception", <object> NULL, <object> NULL) + + # Inherited exceptions + object YodaExc_BinningError = PyErr_NewException( + "yoda.BinningError", YodaExc_Exception, <object> NULL) + object YodaExc_RangeError = PyErr_NewException( + "yoda.RangeError", YodaExc_Exception, <object> NULL) + object YodaExc_LockError = PyErr_NewException( + "yoda.LockError", YodaExc_Exception, <object> NULL) + object YodaExc_GridError = PyErr_NewException( + "yoda.GridError", YodaExc_Exception, <object> NULL) + object YodaExc_LogicError = PyErr_NewException( + "yoda.LogicError", YodaExc_Exception, <object> NULL) + object YodaExc_LowStatsError = PyErr_NewException( + "yoda.WeightError", YodaExc_Exception, <object> NULL) + object YodaExc_WeightError = PyErr_NewException( + "yoda.LowStatsError", YodaExc_Exception, <object> NULL) + object YodaExc_AnnotationError = PyErr_NewException( + "yoda.AnnotationError", YodaExc_Exception, <object> NULL) + object YodaExc_ReadError = PyErr_NewException( + "yoda.ReadError", YodaExc_Exception, <object> NULL) + object YodaExc_UserError = PyErr_NewException( + "yoda.UserError", YodaExc_Exception, <object> NULL) + +# Note that these don't appear in python space due to the cdef. What we will +# have to do is use some magic to make these appear in Python space. Added: trunk/pyext/yoda/include/Histo1D.pyx ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ trunk/pyext/yoda/include/Histo1D.pyx Fri Aug 3 03:14:44 2012 (r514) @@ -0,0 +1,164 @@ + + + +cdef class Histo1D(AnalysisObject): + """ + 1D histogram. Complete histogramming is supported, including + uniform/regular binning, variable wideth bininng, unbinned gaps in + the covered range, and under/overflows. Rebinning by integer + factors, or by explicit merging of contiguous bins is also + supported. + + Rescaling of weights and/or the x axis is permitted in-place: the + result is still a valid Histo1D. Binning-compatible 1D histograms + may be divided, resulting in a Scatter2D since further fulls would + not be meaningful. + + Several sets of arguments are tried by the constructor in the + following order. + + Histo1D(path="", title=""). Construct a histogram with optional path + and title but no bins. + + Histo1D(B, path="", title=""). Construct a histogram from an + iterator of bins, B. + + Histo1D(E, path="", title=""). Construct a histogram from an + iterator of edges, E. + + Should this constructor fail, then + Histo1D(nbins, low, high, path="", title="") + """ + + # Is there a reason why this sounds like an advert? I'M ALREADY SOLD + # DAMMIT! + + + cdef inline c.Histo1D *_Histo1D(self) except NULL: + return <c.Histo1D*> self.ptr() + + # There is a pythonic constructor here, and it looks a little like... + # __init__(self, *args, **kwargs) + # ([edge], path="", title="") + # ([bins], **kwargs) + + def __init__(self, *args, **kwargs): + # Multimethod constructors are blatantly unpythonic. However, + # doing something like the inbuilt collections do would be + # really neat. So we will use an iterable of bins in place of + # the "from other type" constructors, and have a copy() method + # which has exactly the same semantics. + util.try_loop([self.__init2, self.__init5, self.__init3], *args, **kwargs) + + def __init3(self, bins, char *path="", char *title=""): + # Make an iterator over bins. We might as well make our code + # general, as that increases pythonicity. + self.__init2(path, title) + self.addBins(bins) + + def __init2(self, char *path="", char *title=""): + util.set_owned_ptr( + self, new c.Histo1D(string(path), string(title))) + + def __init5(self, nbins, low, high, char *path="", char *title=""): + util.set_owned_ptr( + self, new c.Histo1D( + nbins, low, high, string(path), string(title))) + + def __len__(self): + return self._Histo1D().numBins() + + def __getitem__(self, py_ix): + cdef size_t i = util.pythonic_index( + py_ix, self._Histo1D().numBins()) + + return util.new_borrowed_cls( + HistoBin1D, & self._Histo1D().bin(i), self) + + def fill(self, x, weight=1.0): + """ + (x, weight=1.0) -> None. Fill with given x and optional weight. + + """ + self._Histo1D().fill(x, weight) + + def copy(self, char *path=""): + """(path="") -> Histo1D. Clone this Histo1D with optional new path.""" + return util.new_owned_cls(Histo1D, + new c.Histo1D(deref(self._Histo1D()), string(path))) + + def bins(self): + return list(self) + + @property + def edges(self): + return util.Edges(self._Histo1D().lowEdge(), + self._Histo1D().highEdge()) + + @property + def totalDbn(self): + """The Dbn1D representing the total distribution.""" + # Now does this actually involve a copy in Cython? We should probably + # find out! + return util.new_borrowed_cls( + Dbn1D, &self._Histo1D().totalDbn(), self) + + def reset(self): + """ + Reset the histogram but leave the bin structure. + + """ + self._Histo1D().reset() + + def scale(self, w=1.0): + """ + (double w=1.0) -> None. Scale Histogram and its statistics as if all + weights had been scaled by given factor. + + """ + self._Histo1D().scaleW(w) + + def normalize(self, normto=1.0, includeOverflows=True): + """ + (normto=1.0, includeOverflows=False) -> None. Normalize the histogram. + + """ + self._Histo1D().normalize(normto, includeOverflows) + + def mergeBins(self, a, b): + """mergeBins(a, b) -> None. Merge bins from indices a through b.""" + self._Histo1D().mergeBins(a, b) + + def rebin(self, n): + """(n) -> None. Merge every nth bin.""" + self._Histo1D().rebin(n) + + def addBin(self, low, high): + """(low, high) -> None. Add a bin.""" + self._Histo1D().addBin(low, high) + + def addBins(self, edges_or_bins): + arg = list(edges_or_bins) + util.try_loop([self.__addBins_edges, + self.__addBins_tuples, + self.__addBins_points, + self.__addBins_bins], arg) + + def __addBins_edges(self, edges): + cdef vector[double] cedges + for edge in edges: + cedges.push_back(edge) + + if len(edges): + self._Histo1D().addBins(cedges) + + def __addBins_bins(self, bins): + self.__addBins_tuples(imap(attrgetter('edges'), bins)) + + def __addBins_points(self, points): + self.__addBins_tuples(imap(attrgetter('xRange'), points)) + + def __addBins_tuples(self, tuples): + cdef double a, b + for a, b in tuples: + self._Histo1D().addBin(a, b) Added: trunk/pyext/yoda/include/Histo2D.pyx ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ trunk/pyext/yoda/include/Histo2D.pyx Fri Aug 3 03:14:44 2012 (r514) @@ -0,0 +1,111 @@ +cdef class Histo2D(AnalysisObject): + + cdef inline c.Histo2D *_Histo2D(self) except NULL: + return <c.Histo2D*> self.ptr() + + def __init__(self, *args, **kwargs): + if len(args) <= 2: + self.__init2(*args, **kwargs) + else: + self.__init7(*args, **kwargs) + + def __init2(Histo2D self, char *path="", char *title=""): + util.set_owned_ptr( + self, new c.Histo2D(string(path), string(title))) + + def __init7(Histo2D self, nxbins, xlow, xupp, nybins, ylow, yupp, + char *path="", char *title=""): + + util.set_owned_ptr( + self, new c.Histo2D( + nxbins, xlow, xupp, + nybins, ylow, yupp, + string(path), string(title))) + + def __len__(self): + return self._Histo2D().bins().size() + + def __getitem__(self, py_ix): + cdef size_t i = util.pythonic_index(py_ix, self._Histo2D().bins().size()) + return util.new_borrowed_cls( + HistoBin2D, & self._Histo2D().bins().at(i), self) + + def __repr__(self): + return "Histo2D" + + def fill(self, x, y, weight=1.0): + self._Histo2D().fill(x, y, weight) + + def copy(self, char *path=""): + return util.new_owned_cls(Histo2D, + new c.Histo2D(deref(self._Histo2D()), string(path))) + + @property + def total_dbn(self): + return util.new_owned_cls( + Dbn2D, + new c.Dbn2D(self._Histo2D().totalDbn())) + + def outflow(self, ix=0, iy=0): + # For now we do manual error detection, due to exceptions and references + return util.new_owned_cls( + Dbn2D, + new c.Dbn2D(self._Histo2D().outflow(ix, iy))) + + def sum_w(self, overflows=True): + return self._Histo2D().sumW(overflows) + + def mean(self, overflows=True): + return XY( + self._Histo2D().xMean(overflows), + self._Histo2D().yMean(overflows)) + + def variance(self, overflows=True): + return XY( + self._Histo2D().xVariance(overflows), + self._Histo2D().yVariance(overflows)) + + def std_dev(self, overflows=True): + return XY( + self._Histo2D().xStdDev(overflows), + self._Histo2D().yStdDev(overflows)) + + def std_err(self, overflows=True): + return XY( + self._Histo2D().xStdErr(overflows), + self._Histo2D().yStdErr(overflows)) + + def reset(self): + self._Histo2D().reset() + + def scale(self, w=1.0): + """ + (w=1.0) -> None + + Scale the given parameters + + """ + if w != 1.0: + self._Histo2D().scaleW(w) + + def normalize(self, double normto, bint includeoverflows=True): + self._Histo2D().normalize(normto, includeoverflows) + + #def mergeBins(self, size_t a, size_t b): + # self._Histo2D().mergeBins(a, b) + + #def rebin(self, int n): + # self._Histo2D().rebin(n) + + #def addBin(self, double low, double high): + # """Add a bin to the Histo2D""" + # self._Histo2D().addBin(low, high) + # return self + + #def addBins(self, edges): + # cdef vector[double] cedges + # for i in edges: + # cedges.push_back(i) + # self._Histo2D().addBins(cedges) + # return self + Added: trunk/pyext/yoda/include/HistoBin1D.pyx ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ trunk/pyext/yoda/include/HistoBin1D.pyx Fri Aug 3 03:14:44 2012 (r514) @@ -0,0 +1,42 @@ +cdef class HistoBin1D(Bin1D_Dbn1D): + + cdef inline c.HistoBin1D *_HistoBin1D(self) except NULL: + return <c.HistoBin1D *> self.ptr() + + def __init__(self, double a, double b): + util.set_owned_ptr( + self, new c.HistoBin1D(a, b)) + + def fill(self, double value, double weight=1.0): + self._HistoBin1D().fill(value, weight) + + @property + def area(self): + return self._HistoBin1D().area() + + @property + def height(self): + return self._HistoBin1D().height() + + @property + def areaErr(self): + return self._HistoBin1D().areaErr() + + @property + def heightErr(self): + return self._HistoBin1D().heightErr() + + def __add__(HistoBin1D a, HistoBin1D b): + return util.new_owned_cls( + HistoBin1D, + new c.HistoBin1D( + deref(a._HistoBin1D()) + + deref(b._HistoBin1D()))) + + def __sub__(HistoBin1D a, HistoBin1D b): + return util.new_owned_cls( + HistoBin1D, + new c.HistoBin1D( + deref(a._HistoBin1D()) - + deref(b._HistoBin1D()))) + Added: trunk/pyext/yoda/include/HistoBin2D.pyx ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ trunk/pyext/yoda/include/HistoBin2D.pyx Fri Aug 3 03:14:44 2012 (r514) @@ -0,0 +1,44 @@ +cdef class HistoBin2D(Bin2D_Dbn2D): + + cdef inline c.HistoBin2D *_HistoBin2D(self) except NULL: + return <c.HistoBin2D *> self.ptr() + + def __init__(self, xlow, xhigh, ylow, yhigh): + util.set_owned_ptr( + self, new c.HistoBin2D(xlow, xhigh, ylow, yhigh)) + + def fill(self, x, y, weight=1.0): + self._HistoBin2D().fill(x, y, weight) + + @property + def volume(self): + return self._HistoBin2D().volume() + + @property + def height(self): + return self._HistoBin2D().height() + + @property + def volume_err(self): + return self._HistoBin2D().volumeErr() + + @property + def height_err(self): + return self._HistoBin2D().heightErr() + + def __add__(HistoBin2D a, HistoBin2D b): + return util.new_owned_cls( + HistoBin2D, + new c.HistoBin2D( + deref(a._HistoBin2D()) + + deref(b._HistoBin2D()))) + + def __sub__(HistoBin2D a, HistoBin2D b): + return util.new_owned_cls( + HistoBin2D, + new c.HistoBin2D( + deref(a._HistoBin2D()) - + deref(b._HistoBin2D()))) + + def __repr__(self): + return 'HistoBin2D(%g, %g, %g, %g)' % (self.edges.x + self.edges.y) Added: trunk/pyext/yoda/include/IO.pyx ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ trunk/pyext/yoda/include/IO.pyx Fri Aug 3 03:14:44 2012 (r514) @@ -0,0 +1,95 @@ +# Readers and writers + +cdef list aobjects_to_list(vector[c.AnalysisObject*] *aobjects): + cdef list out = [] + cdef c.AnalysisObject *ao + cdef size_t i + for i in range(aobjects.size()): + ao = deref(aobjects)[i] + out.append( + util.new_owned_cls( + globals()[ao.type().c_str()], ao)) + return out + +# Set a istringstream's string from a C/Python string +cdef void make_iss(c.istringstream &iss, char *s): + iss.str(string(s)) + +## +## Readers +## + +def readYODA(file_or_filename): + cdef c.istringstream iss + cdef vector[c.AnalysisObject*] aobjects + + if hasattr(file_or_filename, 'read'): + s = file_or_filename.read() + else: + with open(file_or_filename) as f: + s = f.read() + + make_iss(iss, s) + + c.ReaderYODA_create().read(iss, aobjects) + + # Not as expensive as it looks! + return aobjects_to_list(&aobjects) + +def readAIDA(file_or_filename): + cdef c.istringstream iss + cdef vector[c.AnalysisObject*] aobjects + + if hasattr(file_or_filename, 'read'): + s = file_or_filename.read() + else: + with open(file_or_filename) as f: + s = f.read() + + make_iss(iss, s) + + c.ReaderAIDA_create().read(iss, aobjects) + + # Not as expensive as it looks! + return aobjects_to_list(&aobjects) + +## +## Writers +## + +def writeYODA(ana_objs, file_or_filename): + cdef c.ostringstream oss + cdef vector[c.AnalysisObject*] vec + cdef AnalysisObject a + + for a in ana_objs: + vec.push_back(a._AnalysisObject()) + + # Most of the time we just won't care about memory + # Perhaps speak with andy re: huge files + c.WriterYODA_create().write(oss, vec) + + if hasattr(file_or_filename, 'write'): + file_or_filename.write(oss.str().c_str()) + else: + with open(file_or_filename, 'w') as f: + f.write(oss.str().c_str()) + + +def writeAIDA(ana_objs, file_or_filename): + cdef c.ostringstream oss + cdef vector[c.AnalysisObject*] vec + cdef AnalysisObject a + + for a in ana_objs: + vec.push_back(a._AnalysisObject()) + + # Most of the time we just won't care about memory + # Perhaps speak with andy re: huge files + c.WriterAIDA_create().write(oss, vec) + + if hasattr(file_or_filename, 'write'): + file_or_filename.write(oss.str().c_str()) + else: + with open(file_or_filename, 'w') as f: + f.write(oss.str().c_str()) Added: trunk/pyext/yoda/include/Point2D.pyx ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ trunk/pyext/yoda/include/Point2D.pyx Fri Aug 3 03:14:44 2012 (r514) @@ -0,0 +1,128 @@ +from collections import namedtuple +from libcpp cimport bool + +cdef inline pair[double, double] read_symmetric(object val) except *: + try: + a, b = val + except TypeError: + a = b = val + + return pair[double, double](a, b) + +cdef inline object read_error_pair(pair[double, double] es): + return util.Errors(es.first, es.second) + +cdef class Point2D(util.Base): + """ + A 2D point with errors, used by the Scatter2D class. + + """ + + def __init__(self, x=0, y=0, xerrs=0, yerrs=0): + util.set_owned_ptr(self, new c.Point2D()) + self.coords = x, y + self.xErrs = xerrs + self.yErrs = yerrs + + def copy(self): + return util.new_owned_cls(Point2D, + new c.Point2D(deref(self._Point2D()))) + + property x: + """x coordinate""" + def __get__(self): + return self._Point2D().x() + + def __set__(self, x): + self._Point2D().setX(x) + + property y: + """y coordinate""" + def __get__(self): + return self._Point2D().y() + + def __set__(self, y): + self._Point2D().setY(y) + + property coords: + """x and y coordinates as a tuple""" + def __get__(self): + return util.XY(self.x, self.y) + + def __set__(self, val): + self.x, self.y = val + + property xRange: + """The minimum and maximum points within the error""" + def __get__(self): + return util.Edges(self._Point2D().xMin(), + self._Point2D().xMax()) + + property xErrs: + """The x errors""" + def __get__(self): + return read_error_pair(self._Point2D().xErrs()) + + def __set__(self, val): + self._Point2D().setXErr(read_symmetric(val)) + + property yErrs: + """The y errors""" + def __get__(self): + return read_error_pair(self._Point2D().yErrs()) + + def __set__(self, val): + self._Point2D().setYErr(read_symmetric(val)) + + property errs: + """The x and y errors as a tuple""" + def __get__(self): + return util.XY(self.xErrs, self.yErrs) + + def __set__(self, val): + self.xErrs, self.yErrs = val + + + property xErrAvg: + def __get__(self): + return self.xErrAvg() + + property yErrAvg: + def __get__(self): + return self.yErrAvg() + + def scale(self, x=1.0, y=1.0): + """ + (x=1.0, y=1.0) -> None + + Scale the Dbn's variables by the given factors + + """ + self._Point2D().scale(x, y) + + def __repr__(self): + return '<Point2D(%g, %g)>' % self.coords + + def __richcmp__(Point2D self, Point2D other, int op): + if op == 0: + return deref(self._Point2D()) < deref(other._Point2D()) + elif op == 1: + return deref(self._Point2D()) <= deref(other._Point2D()) + elif op == 2: + return deref(self._Point2D()) == deref(other._Point2D()) + elif op == 3: + return deref(self._Point2D()) != deref(other._Point2D()) + elif op == 4: + return deref(self._Point2D()) > deref(other._Point2D()) + elif op == 5: + return deref(self._Point2D()) >= deref(other._Point2D()) + + # Magic stuff + cdef c.Point2D *_Point2D(self) except NULL: + return <c.Point2D *> self.ptr() + + def __dealloc__(self): + cdef c.Point2D *p = self._Point2D() + if self._deallocate: + del p + Added: trunk/pyext/yoda/include/Point3D.pyx ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ trunk/pyext/yoda/include/Point3D.pyx Fri Aug 3 03:14:44 2012 (r514) @@ -0,0 +1,122 @@ +from collections import namedtuple +from libcpp cimport bool + +cdef class Point3D(util.Base): + """ + A 1D distribution 'counter', used and exposed by 1D histograms and their bins. + + """ + + def __init__(self, x=0, y=0, xerrs=0, yerrs=0): + util.set_owned_ptr(self, new c.Point3D()) + self.xy = x, y + self.xerrs = xerrs + self.yerrs = yerrs + + def copy(self): + return util.new_owned_cls(Point3D, + new c.Point3D(deref(self._Point3D()))) + + property x: + """x coordinate""" + def __get__(self): + return self._Point3D().x() + + def __set__(self, x): + self._Point3D().setX(x) + + property y: + """y coordinate""" + def __get__(self): + return self._Point3D().y() + + def __set__(self, y): + self._Point3D().setY(y) + + property z: + """y coordinate""" + def __get__(self): + return self._Point3D().z() + + def __set__(self, z): + self._Point3D().setZ(z) + + property xyz: + def __get__(self): + return util.XYZ(self.x, self.y, self.z) + + def __set__(self, val): + self.x, self.y, self.z = val + + property xerrs: + def __get__(self): + return read_error_pair(self._Point3D().xErrs()) + + def __set__(self, val): + self._Point3D().setXErr(read_symmetric(val)) + + property yerrs: + def __get__(self): + return read_error_pair(self._Point3D().yErrs()) + + def __set__(self, val): + self._Point3D().setYErr(read_symmetric(val)) + + property zerrs: + def __get__(self): + return read_error_pair(self._Point3D().zErrs()) + + def __set__(self, val): + self._Point3D().setZErr(read_symmetric(val)) + + property xerr_avg: + def __get__(self): + return self.xErrAvg() + + property yerr_avg: + def __get__(self): + return self.yErrAvg() + + property zerr_avg: + def __get__(self): + return self.zErrAvg() + + def scale(self, x=1.0, y=1.0, z=1.0): + """ + (x=1.0, y=1.0, z=1.0) -> None + + Scale the Dbn's variables by the given factors + + """ + self._Point3D().scale(x, y, z) + + def __repr__(self): + return '<Point3D(x=%g, y=%g)>' % ( + self.x, self.y, self.xerrs, self.yerrs) + + def __str__(self): + return 'Point3D(%g, %g)' % self.xy + + def __richcmp__(Point3D self, Point3D other, int op): + if op == 0: + return deref(self._Point3D()) < deref(other._Point3D()) + elif op == 1: + return deref(self._Point3D()) <= deref(other._Point3D()) + elif op == 2: + return deref(self._Point3D()) == deref(other._Point3D()) + elif op == 3: + return deref(self._Point3D()) != deref(other._Point3D()) + elif op == 4: + return deref(self._Point3D()) > deref(other._Point3D()) + elif op == 5: + return deref(self._Point3D()) >= deref(other._Point3D()) + + # Magic stuff + cdef c.Point3D *_Point3D(self) except NULL: + return <c.Point3D *> self.ptr() + + def __dealloc__(self): + cdef c.Point3D *p = self._Point3D() + if self._deallocate: + del p + Added: trunk/pyext/yoda/include/Profile1D.pyx ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ trunk/pyext/yoda/include/Profile1D.pyx Fri Aug 3 03:14:44 2012 (r514) @@ -0,0 +1,113 @@ +#TODO: docstrings!!! + +cdef class Profile1D(AnalysisObject): + cdef inline c.Profile1D *_Profile1D(self) except NULL: + return <c.Profile1D*> self.ptr() + + def __init__(self, *args, **kwargs): + #TODO: convert to the new-style Profile1D + if len(args) <= 2: + self.__init2(*args) + elif len(args) >= 3: + self.__init5(*args) + + def __init2(self, char *path="", char *title=""): + util.set_owned_ptr( + self, new c.Profile1D(string(path), string(title))) + + def __init5(self, size_t nbins, double lower, double upper, + char *path="", char *title=""): + util.set_owned_ptr( + self, new c.Profile1D( + nbins, lower, upper, string(path), string(title))) + + def __len__(self): + return self._Profile1D().bins().size() + + def __getitem__(self, py_ix): + cdef size_t i = util.pythonic_index(py_ix, self._Profile1D().bins().size()) + return util.new_borrowed_cls( + ProfileBin1D, & self._Profile1D().bins().at(i), self) + + def __repr__(self): + return "<Profile1D at %x>" % id(self) + + def fill(self, double x, double y, double weight=1.0): + self._Profile1D().fill(x, y, weight) + + def fillMany(self, xs, ys, ws=None): + cdef double x, y, w + cdef c.Profile1D *p = self._Profile1D() + + itx = iter(xs) + ity = iter(ys) + if ws: + itw = iter(ws) + while True: + try: + x = next(itx, None) + y = next(ity, None) + w = next(itw, None) + except TypeError: + break + else: + p.fill(x, y, w) + else: + while True: + try: + x = next(itx, None) + y = next(ity, None) + except TypeError: + break + else: + p.fill(x, y, 1.0) + + def copy(self, char *path=""): + return util.new_owned_cls(Profile1D, + new c.Profile1D(deref(self._Profile1D()), string(path))) + + @property + def totalDbn(self): + return util.new_borrowed_cls( + Dbn2D, &self._Profile1D().totalDbn(), self) + + @property + def underflow(self): + return util.new_borrowed_cls( + Dbn2D, &self._Profile1D().underflow(), self) + + @property + def overflow(self): + return util.new_borrowed_cls( + Dbn2D, &self._Profile1D().overflow(), self) + + def reset(self): + self._Profile1D().reset() + + def scale(self, double w=1.0): + """ + (w=1.0) -> None + + """ + if w != 1.0: + self._Profile1D().scaleW(w) + + def mergeBins(self, size_t a, size_t b): + self._Profile1D().mergeBins(a, b) + + def rebin(self, int n): + self._Profile1D().rebin(n) + + def addBin(self, double low, double high): + """Add a bin to the Profile1D""" + self._Profile1D().addBin(low, high) + return self + + # TODO: look at Histo1D + def addBins(self, edges): + cdef vector[double] cedges + for i in edges: + cedges.push_back(i) + self._Profile1D().addBins(cedges) + return self + Added: trunk/pyext/yoda/include/Profile2D.pyx ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ trunk/pyext/yoda/include/Profile2D.pyx Fri Aug 3 03:14:44 2012 (r514) @@ -0,0 +1,148 @@ +cdef class Profile2D(AnalysisObject): + cdef inline c.Profile2D *_Profile2D(self) except NULL: + return <c.Profile2D*> self.ptr() + + def __init__(self, *args, **kwargs): + if len(args) <= 2: + self.__init2(*args, **kwargs) + else: + self.__init7(*args, **kwargs) + + def __init2(Profile2D self, char *path="", char *title=""): + util.set_owned_ptr( + self, new c.Profile2D(string(path), string(title))) + + def __init7(Profile2D self, nxbins, xlow, xupp, nybins, ylow, yupp, + char *path="", char *title=""): + + util.set_owned_ptr( + self, new c.Profile2D( + nxbins, xlow, xupp, + nybins, ylow, yupp, + string(path), string(title))) + + def __len__(self): + return self._Profile2D().bins().size() + + def __getitem__(self, py_ix): + cdef size_t i = util.pythonic_index(py_ix, self._Profile2D().bins().size()) + return util.new_borrowed_cls( + ProfileBin2D, & self._Profile2D().bins().at(i), self) + + def __repr__(self): + return "<Profile2D at %x>" % id(self) + + def fill(self, double x, double y, double z, double weight=1.0): + self._Profile2D().fill(x, y, z, weight) + + def bin_by_coord(self, x, y): + cdef int index = self._Profile2D().findBinIndex(x, y) + print index + return self[index] + + def fill_many(self, xs, ys, zs, ws=None): + cdef double x, y, z, w + cdef c.Profile2D *p = self._Profile2D() + + itx = iter(xs) + ity = iter(ys) + itz = iter(zs) + if ws: + itw = iter(ws) + while True: + try: + x = next(itx, None) + y = next(ity, None) + z = next(itz, None) + w = next(itw, None) + except TypeError: + break + else: + p.fill(x, y, z, w) + else: + while True: + try: + x = next(itx, None) + y = next(ity, None) + z = next(itz, None) + except TypeError: + break + else: + p.fill(x, y, z, 1.0) + + def copy(self, char *path=""): + return util.new_owned_cls(Profile2D, + new c.Profile2D(deref(self._Profile2D()), string(path))) + + @property + def total_dbn(self): + return util.new_borrowed_cls( + Dbn3D, new c.Dbn3D(self._Profile2D().totalDbn()), self) + + @property + def outflow(self, ix, iy): + return util.new_borrowed_cls( + Dbn3D, new c.Dbn3D(self._Profile2D().outflow(ix, iy)), self) + + + #def integral(self, overflows=True): + # return self._Profile2D().integral(overflows) + + def sum_w(self, overflows=True): + return self._Profile2D().sumW(overflows) + + def sum_w2(self, overflows=True): + return self._Profile2D().sumW2(overflows) + + #def mean(self, overflows=True): + # return XY( + # self._Profile2D().xMean(overflows), + # self._Profile2D().yMean(overflows)) + + #def variance(self, overflows=True): + # return XY( + # self._Profile2D().xVariance(overflows), + # self._Profile2D().yVariance(overflows)) + + #def std_dev(self, overflows=True): + # return XY( + # self._Profile2D().xStdDev(overflows), + # self._Profile2D().yStdDev(overflows)) + + #def std_err(self, overflows=True): + # return XY( + # self._Profile2D().xStdErr(overflows), + # self._Profile2D().yStdErr(overflows)) + + def reset(self): + self._Profile2D().reset() + + #def scale(self, double w=1.0): + # """ + # (w=1.0) -> None + + # """ + # if w != 1.0: + # self._Profile2D().scaleW(w) + + #def merge_bins(self, size_t a, size_t b): + # self._Profile2D().mergeBins(a, b) + + # NOTE: Cython 0.17 will be bringing automatic conversion between python and + # C++ -- will be very nice here. + + #def rebin(self, int n): + # self._Profile2D().rebin(n) + + #def add_bin(self, double low, double high): + # """Add a bin to the Profile2D""" + # self._Profile2D().addBin(low, high) + # return self + + #def add_bins(self, edges): + # cdef vector[double] cedges + # for i in edges: + # cedges.push_back(i) + # self._Profile2D().addBins(cedges) + # return self + Added: trunk/pyext/yoda/include/ProfileBin1D.pyx ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ trunk/pyext/yoda/include/ProfileBin1D.pyx Fri Aug 3 03:14:44 2012 (r514) @@ -0,0 +1,56 @@ +cdef class ProfileBin1D(Bin1D_Dbn2D): + + cdef inline c.ProfileBin1D *_ProfileBin1D(self) except NULL: + return <c.ProfileBin1D *> self.ptr() + + def __init__(self, double a, double b): + util.set_owned_ptr( + self, new c.ProfileBin1D(a, b)) + + def fill(self, double x, double y, double weight=1.0): + self._ProfileBin1D().fill(x, y, weight) + + def fill_bin(self, double y, double weight=1.0): + self._ProfileBin1D().fillBin(y, weight) + + @property + def mean(self): + return self._ProfileBin1D().mean() + + @property + def stdDev(self): + return self._ProfileBin1D().stdDev() + + @property + def variance(self): + return self._ProfileBin1D().variance() + + @property + def stdErr(self): + return self._ProfileBin1D().stdErr() + + @property + def rms(self): + return self._ProfileBin1D().rms() + + @property + def sumWY(self): + return self._ProfileBin1D().sumWY() + + @property + def sumWY2(self): + return self._ProfileBin1D().sumWY2() + + def __add__(ProfileBin1D a, ProfileBin1D b): + return util.new_owned_cls( + ProfileBin1D, + new c.ProfileBin1D( + deref(a._ProfileBin1D()) + + deref(b._ProfileBin1D()))) + + def __sub__(ProfileBin1D a, ProfileBin1D b): + return util.new_owned_cls( + ProfileBin1D, + new c.ProfileBin1D( + deref(a._ProfileBin1D()) - + deref(b._ProfileBin1D()))) Added: trunk/pyext/yoda/include/ProfileBin2D.pyx ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ trunk/pyext/yoda/include/ProfileBin2D.pyx Fri Aug 3 03:14:44 2012 (r514) @@ -0,0 +1,59 @@ +cdef class ProfileBin2D(Bin2D_Dbn3D): + + cdef inline c.ProfileBin2D *_ProfileBin2D(self) except NULL: + return <c.ProfileBin2D *> self.ptr() + + def __init__(self, xlow, xhigh, ylow, yhigh): + util.set_owned_ptr( + self, new c.ProfileBin2D(xlow, xhigh, ylow, yhigh)) + + def fill(self, x, y, z, weight=1.0): + self._ProfileBin2D().fill(x, y, z, weight) + + def fill_bin(self, z, weight=1.0): + self._ProfileBin2D().fillBin(z, weight) + + @property + def mean(self): + return self._ProfileBin2D().mean() + + @property + def std_dev(self): + return self._ProfileBin2D().stdDev() + + @property + def variance(self): + return self._ProfileBin2D().variance() + + @property + def std_err(self): + return self._ProfileBin2D().stdErr() + + @property + def rms(self): + return self._ProfileBin2D().rms() + + @property + def sum_wz(self): + return self._ProfileBin2D().sumWZ() + + @property + def sum_wz2(self): + return self._ProfileBin2D().sumWZ2() + + def __add__(ProfileBin2D a, ProfileBin2D b): + return util.new_owned_cls( + ProfileBin2D, + new c.ProfileBin2D( + deref(a._ProfileBin2D()) + + deref(b._ProfileBin2D()))) + + def __sub__(ProfileBin2D a, ProfileBin2D b): + return util.new_owned_cls( + ProfileBin2D, + new c.ProfileBin2D( + deref(a._ProfileBin2D()) - + deref(b._ProfileBin2D()))) + + def __repr__(self): + return 'ProfileBin2D(%g, %g, %g, %g)' % (self.edges.x + self.edges.y) Added: trunk/pyext/yoda/include/Scatter2D.pyx ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ trunk/pyext/yoda/include/Scatter2D.pyx Fri Aug 3 03:14:44 2012 (r514) @@ -0,0 +1,95 @@ + +cdef class Scatter2D(AnalysisObject): + """ + 1D histogram. Complete histogramming is supported, including + uniform/regular binning, variable wideth bininng, unbinned gaps in + the covered range, and under/overflows. Rebinning by integer + factors, or by explicit merging of contiguous bins is also + supported. + + Rescaling of weights and/or the x axis is permitted in-place: the + result is still a valid Scatter2D. Binning-compatible 1D histograms + may be divided, resulting in a Scatter2D since further fulls would + not be meaningful. + + Several sets of arguments are tried by the constructor in the + following order. + + Scatter2D(path="", title=""). Construct a histogram with optional path + and title but no bins. + + Scatter2D(B, path="", title=""). Construct a histogram from an + iterator of bins, B. + + Scatter2D(E, path="", title=""). Construct a histogram from an + iterator of edges, E. + + Should this constructor fail, then + Scatter2D(nbins, low, high, path="", title="") + """ + + # Is there a reason why this sounds like an advert? I'M ALREADY SOLD + # DAMMIT! + + + cdef inline c.Scatter2D *_Scatter2D(self) except NULL: + return <c.Scatter2D*> self.ptr() + + # There is a pythonic constructor here, and it looks a little like... + # __init__(self, *args, **kwargs) + # ([edge], path="", title="") + # ([bins], **kwargs) + + def __init__(self, *args, **kwargs): + #try_loop([self.__init2, self.__init5, self.__init3], *args, **kwargs) + util.try_loop([self.__init_2, self.__init_3], *args, **kwargs) + + def __init_2(self, char* path="", char* title=""): + util.set_owned_ptr(self, new c.Scatter2D(string(path), string(title))) + + def __init_3(self, points, char* path="", char* title=""): + self.__init_2(path, title) + self.addPoints(points) + + def __len__(self): + return self._Scatter2D().numPoints() + + def __getitem__(self, py_ix): + cdef size_t i = util.pythonic_index( + py_ix, self._Scatter2D().numPoints()) + + return util.new_borrowed_cls( + Point2D, & self._Scatter2D().point(i), self) + + def copy(self, char *path=""): + """(path="") -> Scatter2D. Clone this Scatter2D with optional new path.""" + return util.new_owned_cls(Scatter2D, + new c.Scatter2D(deref(self._Scatter2D()), string(path))) + + def addPoints(self, iterable): + for row in iterable: + self.addPoint(*row) + + def addPoint(self, *args, **kwargs): + try: + self.__addPoint_point(*args, **kwargs) + except TypeError: + self.__addPoint_explicit(*args, **kwargs) + + def __addPoint_explicit(self, x, y, xerrs=0, yerrs=0): + self.__addPoint_point(Point2D(x, y, xerrs, yerrs)) + + def __addPoint_point(self, Point2D p): + self._Scatter2D().addPoint(p._Point2D()[0]) + + def combineWith(self, others): + cdef Scatter2D other + try: + # Can we type it as a scatter2D? + other = others + except TypeError: + # Could be an iterable... + for other in others: + self._Scatter2D().combineWith(deref(other._Scatter2D())) + else: + self._Scatter2D().combineWith(deref(other._Scatter2D())) Added: trunk/pyext/yoda/util.pxd ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ trunk/pyext/yoda/util.pxd Fri Aug 3 03:14:44 2012 (r514) @@ -0,0 +1,56 @@ +# A base CppObject which prevents null pointer access +cdef class Base: + """A base class which wraps a pointer""" + cdef void *_ptr + cdef bint _deallocate + cdef object _owner + + cdef inline void *ptr(self) except NULL: + if self._ptr == NULL: + raise MemoryError('Null pointer referenced: ' + 'perhaps the class is uninitialised.') + + return self._ptr + +# Magic for setting pointers + +# Use this for setting a pointer that is owned by the object obj +# e.g. if you want deallocation to happen automatically when this object goes +# out of scope +cdef inline set_owned_ptr(Base obj, void *ptr): + obj._ptr = ptr + obj._deallocate = True + +# Use this for setting a pointer that is *not* owned by the object obj e.g. if +# you were given this object by a class which will deallocate it once it's +# finished +cdef inline set_borrowed_ptr(Base obj, void *ptr, object owner): + obj._ptr = ptr + obj._deallocate = False + obj._owner = owner + +# Use this to create a new object of type cls from the pointer ptr. The class is +# one which owns its pointer, and will deallocate when it's done. It's this one +# that you want to use for e.g. loaders or factory methods where the user is +# expected to manage memory. Or where you've explicitly called new. +cdef inline object new_owned_cls(object cls, void *ptr): + obj = cls.__new__(cls) + set_owned_ptr(obj, ptr) + return obj + +# Use this to create a thin wrapper around a pointer that will *not* be +# deallocated when it goes out of scope. Useful when you're given a reference +# from a class which is not expecting you to delete its innards! +cdef inline object new_borrowed_cls(object cls, void *ptr, object owner): + obj = cls.__new__(cls) + set_borrowed_ptr(obj, ptr, owner) + return obj + +cdef inline size_t pythonic_index(int i, size_t size) except? 0: + if i < 0: + i += size + + if 0 <= i < size: + return i + else: + raise IndexError Added: trunk/pyext/yoda/util.pyx ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ trunk/pyext/yoda/util.pyx Fri Aug 3 03:14:44 2012 (r514) @@ -0,0 +1,19 @@ +from collections import namedtuple + +cdef class Base: + pass + +def try_loop(fs, *args, char *_msg='Invalid arguments', **kwargs): + for f in fs: + try: + f(*args, **kwargs) + return + except TypeError: + pass + + raise TypeError(_msg) + +Edges = namedtuple('Edges', ('low', 'high')) +Errors = namedtuple('Errors', ('minus', 'plus')) +XY = namedtuple('XY', ('x', 'y')) +XYZ = namedtuple('XYZ', ('x', 'y', 'z'))
More information about the yoda-svn mailing list |