Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

initial commit

  • Loading branch information...
commit bc1f1baeb81de577e7143db2cd4dc0beedfa5335 0 parents
@piantado authored
688 LICENSE
@@ -0,0 +1,688 @@
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License version 3 as
+published by the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+=======================================================================
+== GNU PUBLIC LICENSE
+=======================================================================
+
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ <program> Copyright (C) <year> <name of author>
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
411 LOTlib/BasicPrimitives.py
@@ -0,0 +1,411 @@
+# -*- coding: utf-8 -*-
+
+
+"""
+ Primitives that may be used in the LOT
+
+
+ TODO: Include "multiset" objects so that union can either take into account uniqueness or not!
+
+"""
+from LOTlib.Miscellaneous import *
+
+import re
+
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+# Lambda calculus & Scheme
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+def apply_(f,x):
+ return f(x)
+
+def cons_(x,y):
+ try: return [x,y]
+ except: return None
+
+def cdr_(x):
+ try: return x[1:]
+ except: return None
+
+def car_(x):
+ try: return x[0]
+ except: return None
+
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+# Combinators -- all curried
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+def I(x):
+ return x
+
+def K(x): # constant function
+ return (lambda y: x)
+
+def S(x): #(S x y z) = (x z (y z))
+ # (S x) --> lambda y lambda z:
+ return lambda y: lambda z: x(z)( y(z) )
+
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+# For language / semantics
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+def presup_(a,b):
+ if a: return b
+ else:
+ if b: return "undefT" # distinguish these so that we can get presup out
+ else: return "undefF"
+
+def is_undef(x):
+ if isinstance(x,list):
+ return map(is_undef, x)
+ else:
+ return (x is None) or (x =="undefT") or (x == "undefF") or (x == "undef")
+
+def collapse_undef(x):
+ """
+ Change undefT->True and undefF->False
+ """
+ if isinstance(x,list): return map(collapse_undef, x)
+ else:
+ if x is "undefT": return True
+ elif x is "undefF": return False
+ else: x
+
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+# Basic arithmetic
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+import math
+
+def negative_(x): return -x
+
+def plus_(x,y): return x+y
+def times_(x,y): return x*y
+def divide_(x,y):
+ if y > 0: return x/y
+ else: return float("inf")*x
+def subtract_(x,y): return x-y
+
+def sin_(x):
+ try:
+ return math.sin(x)
+ except: return float("nan")
+def cos_(x):
+ try:
+ return math.cos(x)
+ except: return float("nan")
+def tan_(x):
+ try:
+ return math.tan(x)
+ except: return float("nan")
+
+def sqrt_(x):
+ try: return math.sqrt(x)
+ except: return float("nan")
+
+def pow_(x,y):
+ #print x,y
+ try: return pow(x,y)
+ except: return float("nan")
+
+def exp_(x):
+ try:
+ r =math.exp(x)
+ return x
+ except:
+ return float("inf")*x
+
+def log_(x):
+ if x > 0: return math.log(x)
+ else: return -float("inf")
+
+def log2_(x):
+ if x > 0: return math.log(x)/log(2.0)
+ else: return -float("inf")
+def pow2_(x):
+ return math.pow(2.0,x)
+
+def mod_(x,y): return (x%y)
+
+PI = math.pi
+E = math.e
+
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+# Basic logic
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+def id_(A): return A # an identity function
+
+def and_(A,B): return (A and B)
+def nand_(A,B): return not (A and B)
+def or_(A,B): return (A or B)
+def nor_(A,B): return not (A or B)
+def xor_(A,B): return (A and (not B)) or ((not A) and B)
+def not_(A): return (not A)
+def implies_(A,B): return (A or (not B))
+def iff_(A,B): return ((A and B) or ((not A) and (not B)))
+
+def if_(C,X,Y):
+ if C: return X
+ else: return Y
+def ifU_(C,X):
+ if C: return X
+ else: return 'undef'
+
+def gt_(x,y): return x>y
+def gte_(x,y): return x>=y
+def lt_(x,y): return x<y
+def lte_(x,y): return x<=y
+def eq_(x,y): return x==y
+def zero_(x,y): return x==0.0
+
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+# Set-theoretic primitives
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+def union_(A,B): return A.union(B)
+def intersection_(A,B): return A.intersection(B)
+def setdifference_(A,B): return A.difference(B)
+def select_(A): # choose an element, but don't remove it
+ if len(A) > 0:
+ x = A.pop()
+ A.add(x)
+ return set([x]) # but return a set
+ else: return set() # empty set
+
+
+def exhaustive_(A,B): return coextensive_(A,B)
+def coextensive_(A,B): # are the two sets coextensive?
+ #print A,B
+ return (A.issubset(B) and B.issubset(A))
+
+def equal_(A,B): return (A == B)
+def equal_word_(A,B): return (A == B)
+
+def empty_(A): return (len(A)==0)
+def nonempty_(A): return not empty_(A)
+def cardinality1_(A): return (len(A)==1)
+def cardinality2_(A): return (len(A)==2)
+def cardinality3_(A): return (len(A)==3)
+def cardinality4_(A): return (len(A)==4)
+def cardinality5_(A): return (len(A)==5)
+def cardinality_(A): return len(A)
+
+# returns cardinalities of sets and otherwise numbers
+def cardify(x):
+ if isinstance(x, set): return len(x)
+ else: return x
+
+def cardinalityeq_(A,B): return cardify(A) == cardify(B)
+def cardinalitygt_(A,B): return cardify(A) > cardify(B)
+def cardinalitylt_(A,B): return cardify(A) > cardify(B)
+
+def subset_(A,B):
+ return A.issubset(B)
+
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+# Quantification
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+def not_exists_(F,S): return not exists_(F,S)
+def exists_(F,S):
+ #if not isinstance(S,list): return None
+ for s in S:
+ if F(s): return True
+ return False
+
+def not_forall_(F,S): return not forall_(F,S)
+def forall_(F,S):
+ #if not isinstance(S,list): return None
+ for s in S:
+ if not F(s): return False
+ return True
+
+def iota_(F,S):
+ """
+ The unique F in S. If none, or not unique, return None
+ """
+ match = None
+ for s in S:
+ if F(s):
+ if match is None: match = s
+ else: return None # We matched more than one
+ return match
+
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+# Tree operations
+# In a tree T, check relations between some elements. Sometimes T is
+# not used, but we leave it in all functions for simplicity
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+def sisters_(T, x, y, equality=False):
+ """
+ Check if x,y are sisters in T
+ """
+ for s in T.all_subnodes():
+ if immediately_dominates_(s,x) and immediately_dominates_(s,y): return True
+ return False
+
+def immediately_dominates_(x, y):
+ if isinstance(x, FunctionNode):
+ for s in x.args:
+ if s is y: return True
+
+ return False
+
+def dominates_(x,y):
+ """
+ This checks if x >> y, but using object identity ("is") rather than equality
+ """
+ if x is y: return False # A node does not dominate itself
+ if isinstance(x, FunctionNode):
+ for s in x.all_subnodes():
+ if s is y: return True
+ return False
+
+def tree_up_(T, x):
+ """
+ Go up one node in the tree
+ BUT if you are the root (T), then return yourself
+ """
+
+ if x is T: return T
+
+ for s in T.all_subnodes():
+ if immediately_dominates_(s,x): return s
+ return None
+
+def children_(x):
+ if isinstance(x, FunctionNode): return [ c for c in x.args ]
+ else: return []
+
+def descendants_(x):
+ if isinstance(x, FunctionNode): return [ c for c in x.all_subnodes() ]
+ else: return []
+
+def tree_root_(T):
+ return T
+
+def is_nonterminal_type_(x,y):
+ # Check if x is of a given type, but remove corefence information from X (y is the type)
+
+ if x is None or y is None: return False
+
+ if not isinstance(x,str): x = x.name
+
+ # remove the .coreference info
+ x = re.sub(r"\..+$", "", x)
+
+ return (x==y)
+
+def ancestors_(T, x):
+ if not isinstance(x, FunctionNode): return []
+ out = []
+ while not tree_is_(x,T):
+ x = tree_up_(T,x)
+ out.append(x)
+ return out
+
+def whole_tree_(T):
+ # LIST type of all elements of T
+ return [ti for ti in T.all_subnodes() ]
+
+def tree_is_(x,y): return (x is y)
+
+## Co-reference (via strings)
+coref_matcher = re.compile(r".+\.([0-9]+)$")
+def co_refers_(x,y):
+
+ if x is y: return True # Hmm should have this, I Think (regardless of type, etc)
+
+ ## Check if two FunctionNodes or strings co-refer (e.g. are indexed with the same .i in their name)
+ xx = x.name if isinstance(x, FunctionNode) else x
+ yy = y.name if isinstance(y, FunctionNode) else y
+
+ mx = coref_matcher.search(xx)
+ my = coref_matcher.search(yy)
+
+ #print ">>>", x
+ #print ">>>", y
+ #if mx is None or my is None: print "--", "FALSE"
+ #else: print "--", mx.groups("X")[0], my.groups("Y")[0], (mx.groups("X")[0] == my.groups("Y")[0])
+
+ if mx is None or my is None:
+ return False
+ else:
+ return (mx.groups("X")[0] == my.groups("Y")[0]) # set the default in groups so that they won't be equal if there is no match
+
+def non_xes_(x,T):
+ #print ">>", T
+ return filter(lambda v: v is not x, T)
+
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+# closure operations
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+## TODO: Add transitive closure of an operation
+
+def filter_(f,x):
+ return filter(f,x)
+
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+# counting list
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+from collections import defaultdict
+
+# the next word in the list -- we'll implement these as a hash table
+word_list = ['one_', 'two_', 'three_', 'four_', 'five_', 'six_', 'seven_', 'eight_', 'nine_', 'ten_']
+next_hash, prev_hash = [defaultdict(lambda: 'undef'), defaultdict(lambda: 'undef')]
+for i in range(1, len(word_list)-1):
+ next_hash[word_list[i]] = word_list[i+1]
+ prev_hash[word_list[i]] = word_list[i-1]
+next_hash['one_'] = 'two_'
+next_hash['ten_'] = 'undef'
+prev_hash['one_'] = 'undef'
+prev_hash['ten_'] = 'nine_'
+next_hash['X'] = 'X'
+prev_hash['X'] = 'X'
+
+word_to_number = dict() # map a word like 'four_' to its number, 4
+for i in range(len(word_list)):
+ word_to_number[word_list[i]] = i+1
+word_to_number['ten_'] = 'A' # so everything is one character
+
+prev_hash[None] = None
+def next_(w): return next_hash[w]
+def prev_(w): return prev_hash[w]
+
+# and define these
+one_ = 'one_'
+two_ = 'two_'
+three_ = 'three_'
+four_ = 'four_'
+five_ = 'five_'
+six_ = 'six_'
+seven_ = 'seven_'
+eight_ = 'eight_'
+nine_ = 'nine_'
+ten_ = 'ten_'
+undef = 'undef'
+
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+# Access arbitrary features
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+def F1(x): return x.F1
+def F2(x): return x.F2
+def F3(x): return x.F3
+def F4(x): return x.F4
+def F5(x): return x.F5
+def F6(x): return x.F6
+def F7(x): return x.F7
+def F8(x): return x.F8
+def F9(x): return x.F9
+def F10(x): return x.F10
+
+# Some of our own primitivesS
+def is_color_(x,y): return (x.color == y)
+def is_shape_(x,y): return (x.shape == y)
+def is_pattern_(x,y): return (x.pattern == y)
+
+
+from LOTlib.FunctionNode import FunctionNode
91 LOTlib/DataAndObjects.py
@@ -0,0 +1,91 @@
+"""
+ This class defines different types of data for models.
+ It also provides "Obj"s for bundling together features
+
+ For functions, we have a data object consisting of the [output, args]
+
+"""
+from copy import deepcopy
+
+from LOTlib.Miscellaneous import weighted_sample
+
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class FunctionData:
+ """
+ This is a nicely wrapped kind of data--if we give it to a FunctionHypothesis, it knows
+ to extract the "args" (NOT the output) and run those on FunctionHypothesis.value(*args)
+ So when you have functional hypotheses, this is a convenient form of data
+ """
+
+ def __init__(self, args, output, **kwargs):
+ self.args = args
+ self.output = output
+ self.__dict__.update(kwargs)
+
+ def __repr__(self): return str(self.args) + " |-> " + str(self.output)
+
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class UtteranceData:
+ """
+ A wrapper for utterances. An utterance data point is a word, in a context, with some set of possible words we could have said
+ """
+ def __init__(self, word, context, all_words):
+ self.__dict__.update(locals())
+
+ def __repr__(self):
+ return str(self.word)+': '+str(self.context) + " / " + str(self.all_words)
+
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class Obj:
+ """ Represent bundles of features"""
+
+ def __init__(self, **f):
+ for k, v in f.iteritems():
+ setattr(self, k, v)
+
+ def __str__(self):
+ outstr = '<OBJECT: '
+ for k, v in self.__dict__.iteritems():
+ outstr = outstr + str(k) + '=' + str(v) + ' '
+ outstr = outstr + '>'
+ return outstr
+
+ def __repr__(self): # used for being printed in lists
+ return str(self)
+
+ # We may or may not want these, depending on whether we keep Objs in sets...
+ #def __eq__(self, other): return str(self) == str(other)
+ #def __hash__(self): return hash(str(self))
+
+def make_all_objects(**f):
+ """
+ This takes a list of lists and crosses them into all objects
+ e.g. make_all_objects( size=[1,2,3], color=['red', 'green', 'blue'] )
+ """
+
+ keys = f.keys()
+ out_objs = []
+
+ for vi in f[keys[0]]:
+ out_objs.append(Obj( **{keys[0]: vi} ))
+
+ for i in range(1, len(keys)): # for every other key
+ newout = []
+ for o in out_objs:
+ for vi in f[keys[i]]:
+ ok = deepcopy(o)
+ setattr(ok, keys[i], vi)
+ newout.append(ok)
+ out_objs = newout
+
+ return out_objs
+
+
+# make a set of size N appropriate to using "set" functions on -- this means it must contain copies, not duplicate references
+def sample_sets_of_objects(N, objs):
+ s = weighted_sample(objs, N=N, returnlist=True) # the set of objects
+ return map(deepcopy, s) # the set must NOT be just the pointers sampled, since then set() operations will collapse them!
+
109 LOTlib/EnumerativeSearch.py
@@ -0,0 +1,109 @@
+# -*- coding: utf-8 -*-
+from LOTlib.Miscellaneous import *
+from LOTlib.PriorityQueue import *
+
+## Make this interruptable by CTRL-C
+import sys
+import signal
+SIG_INTERRUPTED = False
+def signal_handler(signal, frame):
+ global SIG_INTERRUPTED
+ SIG_INTERRUPTED = True
+signal.signal(signal.SIGINT, signal_handler)
+
+
+def enumerative_search( start, next_states, score, N=-1, breakout=float("inf"), yield_immediate=False):
+ """
+ start -- either a list or a single element saying what we start with. Generally this works better with a rich starting base
+
+ next_states is a function mapping states to a list of potential next states
+
+ score - maps states to values, higher is better (UNLIKE priority queue)
+
+ breakout -- if the current score is worse than max_so_far - breakout, then don't go further
+ this is useful for doing integration since we can ignore the very tiny. This makes the search considerably more efficient, but may not result in correctness
+
+ NOTE / TODO: This has one inefficiency which is that if we find a new path to a state, we just push it on so there are
+ two copies on the stack. The second won't be visited again since it will be put in "visited" the first time it's seen
+ but we could remove it, and then the stack size would be more correct
+ """
+
+ ## Main code:
+ Q = PriorityQueue(N=N, max=True) # the priority queue
+ visited = set() # a set of states we've seen already
+ enumerative_search.maxsofar = float("-inf") # ugh a workaround since nested functions can't change this otherwise
+
+ # an function to score and potentially push a value
+ def score_and_push(k):
+ """
+ Score and push, returning the scored value
+ """
+ if k not in visited:
+ p = score(k)
+
+ if (enumerative_search.maxsofar - breakout > p): return None# if p is really bad, don't append
+
+ # Hmm this could go before or after the above breakout line -- for really big spaces,
+ # we should do this after so we don't cache a lot of nonsense; for really slow
+ # evaluation functions, this should go before so we don't repeat
+ visited.add(k)
+
+ if p != None: # if we return None, don't do it
+
+ # otherwise push onto queue
+ if p > enumerative_search.maxsofar: enumerative_search.maxsofar = p
+ Q.push(k, p)
+ return p
+
+ # add the start state
+ if not isinstance(start, list): start = list(start)
+ for k in start:
+ p = score_and_push(k)
+ if yield_immediate and (p is not None): yield k,p
+
+
+ ## Main loop:
+ while len(Q) > 0 and not SIG_INTERRUPTED: # while there is something in the queue
+
+ #print len(Q)
+ #print out the current priority queue
+ #i = 0
+ #for g in Q.Q:
+ #print " >", g.priority, g.value
+ #i += 1
+ #if i > 50: break
+ #print "\n\n"
+
+ x, s = Q.pop_both()
+ # if we are yeilding from the top of the queue
+ if not yield_immediate: yield x, s
+
+ for k in next_states(x):
+
+ p = score_and_push(k)
+
+ # if we are yeilding as we score
+ if yield_immediate and (p is not None): yield k,p
+
+### Testing:
+#from copy import copy
+
+#def possible_next_states(s):
+ #"""
+ #S is an array and we'll enumerate the changes coordinatewise
+ #"""
+ #for l in xrange(len(s)):
+ #for v in xrange(10):
+ #x = copy(s)
+ #x[l] = v
+ #yield x
+
+#def myscore(x):
+ #target = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 8, 9, 7, 9]
+ #s = 0.0
+ #for i in xrange(len(target)):
+ #s += abs(x[i] - target[i])
+ #return s
+
+#for v in enumerative_search( [0] * 15, possible_next_states, myscore, N=2):
+ #print v
140 LOTlib/Evaluation.py
@@ -0,0 +1,140 @@
+"""
+ Routines for evaluating MCMC runs
+
+ TODO:
+
+ Jan 12 2013 -- Hmm things aren't working so well yet... It appears that maybe it doesn't accurately detect when it's found something it already has??
+
+ -- Make Target a hash so we can actually put things in it!!
+"""
+
+from LOTlib.Miscellaneous import *
+from collections import defaultdict
+from math import log, exp
+from scipy.stats import chisquare
+import numpy
+
+def evaluate_sampler(target, sampler, skip=100, steps=100000, prefix="", trace=False, outfile=None):
+ """
+ target - a hash from hypotheses to lps. The keys of this are the only things we
+ sampler - a sampler we wish to evaluate_sampler
+
+ print a trace of stats:
+ - KL(sample || target) -- KL estimate
+ - KL(sample || target) -- seen hypothesis estimate
+ - percentage of hypotheses found
+ - Total probability mass found
+ """
+
+ hypotheses = target.keys()
+ tZ = logsumexp(target.values()) # get the normalizer
+
+ # keep track of samples
+ posterior_samples = defaultdict(int)
+
+ if outfile is not None: bo = ParallelBufferedIO(outfile)
+
+ n = 0
+ for s in sampler:
+
+ if s in target:
+ posterior_samples[s] += 1
+ #print ">>", s
+ if trace: print "#", s in target, s.lp, s
+
+ if (n%skip)==(skip-1):
+
+ sm = sum( [posterior_samples[x] for x in posterior_samples.keys() ] )
+
+ if sm == 0: print "# No valid samples yet for evaluate_sampler"
+ else:
+ sZ_lp = logsumexp( [x.lp for x in posterior_samples.keys()] )
+ sZ_cnt = log(sm)
+
+ KL_lp = 0.0
+ KL_cnt = 0.0
+ for h,cnt in posterior_samples.items():
+ Q = target[h] - tZ
+
+ P_lp = h.lp - sZ_lp # log prob estimate for h's probability
+ KL_lp += (P_lp - Q) * exp( P_lp )
+
+ P_cnt = log(cnt) - sZ_cnt
+ KL_cnt += (P_cnt - Q) * exp(P_cnt)
+
+ # And compute the percentage found
+ percent_found = float(len( set(posterior_samples.keys()) & set(hypotheses) )) / float(len(hypotheses))
+
+ # compute chi squared counts
+ fobs = numpy.array( [posterior_samples.get(h,0) for h in hypotheses] )
+ fexp = numpy.array( [ numpy.exp(h.lp-tZ) * len(hypotheses) for h in hypotheses])
+ chi,p = chisquare(fobs, f_exp=fexp)
+
+ if outfile is None:
+ print prefix, n, KL_lp, KL_cnt, percent_found, len(hypotheses), len(posterior_samples.keys()), tZ, sZ_lp, log(sZ_cnt), chi, p
+ else:
+ bo.write(prefix, n, KL_lp, KL_cnt, percent_found, len(hypotheses), len(posterior_samples.keys()), tZ, sZ_lp, log(sZ_cnt), chi, p)
+
+ n += 1
+ if n > steps: break
+
+ if outfile is not None: bo.close()
+ return
+
+
+
+
+
+
+"""
+ ########################################################################################################################
+ ## Evaluation of different samplers
+ ########################################################################################################################
+"""
+
+#def compute_sampler_performance(sampler, target, nsamples=xrange(0,1000,100), maxcalls=float("+inf"), pre="", TRACE=False):
+ #"""
+ #Sampler here is a generator; Target is a PriorityQueue
+
+ #"""
+
+ #N = len(target) # the target distribution
+ #targethyps = target.get_sorted(decreasing=True)
+ #targetset = set(targethyps)
+ #Z = logsumexp([ x.lp for x in targethyps ])
+ ##print targethyps[0:25]
+
+ #mysamples = FiniteBestSet(max=True, N=N)
+
+
+ ## reset this
+ #LOTlib.Hypothesis.POSTERIOR_CALL_COUNTER = 0
+
+ #cnt = 0
+ #total_time = 0.0
+ #tick = time.time()
+ #for s in sampler:
+ ##if TRACE: print s
+ #total_time += (time.time() - tick)
+
+ #if cnt > max(nsamples) or LOTlib.Hypothesis.POSTERIOR_CALL_COUNTER > maxcalls:
+ #break # *slightly* inefficient
+
+ #if cnt in nsamples: # *slightly* inefficient
+
+ ## print out at these amounts
+ #ms = mysamples.get_sorted(decreasing=True)
+ #myZ = logsumexp([ x.lp for x in ms ])
+ #myS = set(ms)
+ #topN = 0 # how many in order did we get right?
+ #for x in targethyps:
+ #if x in myS: topN += 1
+ #else: break
+
+ #print pre, cnt, total_time, LOTlib.Hypothesis.POSTERIOR_CALL_COUNTER, myZ, Z, len(myS & targetset), topN, N, q(ms[0])
+
+ #mysamples.push(s, s.lp)
+ #cnt += 1
+ #tick = time.time() # update this - we only want to measure the time spent generating
+
+
9 LOTlib/Examples/BoundVariables/README
@@ -0,0 +1,9 @@
+
+
+These contain examples for doing inference over lambda expressions, with bound variables.
+
+Please note: These have not been well-debugged yet.
+
+FOL, SchemeGenerate just generate from a PCFG.
+
+SchemeFunctionLearn learns a simple list function
BIN  LOTlib/Examples/SymbolicRegression/fig2.gif
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
121 LOTlib/FiniteBestSet.py
@@ -0,0 +1,121 @@
+# -*- coding: utf-8 -*-
+
+"""
+
+ This is a version of what was called "PriorityQueue.py" in LOTlib.VERSION < 0.3.
+"""
+import heapq
+import operator
+
+from LOTlib.Miscellaneous import *
+from collections import deque
+from copy import deepcopy
+
+class QueueItem:
+ """
+ A wrapper to hold items and scores in the queue--just wraps "cmp" on a priority value
+ """
+ def __init__(self, x, p):
+ self.x = x
+ self.priority = p
+
+ def __cmp__(self, y):
+ # Comparisons are based on priority
+ return cmp(self.priority, y.priority)
+
+ def __repr__(self): return repr(self.x)
+ def __str__(self): return str(self.x)
+
+
+class FiniteBestSet:
+ """
+ This class stores the top N (possibly infite) hypotheses it observes. It can also make the set of top hypotheses unique
+ It works by storing a priority queue (in the opposite order), and popping off the worst as we need to add more
+ """
+
+ def __init__(self, N=float("inf"), max=True, unique=True):
+ self.__dict__.update(locals())
+
+ self.max_multiplier = ifelse(self.max, 1, -1) # invert sign
+
+ self.Q = [] # we use heapq to
+ self.unique_set = set()
+
+
+ def __contains__(self, x):
+ return (x in self.Q)
+
+ def __iter__(self):
+ for x in self.get_all(): yield x
+
+ def __len__(self):
+ return len(self.Q)
+
+ def push(self, x, p):
+ """ Add x with value v to the set """
+
+ if self.unique and (x in self.unique_set):
+ return
+ else:
+ heapq.heappush(self.Q, QueueItem(x, self.max_multiplier*p))
+ if self.unique: self.unique_set.add(x) # add to the set
+
+ # if we have too many elements
+ if len(self) > self.N:
+ rr = heapq.heappop(self.Q)
+ if self.unique: self.unique_set.remove(rr.x) # clean out the removed from the set
+
+ def get_all(self, **kwargs):
+ """ Return all elements (arbitrary order). Does NOT return a copy. This uses kwargs so that we can call one 'sorted' """
+ if kwargs.get('sorted', False):
+ return [ c.x for c in sorted(self.Q, reverse = not kwargs.get('decreasing',False))]
+ else:
+ return [ c.x for c in self.Q]
+
+ ##NOTE: NOW DEFUNCT: USE .get_all
+ #def get_sorted(self, decreasing=False):
+ """ Return all elements in sorted order. Returns a *copy* via 'sorted' """
+ #return [ c.x for c in sorted(self.Q, reverse = not decreasing)]
+
+ def merge(self, y):
+ """
+ Copy over everything from y. Here, y may be a list of things to merge (e.g. other FiniteBestSets)
+ This is slightly inefficient becuase we create all new QueueItems, but it's easiest to deal with min/max
+ """
+ if isinstance(y, list):
+ for yi in y: self.merge(yi)
+ else:
+ for yi in y.Q:
+ self.push(yi.x, yi.priority*y.max_multiplier) # mult y by y.max_multiplier to convert it back to the original scale
+
+ def save(self, f):
+ # Just a wrapper for pickle that makes saving a little easier
+ out_file = open(f, 'wb')
+ pickle.dump(self, out_file)
+ out_file.close()
+
+if __name__ == "__main__":
+
+ import random
+
+ # Check the max
+ for i in xrange(100):
+ Q = FiniteBestSet(N=10)
+
+ ar = range(100)
+ random.shuffle(ar)
+ for x in ar: Q.push(x,x)
+
+ assert set(Q.get_all()).issuperset( set([90,91,92,93,94,95,96,97,98,99]))
+ print Q.get_sorted()
+
+ # check the min
+ for i in xrange(100):
+ Q = FiniteBestSet(N=10, max=False)
+
+ ar = range(100)
+ random.shuffle(ar)
+ for x in ar: Q.push(x,x)
+
+ assert set(Q.get_all()).issuperset( set([0,1,2,3,4,5,6,7,8,9]))
+ print Q.get_sorted()
215 LOTlib/FunctionNode.py
@@ -0,0 +1,215 @@
+# -*- coding: utf-8 -*-
+"""
+ A function node -- a tree part representing a function and its arguments.
+ Also used for PCFG rules, where the arguments are nonterminal symbols.
+
+ TODO: This could use some renaming FunctionNode.bv is not really a bound variable--its a list of rules that were added
+"""
+
+import re
+
+from numpy import *
+from copy import deepcopy
+from LOTlib.Miscellaneous import *
+
+def list2FunctionNode(l, style="atis"):
+ """
+ Take a list and map it to a function node.
+ This will take lambda arguments of some given style (e.g. atis, scheme, etc)
+ """
+
+ if isinstance(l, list):
+ if len(l) == 0: return None
+ elif style is 'atis':
+ rec = lambda x: list2FunctionNode(x, style=style) # a wrapper to my recursive self
+ if l[0] == 'lambda':
+ return FunctionNode('FUNCTION', 'lambda', [rec(l[3])], lp=0.0, bv=[l[1]] ) ## TOOD: HMM WHAT IS THE BV?
+ else:
+ return FunctionNode(l[0], l[0], map(rec, l[1:]), lp=0.0, bv=[])
+ elif sytle is 'scheme':
+ pass #TODO: Add this scheme functionality -- basically differnet handling of lambda bound variables
+
+ else: # for non-list
+ return l
+
+
+class FunctionNode:
+ """
+ NOTE: If a node has [ None ] as args, it is treated as a thunk
+
+ bv - stores the actual *rule* that was added (so that we can re-add it when we loop through the tree)
+
+ My bv stores the particlar *names* of variables I've introduced
+ """
+
+ def __init__(self, returntype, name, args, lp=0.0, resample_p=1.0, bv=[], ruleid=None):
+ self.__dict__.update(locals())
+
+ # make all my parts the same as q (not copies)
+ def setto(self, q):
+ self.returntype = q.returntype
+ self.name = q.name
+ self.args = q.args
+ self.lp = q.lp
+ self.resample_p = q.resample_p
+ self.bv = q.bv
+ self.ruleid = q.ruleid
+
+ def copy(self):
+ """
+ A more efficient copy that mainly copies the nodes
+ """
+ newargs = [x.copy() if isinstance(x, FunctionNode) else deepcopy(x) for x in self.args]
+ return FunctionNode(self.returntype, self.name, newargs, self.lp, self.resample_p, self.bv, self.ruleid)
+
+ def is_nonfunction(self):
+ return (self.args is None) or (len(self.args)==0)
+ def is_function(self):
+ return not self.is_nonfunction()
+
+ def as_list(self):
+ """
+ Not a pretty print, just a list of all key feature
+ """
+ return [self.returntype, self.name, self.args, self.lp, self.bv, self.ruleid]
+
+ # output a string that can be evaluated by python
+ ## NOTE: Here we do a little fanciness -- with "if" -- we convert it to the "correct" python form with short circuiting instead of our fancy ifelse function
+ def pystring(self):
+ #print ">>", self.name
+ if self.is_nonfunction(): # a terminal
+ return str(self.name)
+ elif self.name == "if_": # this gets translated
+ return '(' + str(self.args[1]) + ') if (' + str(self.args[0]) + ') else (' + str(self.args[2]) + ')'
+ elif self.name == '':
+ return str(self.args[0])
+ elif self.name is not None and self.name.lower() == 'lambda':
+ #print len(self.bv)
+ #print "HERE !!"
+ return '(lambda '+commalist( [ str(x.name) for x in self.bv])+': '+str(self.args[0])+' )'
+ else:
+ if len(self.args) == 1 and self.args[0] is None: # handle weird case with None as single terminal below
+ return str(self.name)+'()'
+ else:
+ return str(self.name)+'('+' '+commalist( [ str(x) for x in self.args])+' )'
+
+ def fullprint(self, d=0):
+ """ A handy printer for debugging"""
+ tabstr = " . " * d
+ print tabstr, self.returntype, self.name, self.bv, "\t", self.lp #"\t", self.resample_p
+ for a in self.args:
+ if isinstance(a, FunctionNode): a.fullprint(d+1)
+ else: print tabstr, a
+
+ #def schemestring(self):
+ #if self.args == []: # a terminal
+ #return str(self.name)
+ #else: return '('+self.name + ' '+commalist( [ str(x) for x in self.args], sep1=' ', sep2=' ')+' )'
+
+
+
+ # NOTE: in the future we may want to change this to do fancy things
+ def __str__(self): return self.pystring()
+ def __repr__(self): return self.pystring()
+
+ def __eq__(self, other): return (cmp(self, other) == 0)
+ def __ne__(self, other): return not self.__eq__(other)
+
+ ## TODO: overwrite these with something faster
+ # hash trees. This just converts to string -- maybe too slow?
+ def __hash__(self): return hash(str(self))
+ def __cmp__(self, x): return cmp(str(self), str(x))
+
+ def log_probability(self):
+ """
+ Returns the log probability of this node. This is computed by the log probability of each argument,
+ UNLESS "my_log_probability" is defined, and then it returns that
+ """
+ if hasattr(self, 'my_log_probability'):
+ #print ">!!>>", t, self.my_log_probability
+ return self.my_log_probability
+ else:
+ lp = self.lp # the probability of my rule
+ if self.args is None: return lp
+ for i in range(len(self.args)):
+ if isinstance(self.args[i], FunctionNode):
+ #print "\t<", self.args[i], self.args[i].log_probability(), ">\n"
+ lp = lp + self.args[i].log_probability() # plus all children
+ return lp
+
+ # use generator to enumerate all subnodes
+ # NOTE: To do anything fancy, we should use PCFG.iterate_subnodes in order to update the grammar, resample, etc.
+ def all_subnodes(self, no_self=False):
+
+ if not no_self: yield self; # I am a subnode of myself
+
+ for i in range(len(self.args)): # loop through kids
+ if isinstance(self.args[i],FunctionNode):
+ for ssn in self.args[i].all_subnodes():
+ yield ssn
+
+ def all_leaves(self):
+ for i in range(len(self.args)): # loop through kids
+ if isinstance(self.args[i],FunctionNode):
+ for ssn in self.args[i].all_leaves():
+ yield ssn
+ else:
+ yield self.args[i]
+
+ def string_below(self, sep=" "):
+ return sep.join(map(str, self.all_leaves()))
+
+
+ # resample myself from some grammar
+ def resample(self, g, d=0):
+ """
+ Resample this node. d (depth) is included in case we are generating bound variables, and then we need to label them by total tree depth
+ """
+ if g.is_nonterminal(self.returntype):
+ self.setto(g.generate(self.returntype, d=d))
+ else: pass # do nothing if we aren't returnable from the grammar
+
+ ############################################################
+ ## Derived functions that build on the above core
+ ############################################################
+
+ def contains_function(self, x):
+ """
+ Check if this contains x as function below
+ """
+ for n in self.all_subnodes():
+ if n.name == x: return True
+ return False
+
+ def count_nodes(self): return self.count_subnodes()
+ def count_subnodes(self):
+ c = 0
+ for n in self.all_subnodes():
+ c = c + 1
+ return c
+
+ def depth(self):
+ depths = [0.0] # if no function nodes
+ for i in range(len(self.args)):
+ if isinstance(self.args[i],FunctionNode):
+ depths.append( self.args[i].depth()+1 )
+ return max(depths)
+
+ # get a description of the input and output types
+ # if collapse_terminal then we just map non-FunctionNodes to "TERMINAL"
+ def get_type_signature(self):
+ ts = [self.returntype, self.bv]
+ for i in range(len(self.args)):
+ if isinstance(self.args[i],FunctionNode):
+ ts.append(self.args[i].returntype)
+ else:
+ ts.append(self.args[i])
+ return ts
+
+ def is_replicating(self):
+ """
+ A function node is replicating (by definition) if one of its children is of the same type
+ """
+ return any([isinstance(x,FunctionNode) and x.returntype == self.returntype for x in self.args])
+
+
48 LOTlib/GrammarRule.py
@@ -0,0 +1,48 @@
+
+"""
+ This class is a wrapper for representing "rules" in the grammar.
+"""
+
+from math import log
+from LOTlib.Miscellaneous import assert_or_die
+
+class GrammarRule:
+ def __init__(self, nt, name, to, rid, p=1.0, resample_p=1.0, bv=[]):
+ """
+ nt - the nonterminal
+ name - the name of this function
+ to - what you expand to (usually a FunctionNode).
+ rid - the rule id number
+ p - unnormalized probability of expansion
+ resample_p - in resampling, what is the probability of choosing this node?
+ bv - what bound variables were introduced?
+
+ A rule where "to" is a nonempty list is a real expansion:
+ GrammarRule( "EXPR", "plus", ["EXPR", "EXPR"], ...) -> plus(EXPR,EXPR)
+ A rule where "to" is [None] is a thunk
+ GrammarRule( "EXPR", "plus", [None], ...) -> plus()
+ A rule where "to" is [] is a real terminal (non-thunk)
+ GrammarRule( "EXPR", "five", [], ...) -> five
+ A rule where "name" is '' expands without parens:
+ GrammarRule( "EXPR", '', "SUBEXPR", ...) -> EXPR->SUBEXPR
+
+ NOTE: The rid is very important -- it's what we use to determine equality
+ """
+ self.__dict__.update(locals())
+ self.lp = log(p)
+
+ if name == '': assert_or_die( len(to) == 1, "GrammarRules with empty names must have only 1 argument")
+
+
+ def __repr__(self):
+ return str(self.nt) + " -> " + self.name + str(self.to) + " [p=" +str(self.p)+ "; resample_p=" + str(self.resample_p) +"]"
+
+ def __eq__(self, other): return ( self.rid == other.rid and self.nt == other.nt)
+ def __ne__(self, other): return not self.__eq__(other)
+
+class FunctionRule(GrammarRule):
+ """ Just a subtype for when we want to pass distributions to values"""
+ def __init__(self, nt, function, rid, p=1.0, resample_p=1.0):
+ self.__dict__.update(locals())
+ self.lp = log(p)
+
48 LOTlib/HashableArray.py
@@ -0,0 +1,48 @@
+# -*- coding: utf-8 -*-
+import numpy as np
+
+class HashableArray():
+ """
+ A class for image hypotheses that initializes to square zeros
+ and has a much faster hashing function. This basically just
+ wraps a numpy array
+ """
+
+ def __init__(self, ar=None, shape=(5,5), shallow=True, hsh=None):
+ """
+ If shallow, we copy the array (e.g. if it is going to be modified)
+
+ TODO: If hsh==None, then we don't return a strung, we just return hsh. This way, we can construct these
+ really efficiently, giving a finite set their own hsh values. So hsh is an identifier that really speeds up hashing
+ """
+ if ar != None:
+ if shallow: self.ar = ar
+ else: self.ar = deepcopy(ar)
+ else: self.ar = np.zeros( shape, dtype=int )
+
+ # make this accessible to everyone
+ self.shape = self.ar.shape
+
+
+ def __getitem__(self, *args):
+ return self.ar.__getitem__(*args)
+ def __setitem__(self, *args):
+ return self.ar.__setitem__(*args)
+
+ ## TODO: Optimize this--a lot depends on how well we hash these
+ def __hash__(self):
+ #return self.ar.__repr__().__hash__()
+ #return self.ar__str__().__hash__()
+ #return hashlib.sha1(self.ar).hexdigest().__hash__()
+ #if self.hsh == None:
+ return self.ar.dumps().__hash__()
+ #else return self.hsh
+
+ def __eq__(self, y):
+ #if hsh == None:
+ return np.equal(self.ar, y.ar).all()
+ #else:
+ #return self.hsh == y.hsh
+
+ def __str__(self):
+ return self.ar.__str__()
344 LOTlib/Hypothesis.py
@@ -0,0 +1,344 @@
+# -*- coding: utf-8 -*-
+
+"""
+ The main class for samping. This computes log probabilities and proposals
+
+
+ NOTE: In implementation, self.prior, self.likelihood and self.lp must be set whenever prior, likelihood are computed
+
+
+ TODO:
+ - Probably should collapse FunctionHypothesis and STandardHypothesis toegether,since having a FunctionHypothesis is almost always a bad idea (no prior)
+ - type(self).__new__(...args...) -- should be able to use this in the copy constructor, instead of having to compulsively define .copy()
+ - Redo these with a "params" dictionary for each hypothesis, which stores things like decay, alpha, etc.
+ And then you can just copy this over with the copy constructor
+"""
+from LOTlib.Miscellaneous import *
+from LOTlib.BasicPrimitives import * # needed to eval __call__ here, since that's where they are bound
+from LOTlib.DataAndObjects import FunctionData,UtteranceData
+from copy import copy
+import numpy
+
+POSTERIOR_CALL_COUNTER = 0
+
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class Hypothesis:
+ """
+ A hypothesis is...
+
+ - optionally, compute_likelihood stores self.stored_likelihood, giving the undecayed likelihood on each data point
+ """
+
+ def __init__(self, v=None):
+ self.set_value(v) # to zero out prior, likelhood, lp
+ self.prior, self.likelihood, self.lp = [-Infinity, -Infinity, -Infinity] # this should live here in case we overwrite self_value
+ self.stored_likelihood = None
+
+ def set_value(self, v):
+ """ Sets the (self.)value of this hypothesis to v"""
+ self.value = v
+
+ def copy(self):
+ """ Returns a copy of myself by calling copy() on self.value """
+ return Hypothesis(v=self.value.copy())
+
+ # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+ # All instances of this must implement these:
+
+ def likelihood_decay_function(self, i, N, decay):
+ """
+ The weight of the likelihood for the ith point out of N with the given decay parameter.
+ Generally, this should be a power law decay
+ i - What data point (0-indexed)
+ N - how many total data points
+ """
+ return (N-i+1)**(-decay)
+
+ def compute_prior(self):
+ """ computes the prior and stores it in self.prior"""
+ print "*** Must implement compute_prior"
+ assert False # Must implement this
+
+
+ def compute_likelihood(self, d, decay=0.0):
+ """
+ Compute the likelihood of the *array* d, with the specified likelihood decay
+ This also stores the *undecayed* non-culmulative likelihood of each data point in self.stored_likelihood
+ """
+ print "*** Must implement compute_likelihood"
+ assert False # Must implement this
+
+ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ # Methods for accessing likelihoods etc. on a big arrays of data
+
+ def get_culmulative_likelihoods(self, ll_decay=0.0, shift_right=True):
+ """
+ Compute the culmulative likelihoods on the stored data
+ This gives the likelihood on the first data point, the first two, first three, etc, appropriately decayed
+ using the 'pointwise' likelihoods stored in self.stored_likelihood.
+ NOTE: This is O(N^2) (for power law decays; for exponential it could be linear)
+ returns: a numpy array of the likelihoods
+
+ - decay - the memory decay
+ - shift_right -- do we insert a "0" at the beginning (corresponding to inferences with 0 data), and then delete one from the end?
+ - So if you do posterior predictives, you want shift_right=True
+ """
+ assert self.stored_likelihood is not None
+
+ offset = 0
+ if shift_right: offset = 1
+
+ out = []
+ for n in xrange(1-offset,len(self.stored_likelihood)+1-offset):
+ if ll_decay==0.0: # shortcut if no decay
+ sm = numpy.sum(self.stored_likelihood[0:n])
+ else:
+ sm = numpy.sum( [self.stored_likelihood[j] * self.likelihood_decay_function(j, n, ll_decay) for j in xrange(n) ])
+ out.append(sm)
+ return numpy.array(out)
+
+ def get_culmulative_posteriors(self, ll_decay=0.0, shift_right=False):
+ """
+ returns the posterior with the i'th stored CULMULATIVE likelihood, using the assumed decay
+ """
+ return self.get_culmulative_likelihoods(ll_decay=ll_decay, shift_right=shift_right) + self.prior
+
+ def propose(self):
+ """ Generic proposal used by MCMC methods"""
+ print "*** Must implement propose"
+ assert False # Must implement this
+
+ # this updates last_prior and last_likelihood
+ def compute_posterior(self, d):
+ global POSTERIOR_CALL_COUNTER
+ POSTERIOR_CALL_COUNTER += 1
+ p = self.compute_prior()
+ l = self.compute_likelihood(d)
+ return [p,l]
+
+ # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+ # optional implementation
+ # if you do gibbs sampling you need:
+ def enumerative_proposer(self): pass
+
+ # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+ ## These are just handy:
+ def __str__(self):
+ return str(self.value)
+ def __repr__(self): return str(self)
+
+ # for hashing hypotheses
+ def __hash__(self): return hash(self.value)
+ def __cmp__(self, x): return cmp(self.value,x)
+
+ # this is for heapq algorithm in FiniteSample, which uses <= instead of cmp
+ # since python implements a "min heap" we can compar elog probs
+ def __le__(self, x): return (self.lp <= x.lp)
+ def __eq__(self, other):
+ return (self.value.__eq__(other.value))
+ def __ne__(self, other): return (self.value.__ne__(other.value))
+
+
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class FunctionHypothesis(Hypothesis):
+ """
+ A special type of hypothesis whose value is a function.
+ The function is automatically eval-ed when we set_value, and is automatically hidden and unhidden when we pickle
+ This can also be called like a function, as in fh(data)!
+ """
+
+ def __init__(self, v=None, f=None, args=['x']):
+ """
+ v - the value of this hypothesis
+ f - defaultly None, in which case this uses self.value2function
+ args - the argumetns to the function
+ """
+ self.args = args # must come first since below calls value2function
+ Hypothesis.__init__(self,v) # this initializes prior and likleihood variables, so keep it here!
+ self.set_value(v,f)
+
+ def copy(self):
+ """ Create a copy, only deeply of of v """
+ return FunctionHypothesis(v=self.value.copy(), f=self.fvalue, args=self.args)
+
+ def __call__(self, *vals):
+ """ Make this callable just like a function. Yay python! """
+ return self.fvalue(*vals)
+
+ def value2function(self, v):
+ #print "ARGS=", self.args
+ """ How we convert a value into a function. Default is LOTlib.Miscellaneous.evaluate_expression """
+ return evaluate_expression(v, args=self.args)
+
+ def reset_function(self):
+ """ re-construct the function from the value -- useful after pickling """
+ self.set_value(self.value)
+
+
+ def set_value(self, v, f=None):
+ """
+ The the value. You optionally can send f, and not write (this is for some speed considerations) but you better be sure f is correct
+ since an error will not be caught!
+ """
+
+ Hypothesis.set_value(self,v)
+ if f is not None: self.fvalue = f
+ elif v is None: self.fvalue = None
+ else: self.fvalue = self.value2function(v)
+
+ # ~~~~~~~~~
+ # Evaluate this function on some data
+ def get_function_responses(self, data):
+ """
+ Returns a list of my responses to data, handling exceptions (setting to None)
+ """
+ out = []
+ for di in data:
+ #print ">>", di, di.__class__.__name__, type(di), isinstance(di, FunctionData)
+ r = None
+ try:
+ if isinstance(di, FunctionData): r = self(*di.args)
+ elif isinstance(di, UtteranceData): r = self(*di.context)
+ else: r = self(*di) # otherwise just pass along
+ except RecursionDepthException: pass # If there is a recursion depth exception, just ignore (so r=None)
+
+ out.append(r) # so we get "None" when things mess up
+ return out
+
+ # ~~~~~~~~~
+ # Make this thing pickleable
+ def __getstate__(self):
+ """ We copy the current dict so that when we pickle, we destroy the function"""
+ dd = copy(self.__dict__)
+ dd['fvalue'] = None # clear the function out
+ return dd
+ def __setstate__(self, state):
+ self.__dict__.update(state)
+ self.set_value(self.value) # just re-set the value so that we re-compute the function
+
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+class StandardExpression(FunctionHypothesis):
+ """
+ A FunctionHypothesis built from a grammar.
+ Implement a Rational Rules (Goodman et al 2008)-style grammar over Boolean expressions.
+
+ """
+
+ def __init__(self, G, v=None, f=None, start='START', ALPHA=0.9, rrPrior=False, maxnodes=25, ll_decay=0.0, prior_temperature=1.0, args=['x']):
+ """
+ G - a grammar
+ start - how the grammar starts to generate
+ rrPrior - whether we use RR prior or log probability
+ f - if specified, we don't recompile the whole function
+ """
+
+ # save all of our keywords (though we don't need v)
+ self.__dict__.update(locals())
+ if v is None: v = G.generate(self.start)
+
+ FunctionHypothesis.__init__(self, v=v, f=f, args=args)
+
+ self.likelihood = 0.0
+
+ def copy(self):
+ """
+ Return a copy -- must copy all the other values too (alpha, rrPrior, etc)
+ """
+ return StandardExpression(self.G, v=self.value.copy(), start=self.start, ALPHA=self.ALPHA, rrPrior=self.rrPrior, maxnodes=self.maxnodes, args=self.args, ll_decay=self.ll_decay)
+
+ def propose(self):
+ p = self.copy()
+ ph, fb = self.G.propose(self.value)
+ p.set_value(ph)
+ return p, fb
+
+ def compute_prior(self):
+ """
+
+ """
+ if self.value.count_subnodes() > self.maxnodes:
+ self.prior = -Infinity
+ else:
+ # compute the prior with either RR or not.
+ if self.rrPrior: self.prior = self.G.RR_prior(self.value) / self.prior_temperature
+ else: self.prior = self.value.log_probability() / self.prior_temperature
+
+ self.lp = self.prior + self.likelihood
+
+ return self.prior
+
+ def compute_likelihood(self, data):
+ """
+ Computes the likelihood of data.
+ The data here is from LOTlib.Data and is of the type FunctionData
+ """
+
+ # set up to store this data
+ self.stored_likelihood = [None] * len(data)
+
+ # compute responses to all data
+ responses = self.get_function_responses(data) # get my response to each object
+
+ N = len(data)
+ self.likelihood = 0.0
+ for i in xrange(N):
+ r = responses[i]
+ di = data[i]
+
+ # the pointsiwe undecayed likelihood for this data point
+ self.stored_likelihood[i] = log( self.ALPHA*(r==di.output) + (1.0-self.ALPHA)/2.0 )
+ #print self.stored_likelihood[i], di, r, di.output, "\n\n"
+
+ # the total culmulative decayed likeliood
+ self.likelihood += self.stored_likelihood[i] * self.likelihood_decay_function(i, N, self.ll_decay)
+
+ self.lp = self.prior + self.likelihood
+
+ return self.likelihood
+
+ # must wrap these as SimpleExpressionFunctions
+ def enumerative_proposer(self):
+ for k in G.enumerate_pointwise(self.value):
+ yield StandardExpression(v=k)
+
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class GaussianStandardExpression(StandardExpression):
+ """
+ Like StandardExpression but has a Gaussian likelihood
+ """
+
+ def __init__(self, G, ll_sd=1.0, prior_temperature=1.0, ll_decay=0.0):
+ """ kwargs should include ll_sd """
+ StandardExpression.__init__(self, G)
+ self.__dict__.update(locals())
+
+ def copy(self):
+ """ Return a copy -- must copy all the other values too (alpha, rrPrior, etc) """
+ return GaussianStandardExpression(G=self.G, ll_sd=self.ll_sd, prior_temperature=self.prior_temperature, ll_decay=self.ll_decay)
+
+
+ def compute_likelihood(self, data):
+ """ Compute the likelihood with a Gaussian"""
+
+ # set up to store this data
+ self.stored_likelihood = [None] * len(data)
+
+ # compute responses to all data
+ responses = self.get_function_responses(data) # get my response to each object
+
+ N = len(data)
+ self.likelihood = 0.0
+ for i in xrange(N):
+ self.stored_likelihood[i] = normlogpdf(responses[i], data[i].output, self.ll_sd)
+
+ # the total culmulative decayed likeliood
+ self.likelihood += self.stored_likelihood[i] * self.likelihood_decay_function(i, N, self.ll_decay)
+
+ self.lp = self.prior + self.likelihood
+
+ return self.likelihood
276 LOTlib/Lexicon.py
@@ -0,0 +1,276 @@
+# -*- coding: utf-8 -*-
+
+"""
+
+ TODO:
+ - Make the lexicon be indexable like an array/dict, rather than having to say h.lex[...] say h[..]
+
+
+"""
+
+from random import sample
+from copy import deepcopy
+
+from LOTlib.Hypothesis import Hypothesis, StandardExpression
+from LOTlib.DataAndObjects import UtteranceData
+from LOTlib.Miscellaneous import *
+
+class SimpleLexicon(Hypothesis):
+ """
+ A class for representing learning single words
+ This stores a dictionary mapping words to FunctionHypotheseses
+ A SimpleLexicon itself is a hypothesis, allowing joint inferences over the whole set of meanings
+
+ You may overwrite the weightfunction, which maps *[h f] to a positive number corresponding to the probability of producing a word h, f
+ It defaults to lambda x: 1.0
+
+ TODO: we can probably make this faster by not passing around the context sets so much.
+
+ """
+
+ def __init__(self, G, args, alpha=0.90, palpha=0.90):
+ Hypothesis.__init__(self)
+ self.lex = dict()
+ self.grammar = G
+ self.args = args
+ self.alpha = alpha
+ self.palpha = palpha
+
+ def copy(self):
+ """ Copy a lexicon. We don't re-create the fucntions since that's unnecessary and slow"""
+ new = SimpleLexicon(self.grammar, self.args, alpha=self.alpha, palpha=self.palpha)
+ for w in self.lex.keys():
+ new.lex[w] = self.lex[w].copy()
+ return new
+
+ def __str__(self):
+ out = ''
+ for w, v in self.lex.iteritems():
+ out = out + str(w) + ':\t' + str(v) + '\n'
+ return out
+ def __hash__(self): return hash(str(self))
+ def __eq__(self, other): return (str(self)==str(other)) # simple but there are probably better ways
+
+ # this sets the word and automatically compute its function
+ def set_word(self, w, v, f=None):
+ """
+ This sets word w to value v. v can be either a FunctionNode or a Hypothesis, and
+ in either case it is copied here. When it is a Hypothesis, the value is exrtacted
+ """
+
+ # Conver to standard expressiosn
+ if isinstance(v, Hypothesis): v = v.value # extract the value (hopefully a FunctionNode)
+ v = v.copy() # and copy it
+
+ assert isinstance(v, FunctionNode)
+
+ # set this equal to the StandardExpression
+ if self.lex.get(w,None) is None:
+ self.lex[w] = StandardExpression(self.grammar, v=v, f=f, args=self.args) # create this
+ else: self.lex[w].set_value(v=v, f=f)
+
+ # set this function (to make something to sample from)
+ def force_function(self, w, f):
+ self.lex[w] = StandardExpression(self.grammar, v=None, f=f) # this sloppily generates v=None, but it's easy for now
+ self.lex[w].value = w # Now overwrite so .value is just the word
+
+ def all_words(self): return self.lex.keys()
+
+ def weightfunction(self, h): return 1.0
+ def weightfunction_word(self, w):
+ return self.weightfunction(self.lex[w])
+
+
+ ###################################################################################
+ ## MH stuff
+ ###################################################################################
+
+ def propose(self):
+ new = self.copy()
+ w = weighted_sample(self.lex.keys()) # the word to change
+ p,fb = self.grammar.propose( self.lex[w].value )
+
+ new.set_word(w, p)
+
+ return new, fb
+
+
+ def compute_prior(self):
+ self.prior = sum([x.compute_prior() for x in self.lex.values()])
+ self.lp = self.prior + self.likelihood
+ return self.prior
+
+ # This combines score_utterance with likelihood so that everything is much faster
+ def compute_likelihood(self, data):
+ self.likelihood = 0.0
+
+ # pre-compute the weights for each word
+ weights = dict()
+ for w in self.lex.keys():
+ weights[w] = self.weightfunction(self.lex[w])
+
+ # set up the stored likelihood
+ N = len(data)
+ self.stored_likelihood = [None]*N
+
+ self.likelihood = 0.0
+ for i in xrange(N):
+
+ di = data[i]
+
+ # evaluate the word in the context
+ f = self.lex[di.word](*di.context)
+
+ # partition all the other utterances
+ t,m = self.partition_words(di.all_words, di.context)
+
+ ## This is the slow part! You should score a bunch of utterances at once!
+ all_weights = sum(map( lambda x: weights[x], di.all_words))
+ true_weights = sum(map( lambda x: weights[x], t))
+ met_weights = sum(map( lambda x: weights[x], m))
+
+ p = 0.0
+ w = weights[di.word] # the weight of the one we have
+ if f == True: p = self.palpha * self.alpha * w / true_weights + self.palpha * (1.0 - self.alpha) * w / met_weights + (1.0 - self.palpha) * w / all_weights # choose from the trues
+ elif f == False: p = ifelse(true_weights==0, 1.0, 1.0-self.alpha) * self.palpha * w / met_weights + (1.0 - self.palpha) * w / all_weights # choose from the trues
+ else: p = ifelse(met_weights==0, 1.0, (1.0 - self.palpha)) * w / all_weights
+
+ self.stored_likelihood[i] = log(p)
+
+ self.likelihood += self.stored_likelihood[i]
+
+ self.lp = self.prior + self.likelihood
+ return self.likelihood
+
+
+ ###################################################################################
+ ## Sampling and dealing with responses
+ ###################################################################################
+
+ # Take a set of utterances and partition them into true/met
+ def partition_words(self, all_words, context):
+ """
+ Partition utterances, returning those that are true, and those with the presup met
+ NOTE: The commended code is much slower
+ """
+
+ trues, mets = [], []
+ for w in all_words:
+ fa = self.lex[w](*context)
+ if fa is True:
+ trues.append(w)
+ mets.append(w)
+ elif fa is False:
+ mets.append(w)
+ else: pass # not met
+
+ return [trues, mets]
+
+ # take a set of utterances and sample them according to our probability model
+ def sample_word(self, all_words, context):
+
+ t,m = self.partition_words(all_words, context)