PyPedal 2 CHANGELOG
===================

"---" indicates a known bug;
"+++" indicates a feature addition;
"***" indicates an API change or a major bugfix;
"'''" indicates a minor bugfix or feature enhancement.
"???" indicates a possible problem (i.e. bug) that has
      not been verified.
"XXX" indicates a feature that has been deprecated or removed.
"!!!" indicates a feature that is planned or stubbed, but is not yet working.

CHANGES in PyPedal 2.0.0a19
===========================
+++ 10/25/2005  Added new functions to pyp_network for identifying ancestors,
            identifying descendants, identifying immediate family members
            (defined as parents and offspring, and does not include siblings), and
            identifying influential progeny based on the number of progeny they
            produce.
*** 10/20/2005  Fixed to pyp_network/ped_to_graph() so that graphs are now ordered
            in the correct direction.  Before this fix, the graph was ordered back-
            wards, so that offspring preceded parents in the graph.
!!! 10/20/2005  It looks like most of the stuff on pyp_demog can be moved into
            pyp_reporting.  I'm going to think about this.  The demographics reports
            may be moved.  The code would be a lot cleaner if those reports were queries
            against the database rather than formed from walking the pedigree.
''' 10/20/2005  Added a new option, oid, to pyp_network/ped_to_graph().
''' 09/23/2005  There is now a simple document history in the GUI.  The "View log"
            feature in pyp_gui has been rewritten so that it displays the logfile
            associated with the loaded pedigree rather than presenting the user with
            a file selection dialogue.
*** 09/23/2005  I decided to back out of using Wax and just stick with plain old
            wxPython for the GUI.  After messing with it for a couple of days I
            have decided that (i) Wax has a lot of potential and (ii) it's just not
            quite there yet in terms of functionality and, more importantly,
            documentation.  If I am going to spend hours in the wxPython docs anyway
            I'll just write with that tool.  Despite the much-despised IDs that I have
            to assign, pass, and debug.
+++ 09/21/2005  I have added a new PyPedal file, pyp_gui_metrics, which contains
            convenience functions for entries in the Metrics menu to reduce repetitive
            code in pyp_gui.
*** 09/21/2005  pyp_metrics/a_effective_founders_lacy() and pyp_metrics/
            effective_founders_lacy() now return a dictionary that contains summary
            statistics, including the effective founder number.  This change breaks
            at least one example program and may break at least one unit test.
''' 09/21/2005  Added a new option, log_long_filenames, that indicates whether or not
            logfile names should include datestamps.  The default is to not use them.
+++ 09/20/2005  Added pyp_io/summary_inbreeding() which returns a string
            representation of the data contained in the 'metadata' dictionary
            contained in the output dictionary returned by pyp_nrm/pyp_inbreeding().
+++ 09/20/2005  I've been messing with Wax and pyp_gui today.  I have added a new
            PyPedal file, pyp_gui_graphs, to package the classes subclassed from
            Dialog, such as PyPedalGraphDialogInbreeding().  Oh, sure, I tried to
            have a single PyPedalGraphDialog() class that I could use for any graph
            by passing titles and filenames, but I could not get it to work correctly.
            If anyone wants to try and fix the dreaded-and-deadly
            "TypeError: __init__() got multiple values for keyword argument 'pgdTitle'"
            problem they are welcome to it.  For now it is easiest to just have a
            subclass for each graph that I am going to draw.
''' 09/20/2005  Cleanups in pyp_db so that messages are only printed to STDOUT when
            the 'messages' option is set to 'verbose'.
*** 09/20/2005  pyp_nrm/inbreeding() now returns a dictionary that contains two
            dictionaries: 'metadata', which contains summary statistics for the CoI
            in the pedigree, and 'fx', which contains the actual CoI for each animal.
            This change breaks at least one example program and may break at least one
            unit test.
+++ 09/19/2005  Added a new function, plot_line_xy(), to pyp_graphics.  plot_line_xy
            produces a plot of the values in an input dictionary by levels of the
            keys in the dictionary.  It can take the dictionary returned by
            pyp_reports/meanMetricBy() and produce a simple chart from it.
''' 09/19/2005  Cleaned up logging in pyp_db/loadPedigreeDatabase().
''' 09/19/2005  Automatically-generated logfile names now include datestamps.
+++ 09/19/2005  Added a new function, pyp_datestamp(), to pyp_utils which returns a
            datestamp of the form YYYYMMDDHHMMSS.
''' 09/19/2005  Added a new option, log_ped_lines, that indicates how many lines of
            the pedigree file should be written to the logfile for debugging.  The
            default is zero.  Any value other than a non-negative integer is set to
            0 and a warning is written to the log.
''' 09/16/2005  pyp_newclasses/NewPedigree.save() has been modified so that it
            will save CoI whenever they have been computed for a pedigree.
''' 09/16/2005  pyp_nrm/inbreeding_vanraden() now writes some summary statistics
            to the logfile, and the screen when requested, whenever a round of
            processing included at least 1% of the animals in the pedigree.
''' 09/16/2005  Modified pyp_nrm/inbreeding_vanraden() to stop overwriting known
            CoI in the fx{} dictionary unnecessarily.  This may not buy much in
            terms of efficiency, but why make lots of writes when you don't need to?
''' 09/16/2005  pyp_nrm/inbreeding() now calls pyp_nrm/inbreeding_vanraden() for
            pedigrees of 1,000 animals or more rather than 10,000.
''' 09/16/2005  Modified pyp_nrm/inbreeding() to set the f_computed flag before
            returning when it has been successful.
''' 09/16/2005  Added a new option, f_computed, that indicates whether or not CoI
            have been computed for animals in the current pedigree.  If the pedigree
            format string includes 'f' this will be set to 1; it is also set to 1 on
            a successful return from pyp_nrm/inbreeding().
''' 09/14/2005  Added some code in pyp_io/unpickle_pedigree() to prevent a
            the addition of a .pkl extension to filenames that already have
            extensions.  This solves the dreaded-and-deadly Double Pickle
            Problem.
??? 09/14/2005  pyp_io/pickle_pedigree() and pyp_io/unpicle_pedigree() seem to
            work on Python 2.4.1 compiled from source for 64-bit processors.
            More investigation is needed to see why this works on the 366 but
            not the 440.
''' 09/14/2005  Fixed a typo in an option name in pyp_newclasses/NewAMatrix().
+++ 08/23/2005  Added a new module, pyp_network, for experimenting with the
            NetworkX graph library for Python (https://networkx.lanl.gov/).  This
            provides a way to represent pedigrees (for example) as algebraic graphs
            and may provide a nice way to get around some of the problems I am having
            trying to code routines such as pyp_metrics/num_traced_gens().
''' 08/23/2005  pyp_utils/assign_offspring() now checks the pedigree format string
            to see if animal sexes were provided.  If they were, offspring are assigned
            to their parents' correct sons or daghters list rather than to the unknowns
            list.
+++ 08/22/2005  pyp_nrm/fast_a_matrix() and pyp_nrm/fast_a_matrix_r() now take an
            optional argument, 'method', that indicates whether a dense ('dense') or
            sparse ('sparse') matrix should be used for storing the NRM.  The sparse
            matrix support is provided by PySparse (http://pysparse.sourceforge.net/),
            and there are source code and binary (Python 2.4 for Windows 32) versions
            available for download.  This should allow PyPedal to manipulate larger NRM
            than is possible with the dense matrices provided by Numarray.
''' 08/22/2005  Several routines that did not return any values before now return
            some result (dictionaries of summary statistics, 0/1 on failure/success,
            etc.).  In addition, many routines were not guaranteed to return the
            value(s) specified in the docstrings.  This has been fixed with the judicious
            use of try/except blocks and default values, such as initially empty lists and
            dictionaries.  The end result should be more stability from the user's
            perspective due to fewer instances of behavior contrary to the documentation.
''' 08/22/2005  Almost all routines now write messages to the logfile when they are
            entered and exited.  Exceptions are made for, e.g., pad_id() which would
            result in an entry being made for each animal in the pedigree.  try/except
            blocks are used to make sure that things don't fail if no logfile has been
            created, for example if someone is using an odd PyPedal routine or two out-
            of-context as it were.
''' 08/22/2005  Cleaned-up the output-to-file code.  There were lots of places where
            output was created as a string in one line and written to an output file
            on a second line.  This needless separation of powers was eliminated.
''' 08/22/2005  Lots of work has been done on the documentation.  LaTeX is the One
            True Way.  Repent, non-believers, or Many Bad Things will befall you in
            the next life!
''' 08/19/2005  Added a new pedigree format code, Z, that can be used to skip columns
            when reading a pedigree file.
''' 08/04/2005  pyp_newclasses/NewPedigree.preprocess() now checks to see if the
            datalines read from the pedigree file contain the same number of columns
            as specified in the pedigree format string.  If there is a mis-match an
            error message is written to the console and PyPedal halts.
''' 08/04/2005  Fixed a possible bug in pyp_nrm/inbreeding_vanraden() caused by
            recent change in the arguments expected by pyp_nrm/fast_a_matrix().
??? 08/04/2005  Added a new function, unpickle_pedigree(), to pyp_io that
            unserializes (unpickles) a pedigree using the cPickle module.  It is
            used to load a pedigree from a pickled file created with the
            pickle_pedigree() function.
--- 08/04/2005  Added a new function, pickle_pedigree(), to pyp_io that
            serializes (pickles) pedigrees using the cPickle module.  Unfortunately,
            the call to the dump function in cPickle is throwing an exception I
            cannot decipher.  I have e-mailed David S. to bug him for ideas.
''' 08/04/2005  More work on pyp_db and pyp_reports.
''' 08/02/2005  Minor bugfixes in pyp_nrm.
''' 08/02/2005  Some minor work on pyp_db and pyp_reports.
+++ 07/22/2005  Added a new function, string_to_table_name, to pyp_utils().  It is
            used to produce strings that are safe for use as SQLite table names.
''' 07/22/2005  Added two new options, database_name and dbtable_name, for support
            of the new SQLite/pyp_reports features.
+++ 07/21/2005  Added new modules pyp_db and pyp_reports.  pyp_db is an optional
            module that can use SQLite (http://www.sqlite.org/) and the Python
            bindings to SQLite, pysqlite (http://initd.org/tracker/pysqlite) to
            store (and retrieve) pedigrees in a simple database.  pyp_reports uses
            pyp_db to prepare summary reports, produce figures, etc.  Neither of
            these modules is much use without SQLite.  The thinking here is that
            there are lots of reports that are pretty trivial to produce with SQL,
            but which require lots of looping over PyPedal pedigrees.  So I decided
            to give this a whirl.  SQLite, "a small C library that implements a
            self-contained, embeddable, zero-configuration SQL database engine" is
            in the public domain.
??? 07/21/2005  It seems like FTP upload to Sourceforge is still FUBARed.  I don't
            know when I will get the 2.0.0a18 tarballs uploaded.

CHANGES in PyPedal 2.0.0a18
===========================
''' 07/19/2005  pyp_metrics/min_max_f() now works, at least sort-of.
+++ 07/19/2005  Added a new function, pyp_utils.sort_dict_by_values(), which
            sorts dictionaries by their values and keys within values.  It
            Returns a list of tuples in sorted order.
''' 07/19/2005  pyp_metrics/a_coefficients() and pyp_metrics/fast_a_coefficients()
            have been updated to check for an attached NRM when processing a pedigree.
            If kw['form_nrm'] is '0' they will form the NRM from scracth; otherwise
            they use the NRM attached to the pedobj.  They return a dictionary of
            individual non-zero COI.
''' 07/19/2005 Rewrote part of pyp_newclasses/NewAMatrix.  There is now a single
            method, NewAMatrix.form_a_matrix(), for creating NRM.  The option
            'nrm_method' is used to determine whether or not to correct for
            parental inbreeding.  The default is no correction.
''' 07/19/2005 pyp_newclasses/NewPedigree.preprocess() checks for conflicts
            between the sepchar and alleles_sepchar options when allelotypes
            are provided in an input file.  In case of a conflict warnings are
            written to the console and the logfile, and the allelotypes are
            ignored.
+++ 07/19/2005 Added a new option, form_nrm, that will result in the formation
            of a NRM as an instance of a NewAMatrix object that is attached to
            your NewPedigree instance.  This is probably best avoided with large
            pedigrees, at least until I have tested it further.
+++ 07/19/2005 Changed from using distutils to using setuputils in an effort
            to make installation simpler.
--- 06/21/2005 pyp_metrics/min_max_f() must never have been tested -- there is no
            way, looking at the code, that it can work as written.
''' 06/21/2005 Cleaned-up several functions in pyp_metrics so that they obey the
            'quiet' form of kw['messages'].
''' 06/21/2005 Many small bug/typo fixes in pyp_metrics thanks to the unit tests.
XXX 06/21/2005 Removed pyp_metrics/a_effective_ancestors() because the two
            subroutines that it called return different values (a single value
            versus a tuple).  There is no particularly good reason to hide them from
            the user, anyway.
+++ 06/20/2005 Added a unit testing framework using TestOOB
            (http://testoob.sourceforge.net/).
''' 06/20/2005 Fixed a bug in pyp_newclasses/NewPedigree.load() in which the
            wrong argument was was passed to pyp_utils/assign_offspring().
''' 06/20/2005 Fixed a bug in pyp_newclasses/NewPedigree.renumber() in which the
            wrong argument was was passed to pyp_utils/assign_offspring().
''' 06/20/2005 Fixed a typo in pyp_nrm/fast_a_matrix() that caused programs to
            crash.
''' 06/20/2005 Changed option, 'is_renumbered', that allows the user to specify
            whether or not the pedigree is already renumbered, to
            'pedigree_is_renumbered'.
''' 06/20/2005 Hackage on pyp_nrm/fast_a_matrix() so that it no longer takes a
            PyPedal pedigree pyp_metrics/effective_founders_lacy use this
            routine to form NRMs from "subpedigrees", which are lists of animals
            rather than instances of PyPedal pedigree objects.

CHANGES in PyPedal 2.0.0a17
===========================
''' 05/19/2005 Updated all routines in pyp_nrm to conform to new object model.
''' 05/19/2005 Updated all routines in pyp_metrics to conform to new object model.
''' 05/19/2005 Updated all routines in pyp_io to conform to new object model.
XXX 05/19/2005 pyp_io/id_map_from_file() has been removed.  Similar functionality id provided by
            pyp_utils/load_id_map().
XXX 05/19/2005 pyp_io/a_matrix_from_text_file() has been removed.  This procedure was stubbed and
            never written.
XXX 05/19/2005 pyp_io/a_matrix_to_file() and pyp_io/a_matrix_from_file() have been removed.  Similar
            functionality is provided by the load() and save() methods of pyp_newclasses/NewAMatrix.
''' 05/19/2005 Updated all routines in pyp_graphics to conform to new object model.  Note that
            the routines that need a NRM are still using "raw" numarray matrices rather than instances
            of the NewAMatrix class.
''' 05/19/2005 Updated all routines in pyp_demog to conform to new object model.
''' 05/19/2005 Added a new option, 'debug_messages', that indicates whether or not PyPedal should
            print debugging information.
''' 05/13/2005 Updated all routines in pyp_utils to conform to new object model.
''' 05/13/2005 pyp_utils/set_ancestor_flag() has been updated to take only a single argument,
            an instance of a NewPedigree object.  Keyword options in the 'NewPedigree.kw' dictionary
            are used to control messages and I/O.  The logging module is used for recording operations.
            The documentation has been updated.
''' 05/13/2005 Added a new option, 'file_io', that tells routines that can write results to
            output files to do and put messages in the program log to that effect.
XXX 05/13/2005 pyp_utils/new_preprocess() has been removed.
XXX 05/13/2005 pyp_utils/preprocess() has been removed.
XXX 05/13/2005 pyp_utils/load() has been removed.
''' 05/13/2005 Updated __version__.py.
''' 05/06/2005 Added support for psyco metaclasses to pyp_newclasses.  If the psyco optimizing
            compiler for Python is installed on your system all of the methods in the classes
            defined in pyp_newclasses will automatically be bound by psyco.  More details may
            be found in the psyco documentation (http://psyco.sourceforge.net/).
+++ 05/06/2005 Added two new routines to pyp_graphics: spy_matrix_pylab() and spy_matrix_pylab().
            They are matplotlib implementations of the rmuller_spy_matrix_pil() and
            rmuller_pcolor_matrix_pil() functions, respectively.  They are not well-tested,
            spy_matrix_pylab() seems to only use greyscale at the moment, and the output
            from pcolor_matrix_pylab() is rotated 90-degrees from what it should be.  Patches are
            welcome.
+++ 05/04/2005 Added support for animal/sire/dam IDs as strings.  There are new pedigree format
            codes (A, S, D) corresponding to these formats.  The 'sepchar' character should NOT
            appear in the ID string; if it does, breakage will occur.
''' 05/04/2005 Added a new method, string_to_int(), to pyp_newclasses/NewAnimal() that converts
            any Python string to a string composed of the ASCII values of each character in the
            original string.  The new string can be cast to an integer.  I prefer this to the
            Python hash() function because the hash() function can return negative values.

CHANGES in PyPedal 2.0.0a16
===========================
''' 05/03/2005 Finally started updating the non-API documentation.
''' 05/03/2005 Added a new option, 'missing_parent', that allows the user to specify the value
            in the pedigree used to indicate missing/unknown parents.  This defaults to '0', and
            whenever a parent ID matching the 'missing_parent' value is encountered '0' is recorded
            in the offspring's record.
''' 05/03/2005 Rewrote the script which autogenerates the documentation so that it can pull the
            HTML API into the LaTeX document used to created the PS and PDF manuals.
+++ 05/03/2005 Added a new class, NewAMatrix, to pyp_newclasses.  This class is a wrapper around
            a Numarray matrix that provides convenience methods for saving and loading numerator
            relationship matrices.

CHANGES in PyPedal 2.0.0a15
===========================
''' 04/28/2005 Some minor cleanup in all files to enforce consistens useage of
            pyp_utils/pyp_nice_time() for date/time reporting.
''' 04/28/2005 Fixed a typo in pyp_newclasses/NewAnimal.__init__() that caused an error when
            trying to read coefficients of inbreeding from a pedigree file.
''' 04/28/2005 Added a new option, 'pedigree_is_renumbered', which indicates whether or not
            a pedigree has been renumbered.  This should NOT be confused with the 'renumber'
            flag, which indcates that a pedigree should be renumbered.  This is an
            informational flag, not a command flag, and is not documented in OPTIONS.
+++ 04/28/2005 Added a new method, renumber(), to pyp_newclasses/NewPedigree.  This is used
            by pyp_newclasses/NewPedigree.load() and pyp_metrics/effective_founders_lacy()
            to renumber pedigrees.  It will eventually be used by any routine that needs
            to renumber the pedigree.
''' 04/28/2005 pyp_metrics/effective_founders_lacy() will renumber a pedigree passed to
            it if the pedigree's 'renumber' flag is set to 0, update the ID maps, and
            assign offspring.  These actions are noted in the logfile and fix the impact
            of the pyp_utils/assign_offspring() side-effect.  A better solution is probably
            to run pyp_utils/assign_offspring() in the background whenever a pedigree is
            renumbered.
--- 04/28/2005 While investigating Edward H. Hagen's bug report I found a problem in
            pyp_utils/assign_offspring().  If you pass it an un-renumbered pedigree,
            offspring were getting assigned to their parents's unks dictionaries but the
            dictionaries were not being "cleaned out" before the updating.  This resulted
            in two sets of offspring IDs in the same dictionary, the original IDs and the
            renumbered IDs.  Further downstream, for example in
            pyp_metrics/effective_founders_lacy(), this causes problems with
            pyp_metrics/descendants() and pyp_metrics/founder_descendants() such that
            incorrect answers are returned.
''' 04/28/2005 Edward H. Hagen reported an error in pyp_metrics/effective_founders_lacy() that
            has been fixed.  When a renumbered pedigree is used, the renumbered founder IDs
            need to be looked up from the idmap dictionary in the NewPedigree object.  A sim-
            ilar change was made to pyp_metrics/founder_descendants().

CHANGES in PyPedal 2.0.0a14
===========================
''' 04/27/2005 An OPTIONS file, which describes the keyword options that PyPedal currently
            understands, is now included in the distribution file.
''' 04/27/2005 pyp_newclasses/NewPedigree.load() calls pyp_utils/reorder() instead of
            pyp_utils/fast_reorder() if the input pedigree file does not contain birth year
            or birth date and if you set the option 'slow_reorder' to 1.  The new default
            behavior is to use the slower, but more-likely-to-be-correct, reorder() routine
            unless you are more concerned with speed than correctness.  The pad_id() method
            in pyp_newclasses/NewAnimal uses the animal ID and birth year to form an ID used
            by pyp_utils/fast_reorder() for quick sorting; if your pedigree file is numbered
            such that offspring always have larger IDs than their parents and your birthyears
            (if provided) are correct (that is, parents always born BEFORE offspring) then
            pyp_utils/fast_reorder() works fine, so it is not completely useless.  If you do
            not provide birthyears in your pedigree file but your parent IDs are always smaller
            than your  animal IDs you will likewise be okay.  Messy (i.e. real) pedigrees are
            likely to have errors that could give incorrect results with fast_reorder().  Large
            pedigrees should be reordered and renumebred and written to a file.  That way you
            only have to pay the performance penalty for slow, but correct, renumbering once.
*** 04/27/2005 Much rewriting of pyp_utils/reorder().  In fact, the routine has been completely
            rewritten.  While it is still noticeably slower than pyp_utils/fast_reorder(), it
            is guaranteed to put animals in the correct order.  Barring pedigree errors, of
            course.
--- 04/27/2005 There is now a known bug with pyp_utils/fast_reorder(): in pedigrees with
            no birth years or birth dates AND animals whose parents' ID numbers are larger
            than the animal's are reordered incorrectly.  This first manifested itself in
            screwed-up inbreeding calculations.
''' 04/27/2005 A new pedigree format code, 'A', has been added to support alleles.
''' 04/27/2005 The PEDIGREE_FORMAT_CODES file is now included in the distribution file.

CHANGES in PyPedal 2.0.0a13
===========================
''' 04/26/2005 In pyp_newclasses/NewPedigree.save() accepts an option, idformat, that specifies
            which animal, sire, and dam IDs are written.  The 'o' (original) option writes a
            pedigree with the original IDs as read from the original input pedigree file.  The
            'r' (renumbered) option will write a pedigree file containing renumbered animal, sire,
            and dam IDs.
''' 04/26/2005 In pyp_newclasses/NewPedigree.save() accepts an option, outformat, that specifies
            how the saved pedigree is written.  The 'o' (original) option writes a pedigree with
            the same pedformat as the original input pedigree file; this is useful if you have
            computed CoI, inferred sex, and that kind of thing.  The 'l' (long) option will write
            a pedigree file containing all known fields in the animal object for which there is
            are pedigree format codes (see the file PEDIGREE_FORMAT_CODES).
''' 04/26/2005 In pyp_newclasses/NewPedigree.__init__() the default logfile name is now
            <self.kw['filetag']>.log.
''' 04/26/2005 Some changes were made to layout options in pyp_graphics/draw_pedigree().
            Pedigrees are now drawn landscaped on US letter-sized pages (8.5 in x 11 in) and
            will, in theory, be tiled across pages if they cannot fit on a single page.  This
            does not work as well as hoped, but I am working on it.
''' 04/26/2005 pyp_graphics/draw_pedigree() now takes an optional parameter, gdot, that tells
            draw_pedigree() whether or not write the raw (dot language) representation of the
            pedigree to a file.  Code is written to a file named <gfilename>_pedigree.dot.
''' 04/25/2005 pyp_graphics/draw_pedigree() now takes an optional parameter, gsize, that tells
            draw_pedigree() whether or not write the raw (dot language) representation of the
            pedigree to a file.
''' 04/25/2005 pyp_graphics/draw_pedigree() now takes an optional parameter, gsize, that specifies
            the size of the resulting graphic: 'f' (default) produces as large a graph as necessary
            to accomodate the layout and 'l' produces a diagram scaled to fit on a letter-sized sheet
            of paper.
+++ 04/25/2005 Added a new method, save(), to pyp_newclasses/NewPedigree().  This long-overdue
            feature lets you easily save a pedigree after, for example, computing CoI.  It
            eliminates the need to perform time-consuming computations on pedigrees every time
            they are accessed by making it easy to store a "large format" PyPedal pedigree.
*** 04/25/2005 Fixed a bug in pyp_newclasses/NewPedigree.preprocess() in which records for
            sires and dams that appear in a pedigree, but which do not have individual entries
            in the pedigree file, were assigned birth years of 0 when dummy records were inserted
            into the pedigree.  This was causing pyp_newclasses/NewAnimal.pad_id() to return a
            munged up paddedID that caused problems in pyp_utils/fast_reorder().  Tricky problem
            to find, that was.
''' 04/25/2005 Made a small change to pyp_newclasses/NewPedigree.preprocess() so that blank
            lines are caught and handled correctly.  Before this fix a blank line with, say,
            an embedded TAB character would cause a fatal error b/c it was treated as a
            "regular" record.

CHANGES in PyPedal 2.0.0a12
===========================
''' 04/19/2005 Rolled back changes to pyp_newclasses/NewAnimal.pad_id() in response to a bug
            report that I could not duplicate.

CHANGES in PyPedal 2.0.0a11
===========================
??? 04/15/2005 I think that pyp_graphics/draw_pedigree() may be inserting a spurious node
            when drawing the pedigree, but I have not yet figured out where it is happening.
''' 04/15/2005 Removed references to "species" from pyp_newclasses/NewAnimal.printme() and
            pyp_newclasses/NewAnimal.stringme().
''' 04/15/2005 Tweaked pyp_newclasses/NewAnimal.pad_id() so that it casts values to INTs
            before concatenating them.
*** 04/15/2005 pyp_newclasses/NewPedigree.preprocess has been fixed to handle parents
            that do not have their own entry in the pedigree file.  They are added to
            the pedigree with an unknown sire and dam.
''' 04/15/2005 Changed pyp_nrm/inbreeding() so that the output file written contains
            the original ID, the renumbered ID, and the CoI (in that order).
''' 04/15/2005 Added a dictionary, "backmap", to pyp_newclasses/NewPedigree that maps
            renumbered IDs (keys) to original IDs (values).  It is the reverse direction
            of that provided by idmap.
+++ 04/15/2005 Added pyp_graphics/plot_pct_founders_by_year() to plot the frequency
            of founders in each birth year.  NOTE: This requires matplotlib
            (http://matplotlib.sourceforge.net/).  If matplotlib is not
            installed/cannot be imported, a value of 0 is returned.
''' 04/15/2005 Fixed pyp_graphics/draw_pedigree() so that it labels animals with their
            original IDs instead of their renumbered IDs.
''' 04/15/2005 Fixed pyp_graphics/draw_pedigree() so that it displays the gtitle.
''' 04/14/2005 Fixed a typo in pyp_newclasses/NewAnimal.__init__() that broke
            proper birthyear assignment.
+++ 04/14/2005 Added pyp_graphics/plot_founders_by_year() to write a histogram of
            number-of-founders by year of birth.  NOTE: This requires matplotlib
            (http://matplotlib.sourceforge.net/).  If matplotlib is not
            installed/cannot be imported, a value of 0 is returned.
''' 04/14/2005 Changed pyp_demog/BASE_DEMOGRAPHIC_YEAR from 1950 to 1900.  This brings
            it in line with the default birthyear of 1900 used in pyp_newclasses.
+++ 04/14/2005 Added pyp_demog/founders_by_year() which provides a dictionary, keyed by
            birthyear, of the number of founders with each birthyear.

CHANGES in PyPedal 2.0.0a10
===========================
''' 04/14/2005 Fixed a typo in the MANIFEST.in file used to roll the distribution.  The
            __init__.py file should be included now.
''' 04/14/2005 Added __version__.py to the distribution.
XXX 04/14/2005 Disabled gettext functionality in pyp_classes after receiving a report of
            problems under FreeBSD (thanks to Thomas von Hassel).

CHANGES in PyPedal 2.0.0a9
==========================
''' 03/30/2005  pyp_io/pyp_file_header() and pyp_io/pyp_file_footer() now work.
*** 03/30/2005  Added pyp_metrics/effective_founders_lacy(), which is a re-write of
            pyp_metrics/a_effective_founders_lacy() that works with the new object
            model.  Correctness was verified by comparing results against Table 3
            in Lacy (1989) and Tables I & II in Boichard et al. (1997).You can use
            examples/new_lacy.py to verify the results.
*** 03/30/2005  Fixed a nasty bug in pyp_metrics/a_effective_ancestors_definite() that was
            due to an indentation screwup when moving from one editor to another.  Correctness
            was verified by comparing results against Tables I and II in Boichard et al. (1997).
            You can use examples/new_lacy.py to verify the results.
''' 03/30/3005  Added pyp_utils/pyp_nice_time() which returns the current date and time as
            a nicely-formatted string.
''' 03/29/2005  Added pyp_metrics/descendants() and pyp_metrics/founder_descendants() to
            support the rewritten pyp_metrics/effective_founders_lacy() routine.
''' 03/29/2005  Added pyp_utils/assign_offspring(), which adds offspring of an animal to that
            animal's 'unks' list.
!!! 03/28/3005  Stubbed pyp_io/pyp_file_header() and pyp_io/pyp_file_footer() in preparation
            for standardizing the output files written by PyPedal.
+++ 03/04/2005  Added pyp_graphics module.  It currently includes three functions from the
            ASPN Python Cookbook (http://aspn.activestate.com/ASPN/Cookbook/Python/) for
            visualizing the sparsity and the elements of matrices.  I have also moved the
            draw_pedigree() function from pyp_utils to pyp_graphics.  From now on, any functions
            related to visualization will go in pyp_graphics.
--- 02/24/2005  It looks like the sons and daus lists get screwed up when the pedigree is re-
            numbered, but I think that it is a consequence of the item below.
--- 02/24/2005  When a pedigree that needs renumbering is read, pyp_utils/preprocess() throws
            an exception when trying to assign sex codes because it uses the sire's and dam's
            original IDs as keys.  This represents fundamental breakage in the ordering of
            events in pedigree creation.  I have sort-of hacked around this for the moment, but
            the bug is still there.
''' 02/23/2005  Added a new pedigree format code, asdgb, to pyp_utils/preprocess().
+++ 02/23/2005  Added pyp_metrics/generation_lengths_all() which computes the average generation
            interval in years for each of the four selection paths (sire-son, sire-daughter,
            dam-son, and dam-daughter) for all births of a parent's offspring.
+++ 02/23/2005  Added pyp_utils/assign_sexes() which iterates over a renumbered PyPedal pedigree
            to update sexes of sires and dams based on knowledge of their sons and daughters.
            This seems to catch cases that are missed in pyp_utils/preprocess(), which needs to
            be cleaned up.
''' 02/23/2005  Upon further examination, it seems like males and females are being correctly
            assigned.  Hm...OK.  Fixed a bug in pyp_utils/preprocess() that incorrectly
            assigned sires and dams with unknown parents to the sons and daus lists of the
            last animal in the pedigree.  This was fixed by casting to an INT before a
            comparison with 0.
--- 02/11/2005  See examples/generations.py -- sons and daughters are not being correctly
            assigned to foo.sons and foo.daus.
--- 02/11/2005  Need to fix a bug in pyp_utils/new_preprocesss() in which unknown sires
            and dams (animals with IDs of 0) were being put into male, female, son, and
            daughter lists.
''' 02/11/2005  Fixed a bug in pyp_utils/preprocesss() in which unknown sires and dams
            (animals with IDs of 0) were being put into male, female, son, and daughter
            lists.
+++ 02/11/2005  Added pyp_metrics/generation_lengths() which computes the average generation
            interval in years for each of the four selection paths (sire-son, sire-daughter,
            dam-son, and dam-daughter) for the oldest (first-born) of parents.
!!! 02/11/2005  Added pyp_metrics/num_traced_gens(), pyp_metrics/num_equiv_gens(), and
            pyp_metrics/pyp_partial_inbreeding().
XXX 02/11/2005  Lots of code cleanup in pyp_classes.  Removed pad_id() and renamed
            pad_id_new() to pad_id().
XXX 02/11/2005  Removed the originalID and species attributes from the Animal() class.

CHANGES in PyPedal 2.0.0a8
==========================
+++ 11/01/2004  Started working on an output-rendering framework that will easily allow for
            writing strings as HTML or text, depending on a variable set in pypedal.conf.  Right now,
            use PYPEDAL_OUTPUT_TYPE, which is hard-coded in pyp_classes.
*** 07/21/2004  Major overhaul of pyp_utils/preprocess() pedigree format code handling.

CHANGES in PyPedal 2.0.0a7
==========================
''' 08/12/2004  Changed pyp_metrics/fast_a_coefficients() to catch exceptions
        when no relationship matrix is provided and the pedigree is too large for
        fast_a_matrix() to compute one.  In these cases, a value of -999.9 is
        returned.
''' 08/12/2004  Changed pyp_metrics/a_effective_ancestors_indefinite() to catch exceptions
        when no relationship matrix is provided and the pedigree is too large for
        fast_a_matrix() to compute one.  In these cases, a value of -999.9 is
        returned.
''' 08/12/2004  Changed pyp_metrics/a_effective_ancestors_definite() to catch exceptions
        when no relationship matrix is provided and the pedigree is too large for
        fast_a_matrix() to compute one.  In these cases, a value of -999.9 is
        returned.
''' 08/12/2004  Changed pyp_metrics/a_effective_founders_boichard() to catch exceptions
        when no relationship matrix is provided and the pedigree is too large for
        fast_a_matrix() to compute one.  In these cases, a value of -999.9 is
        returned.
''' 08/12/2004  Changed pyp_metrics/a_effective_founders_lacy() to catch exceptions
        when no relationship matrix is provided and the pedigree is too large for
        fast_a_matrix() to compute one.  In these cases, a value of -999.9 is
        returned.
''' 08/12/2004  Made changes to pyp_metrics/a_coefficients() to catch exceptions in
        fast_a_matrix() or fast_a_matrix_r() when they cannot allocate a matrix.
        When an exception is caught all successive computations are performed on
        a 1x1 matrix whose value is -999.9.  This is kind of hacky, but will prevent
        many problems.
''' 08/12/2004  Added summary statistics (mean/min/max) to the pyp_nrm/inbreeding() routine.
*** 08/12/2004  Changed pyp_classes/Pedigree.nus() to use dictionaries instead of lists;
        Changed pyp_classes/Pedigree.nud() to use dictionaries instead of lists;
        Changed pyp_classes/Pedigree.nug() to use dictionaries instead of lists;
        Changed pyp_classes/Pedigree.nuy() to use dictionaries instead of lists;
        Changed pyp_classes/Pedigree.nuf() to use dictionaries instead of lists.
        There are, as always, huge gains in large pedigrees from doing this.  Why?
        Because, silly rabbit, you avoid looping over increasingly-large arrays for
        every animal in the pedigree.  It is not a big win on a small pedigree, but
        on, e.g., an 800,000 animal pedigree it makes a very significant difference.
*** 08/02/2004  Changed pyp_utils/renumber() so that it checks sire and dam birthyears
        before renumbering.  If the child has an earlier birthdate than a parent,
        that parent is set to unknown, '0'.  This is a temporary fix pending a
        rewrite of the actual pedigree component of PyPedal.  I am thinking that a
        dictionary of animal objects might be a better way to handle things than a
        simple list.  If everything was in a dictionary, for example, then it would
        be simple to check the sire and dam birthyears using a key->value lookup.  As
        is, there is no reliable way to check those sorts of things unless the pedigree
        has been reordered and renumbered.
''' 07/31/2004  Added a new pedigree format code, asdbx, to pyp_utils/preprocess().
''' 07/31/2004  Changed pyp_classes/Animal() so that the default birthyear is 1900.
''' 07/31/2004  Added debug statements to several routines in pyp_utils.
*** 07/29/2004  Added pyp_utils/new_preprocess() which is the major rewrite of the pedigree
        format code handling that I have been promising for a while.
''' 07/21/2004  Added a species attribute to the Animal() class which defaults to 'u'.
+++ 07/21/2004  Added pyp_utils/reverse_string() to reverse a string.  Useful when you have a
        string on which you cannot readily use string.split().
+++ 07/21/2004  Added pyp_demog/age_distribution() for computing the distribution of ages in
        a population.
+++ 07/21/2004  Added pyp_utils/simple_histogram_dictionary() for creating a simple test-based
        histogram from a dictionary of integral counts.
''' 07/21/2004  Changed Animal/__init__() so that birthyears default to -999 when they are
        not specified in the pedigree file.  This was done to support age computations
        in the demographics module.
''' 07/21/2004  Added age and alive attributes to the Animal() class which default to -999.
+++ 07/21/2004  Added a new file, pyp_demog.py, which contains some routines for
        demographic computations, such as age distributions.  There are going
        to be some potentially hairy issues with date handling.  Maybe.  If I
        don't get lazy and just say that everything is on a year basis.
''' 07/21/2004  Added a stub file, pyp_peel.py, for forthcoming support for pedigree
            peeling.
''' 07/20/2004  Added some notes to pyp_utils/preprocess() detailing an idea for
        greatly improving the way in which pedigree format strings are handled.
        No code has been written yet, but the idea is on the table.
*** 07/20/2004  I *think* that pyp_metrics/pedigree_completeness() works correctly now.
''' 07/20/2004  Added a breedcode attribute to the Animal() class which defaults to 'u'.
''' 07/20/2004  Fixed recurse_pedigree_n() so that it recurses to the correct depth.
+++ 07/20/2004  Added recurse_pedigree_onesided() to pyp_nrm.  It recurses to return the
        complete sire or dam side of an animal's pedigree.
*** 06/11/2004  Added stringme() methods to the Animal() and Pedigree() classes  to support
        integration with the GUI.  The output returned is identical to the printme() methods.
+++ 06/11/2004  Started working on a GUI for PyPedal, pyp_gui.  It requires that you have
        the wxPython toolkit installed.  How to do that is up to you.
*** 05/26/2004  Added pyp_nrm/recurse_pedigree_n(), which returns a pedigree of a
        specified depth.
''' 05/26/2004  Fixed pyp_classes/Animal() so that animal names are actually assigned
        correctly in __init__().
*** 05/26/2004  Added pyp_utils/set_generation() to infer the generation to which
        each individual in the pedigree belongs.  This was added to make
        pyp_metrics/pedigree_completeness() easier to code as the igen is
        really just a count of the depth of an individual's pedigree.
''' 05/26/2004  Added an igen (inferred generation) attribute to the Animal() class
        which defaults to -999.  A non-negative value will be assigned to
        this attribute by pyp_utils/set_generation().
''' 05/26/2004  Added a pedcomp attribute to the Animal() class which defaults to
        -999.9.  A non-negative value will be assigned to this attribute by
        pyp_metrics/pedigree_completeness().
--- 05/26/2004  There is a bug in pyp_utils/renumber() such that the offspring stored
        in myped[i].sons, myped[i].daus, and myped[i].unks are not updated to
        reflect changes in animal IDs when a pedigree is renumbered.
--- 05/26/2004  There is still a bug in pyp_utils/preprocess() where the sex of an
        animal is assigned based on a "best guess".
*** 05/26/2004  Added pyp_utils/load_pedigree(), which is a wrapper around several
        pedigree processing routines.  It is a convenient way to roll several
        common operations (load, reorder, renumber, etc.) into a single call.
''' 05/26/2004  Updated pyp_utils/renumber() so that the renumberedID attribute is
        set as each animal is renumbered.
''' 05/26/2004  Added originalID and renumberedID properties to the Animal() class
        with the eventual goal of eliminating ID maps from the renumbering
        code.  originalID defaults to animalID and renumberedID defaults to
        -999.
''' 05/26/2004  Made small changes to Animal.printme() method to add new attributes.
''' 05/26/2004  Changed pyp_metrics/effective_founder_genomes() so that the quiet flag
        suppresses all outout to stdio.

CHANGES in PyPedal 2.0.0a6
==========================
*** 05/25/2004  Added pyp_metrics/effective_founder_genomes() for running gene-drop
        simulations on a pedigree to determine the effective number of
        founder genomes as defined in Lacy (1989) and Boichard et al. (1997).
*** 05/25/2004  Added pyp_metrics/assign_founder_alleles() to be used for setting-up
        gene-drop simulations on pedigrees for which no founder alleles are
        provided in the input file.
*** 05/25/2004  Added a new pedigree format code, 'asdt', to pyp_classes/preprocess()
            to support simple pedigrees with genotype data (two alleles only).
''' 05/25/2004  Added an alleles attribute to pyp_classes/Animal() to support
            gene dropping.
*** 05/25/2004  Added pyp_utils/sort_dict_by_keys() to return a dictionary where the keys
            are sorted in ascending order (from "Python Cookbook", P. 39).

CHANGES in PyPedal 2.0.0a5
==========================
*** 05/06/2004  Added pyp_nrm/fast_a_coefficients() for testing some loop
            optimization.
*** 05/06/2004  Moved some code in pyp_utils/preprocess() outside of a loop in
            which it did not belong for a HUGE win in performance!
''' 05/06/2004  Made changes to pyp_utils/preprocess() to support the changed
            attribute types in pyp_classses/Animal/__init__().
''' 05/06/2004  Changed self.sons, self.daus. and self.unk from lists to
            dictionaries.
''' 05/03/2004  Tweaked pyp_classes/Pedigree/nus() and pyp_classes/Pedigree/nud()
            so that the counts computed do NOT include unknown sires or dams.
''' 04/27/2004  Added some "try...except" code to pyp_utils/preprocess() so that
            non-renumbered pedigrees do not cause the sex assignment code to
            halt the program.
''' 04/27/2004  Added a "method" parameter to pyp_metrics/a_coefficients() so that
            the user can specify which type of relationship they would like --
            the NRM using pyp_nrm/fast_a_matrix() or the complete (inbreeding-
            adjusted) RM using pyp_nrm/fast_a_matrix_r().  Method takes the values
            'frm' (full relationship matrix) or 'nrm' (numerator relationship
            matrix).
+++ 04/27/2004  Added pyp_nrm/fast_a_matrix_r(), which corrects the relationships
            in A for the inbreeding of the parents.  The A matrix returned by
            fast_a_matrix_r is, therefore, NOT a numerator relationship matrix.
            It is a matrix of coefficients of relationship.

CHANGES in PyPedal 2.0.0a4
==========================
''' 04/23/2004  Possibly corrected a subtle bug in the Animal.pad_id_new method that
            resulted in incorrect sorting in some cases.
+++ 04/23/2004  Added pyp_metrics/mating_coi(), which computes the coefficient of
            inbreeding of offspring that would result from a matinge  between
            two animals.
+++ 04/23/2004  Added pyp_metrics/relationship(), which computes the coefficient of
            relationship between two animals.
''' 04/23/2004  Added three new attributes to Animal() objects: self.sons, self.daus,
            and self.unks, which are lists to store renumbered animalIDs of sons
            and daughters of an animal, as well as the IDs of offspring with un-
            known sex.
''' 04/20/2004  Added a 'name' attribute to the Animal() object to accomodate,
            e.g., dog breeders.
+++ 04/20/2004  Added a new procedure, pyp_utils/draw_pedigree(), to draw
            pedigrees using the pydot interface to Graphviz.  If the necessary
            modules are not installed the procedure will return a result of '0'
            rather than exploding.  :-)
''' 04/20/2004  Beginnings of a tutorial in the PyPedal manual.
''' 04/19/2004  Corrected a minor bug in pyp_nrm/inbreeding_tabular() that
            resulted in negative CoI being written to returned dictionary.
''' 04/19/2004  Enhanced pyp_nrm/inbreeding() to update Animal() instances with
            the CoI computed by that routine.
''' 04/19/2004  Enhanced pyp_utils/preprocess() to assign sex codes to Animal()
            instances based on the inferred sex iff no sex code was specified
            in the pedigree file.

CHANGES in PyPedal 2.0.0a3
==========================
+++ 04/19/2004  Added a new routine, pyp_metrics/a_effective_ancestors, that will
            call either a_effective_ancestors_definite() or
            a_effective_ancestors_indefinite() depending on the size of the
            pedigree passed in.  Currently, they cutoff is 1,000.
+++ 04/19/2004  Added a new routine, pyp_metrics/a_effective_ancestors_indefinite()
            routine, that attempts to estimate upper and lower bounds for f_a
            in large pedigrees rather than computing all contributions
            explicitly.  a_effective_ancestors_indefinite() is NOT WELL TESTED.
            There are almost certainly bugs; the routine does not iterate.  All
            I can really tell you for sure is that it sometimes returns values
            that are extreme underestimates of f_a.  It is supposed to work
            reasonably well on large pedigrees rather than small ones.
*** 04/19/2004  FINALLY fixed all known bugs in the tragically-written
            pyp_metrics/a_effective_ancestors_definite() routine!
+++ 04/16/2004  Added pyp_utils/set_ancestor_flag() to be used to
            set ancestor flags.
+++ 04/16/2004  Added an ancestor flag to pyp_classes/Animal/__init__().
*** 04/15/2004  Fixed bugs in pyp_metrics/a_effective_founders_lacy()
            and pyp_metrics/a_effective_founders_boichard() that
            were introduced by changes in pyp_utils/preprocess().
*** 04/15/2004  Changed pyp_utils/preprocess() so that pedigree entries
            are not made for unknown parents by the "add parent
            records to the pedigree if they are not already there"
            routine.
+++ 04/15/2004  Added pyp_metrics/common_ancestors() which
            returns a list of all of the ancestors that
            two animals share in common.
+++ 04/15/2004  Added pyp_metrics/related_animals() which
            recurses through a pedigree to build a
            list of all animals related to a given
            animal, if any.

CHANGES in PyPedal 2.0.0a2
==========================
***/+++ 04/??/2004  Refactored and added new code to pyp_nrm to
            support a VanRaden's iterative method for
            computing CoI in large pedigrees:
                inbreeding()
                inbreeding_vanraden()
                recurse_pedigree()
                inbreeding_tabular()

CHANGES in PyPedal 2.0.0a1
==========================
--- 03/31/2004  pyp_utils/fast_a_matrix blows up when passed a
            pedigree of size 80,000 or so;
+++ 03/31/2004  pyp_utils/pedigree_range was added -- allows the
            easy creation of a pedigree containing animals
            1 through <n> from a large pedigree.  This will
            be used to determine how large a pedigree PyPedal
            can currently handle;
+++ 03/31/2004  pyp_utils/preprocess rewritten to use dictionary
            lookups instead of list lookups -- improved the
            performance of this routine by about 2 orders of
            magnitude;
+++     03/31/2004      pyp_utils/preprocess now accepts a delimiter to
            accomodate pedigree files that are not CSV;
+++     03/31/2004      pyp_utils/preprocess now properly handles base
            animals that do not have an entry in the pedigree
            file, that is, who only appear as a sire or dam
            in another animal's record;
*** 04/06/2003  Complete rewrite of PyPedal begun.  Major changes
            include incorporation of metadata into the pedigree
            object.

CHANGES in PyPedal 0.0.1
========================
*** First version released to the general public;
--- a_effective_founders_boichard() does not return correct
    answers.  I have not yet found the error in my implementation
    of Boichard's algorithm;
--- a_effective_ancestors_definite() does not return correct
        answers.  I have not yet found the error in my implementation
        of Boichard's algorithm;
??? a_effective_founders_lacy() is believed to work correctly;
--- a_matrix() is deprecataed in favor of fast_a_matrix().  It will
    return a properly-formed numerator relationship matrix, but it
    is extremely slow (orders-of-magnitude slower than fast_a_matrix());
--- reorder() is deprecated in favor of fast_reorder();
??? Neither reorder() nor fast_reorder() should be used on a pedigree
    returned by renumber() unless the results are checked very carefully.
    In some cases, renumbered pedigrees are reordered incorrectly.  This
    is due to a bug in the ID padding algorithm which is believed to be
    fixed, but more testing is needed.