diff --git a/LICENSE b/LICENSE
index 012b6a34..f288702d 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1 +1,674 @@
-PonyGE2 is hereby licensed for use under the GPL version 3.
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ 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.
+
+
+ Copyright (C)
+
+ 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 .
+
+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:
+
+ Copyright (C)
+ 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
+.
+
+ 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
+.
diff --git a/README.md b/README.md
index 1b963b7a..6609eb0d 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,4 @@
# Introduction
---------------
Grammatical Evolution (GE) is a population-based evolutionary algorithm, where a BNF-style grammar is used in the genotype to phenotype mapping process [O'Neill & Ryan, 2003].
@@ -7,32 +6,23 @@ PonyGE2 is an implementation of GE in Python. It's intended as an advertisement
The original version of PonyGE (https://github.com/jmmcd/ponyge) was originally designed to be a small single-file implementation of GE. However, over time this has grown to a stage where a more formal structured approach was needed. This has led to the development of PonyGE2 (https://github.com/PonyGE/PonyGE2), presented here.
-A technical paper describing PonyGE2 has been published and been made freely available on arXiv at:
-
-https://arxiv.org/abs/1703.08535
+A technical paper describing PonyGE2 has been published and been made freely available on arXiv [here](https://arxiv.org/abs/1703.08535).
PonyGE2 can be referenced using the following citation:
- Fenton, M., McDermott, J., Fagan, D., Forstenlechner, S., Hemberg, E., and O'Neill, M. PonyGE2: Grammatical Evolution in Python. arXiv preprint, arXiv:1703.08535, 2017.
-
-The PonyGE2 development team can be contacted via [GitHub](https://github.com/jmmcd/PonyGE2/issues/new).
-
-PonyGE2 is copyright (C) 2009-2017
-
+> Fenton, M., McDermott, J., Fagan, D., Forstenlechner, S., Hemberg, E., and O'Neill, M. PonyGE2: Grammatical Evolution in Python. arXiv preprint, arXiv:1703.08535, 2017.
# Requirements
---------------
-PonyGE2 requires Python 3.5 or higher.
-Using matplotlib, numpy, scipy, scikit-learn (sklearn), pandas.
+We don't provide any `setup.py` script for now, so you cannot yet pip-install PonyGE2. PonyGE2 requires Python 3.5 (or higher) and it uses matplotlib, numpy, scipy, scikit-learn (sklearn), pandas, which can be installed as follows
-All requirements can be satisfied with [Anaconda](https://www.continuum.io/downloads).
+ pip install -r requirements.txt
+All requirements can be satisfied with [Anaconda](https://www.continuum.io/downloads).
# Running PonyGE2
------------------
-We don't provide any setup script. You can run an example problem (the default is regression, see below) just by typing:
+You can run an example problem (the default is regression, see below) just by typing:
$ cd src
$ python ponyge.py
@@ -50,20 +40,25 @@ There are a number of arguments that can be used for passing values via the comm
# About PonyGE2
----------------
Grammatical Evolution (GE) [O'Neill & Ryan, 2003] is a grammar-based form of Genetic Programming [Koza, 1992]. It marries principles from molecular biology to the representational power of formal grammars. GE’s rich modularity gives a unique flexibility, making it possible to use alternative search strategies, whether evolutionary, deterministic or some other approach, and to radically change its behaviour by merely changing the grammar supplied. As a grammar is used to describe the structures that are generated by GE, it is trivial to modify the output structures by simply editing the plain text grammar. This is one of the main advantages that makes the GE approach so attractive. The genotype-phenotype mapping also means that instead of operating exclusively on solution trees, as in standard GP, GE allows search operators to be performed on the genotype (e.g., integer or binary chromosomes), in addition to partially derived phenotypes, and the fully formed phenotypic derivation trees themselves.
PonyGE2 is primarily a Python implementation of canonical Grammatical Evolution, but it also includes a number of other popular techniques and EC aspects.
-# For full documentation of PonyGE2, see the [wiki](https://github.com/PonyGE/PonyGE2/wiki).
----------------------------------------------------------------------------
+# Documentation
+
+For full documentation of PonyGE2, see the [wiki](https://github.com/PonyGE/PonyGE2/wiki).
+
+# Contact
+
+The PonyGE2 development team can be contacted via [GitHub](https://github.com/jmmcd/PonyGE2/issues/new).
+
+# License
-https://github.com/PonyGE/PonyGE2/wiki
+PonyGE2 is hereby licensed for use under the GNU General Public License v3.0. See the file [LICENSE](./LICENSE) for more details. PonyGE2 is copyright (C) 2009-2017.
# References
-------------
-Michael O'Neill and Conor Ryan, "Grammatical Evolution: Evolutionary Automatic Programming in an Arbitrary Language", Kluwer Academic Publishers, 2003.
+- Michael O'Neill and Conor Ryan, "Grammatical Evolution: Evolutionary Automatic Programming in an Arbitrary Language", Kluwer Academic Publishers, 2003.
-Koza, J.R., 1992. "Genetic programming: on the programming of computers by means of natural selection (Vol. 1)". MIT press.
+- Koza, J.R., 1992. "Genetic programming: on the programming of computers by means of natural selection (Vol. 1)". MIT press.
diff --git a/src/agent/agent.py b/src/agent/agent.py
index 87167b18..dd91d814 100644
--- a/src/agent/agent.py
+++ b/src/agent/agent.py
@@ -1,12 +1,13 @@
-from operators.initialisation import initialisation
from fitness.evaluation import evaluate_fitness
-from stats.stats import stats, get_stats
from operators.crossover import crossover
+from operators.initialisation import initialisation
from operators.mutation import mutation
-from operators.replacement import replacement, steady_state
+from operators.replacement import replacement
from operators.selection import selection
+from stats.stats import get_stats
-class Agent():
+
+class Agent:
"""
Class representing individual robots/agents. The agents has three main methods.
Sense - method responsible for getting information from the environment from different sensors
@@ -19,7 +20,7 @@ def __init__(self, ip):
self.interaction_probability = ip
# Only initialize single individual. Single agent can only have single genetic information
- self.individual = initialisation(1)
+ self.individual = initialisation(1)
# Evaluate the fitness for the the individual
self.individual = evaluate_fitness(self.individual)
@@ -27,7 +28,6 @@ def __init__(self, ip):
# Flag which store the boolean value for other neighbouring agents found or not
self.agents_found = False
-
def sense(self, agents):
# This part makes this GE algorithm useful for multi-agent systems. This method is responsible to sense
# information from the environment
@@ -40,25 +40,26 @@ def sense(self, agents):
# the agent has found some nearby agents. Higher the probability, better the chance of agent to share its
# gnome with other agents
if random.random() > self.interaction_probability:
- #Turn the flag to True
+ # Turn the flag to True
self.agents_found = True
# Getting values to sample agents for interaction
- range_min = int( (self.interaction_probability * len(agents) ) / 3)
- range_max = int( (self.interaction_probability * len(agents) ) / 2)
- range_avg = int( (range_min + range_max) / 2)
+ range_min = int((self.interaction_probability * len(agents)) / 3)
+ range_max = int((self.interaction_probability * len(agents)) / 2)
+ range_avg = int((range_min + range_max) / 2)
# Sample the agents from the list of agents. The number of samples depend on above values
- no_agents_found = random.sample(range (len(agents) ), random.choice( [range_min,range_max,range_avg] ) )
-
+ no_agents_found = random.sample(range(len(agents)), random.choice(
+ [range_min, range_max, range_avg]))
+
# Extract the individuals from the nearby agents and store the individuals in the class variable
- self.nearby_agents = [ agents[id].individual[0] for id in no_agents_found ]
+ self.nearby_agents = [agents[id].individual[0] for id in
+ no_agents_found]
def act(self):
# Process the information if the agent has sense nearby agents
if self.agents_found:
-
- # Combine the original individual and individuals found by interacting with nearby agents to form
+ # Combine the original individual and individuals found by interacting with nearby agents to form
# a population
individuals = self.individual + self.nearby_agents
@@ -79,16 +80,15 @@ def act(self):
# Generate statistics for run so far
get_stats(individuals)
-
+
# Sort the individuals list
individuals.sort(reverse=True)
# Get the highest performing individual from the sorted population
self.new_individual = individuals[0]
-
+
def update(self):
- #Update the information if the agent has sense nearby agents
+ # Update the information if the agent has sense nearby agents
if self.agents_found:
-
# Replace the individual with the highest performing individual obtained from act method
- self.individual = [self.new_individual]
\ No newline at end of file
+ self.individual = [self.new_individual]
diff --git a/src/algorithm/distributed_algorithm/search_loop.py b/src/algorithm/distributed_algorithm/search_loop.py
index eeda9fd9..4ed52751 100644
--- a/src/algorithm/distributed_algorithm/search_loop.py
+++ b/src/algorithm/distributed_algorithm/search_loop.py
@@ -1,16 +1,18 @@
from agent.agent import Agent
from algorithm.parameters import params
-#from fitness.evaluation import evaluate_fitness
from stats.stats import stats
-#from utilities.stats import trackers
-#from operators.initialisation import initialisation
-#from utilities.algorithm.initialise_run import pool_init
-def create_agents(n,p):
+
+# from fitness.evaluation import evaluate_fitness
+# from utilities.stats import trackers
+# from operators.initialisation import initialisation
+# from utilities.algorithm.initialise_run import pool_init
+
+def create_agents(n, p):
"""
Create a list of agent specified by n parameter
"""
- return [ Agent(p) for a in range(n) ]
+ return [Agent(p) for a in range(n)]
def search_loop():
@@ -18,13 +20,14 @@ def search_loop():
This loop is used when the multi-agent parameter is passed
"""
# Create a list of agents based on the parameter passed
- agents = create_agents(params['AGENT_SIZE'],params['INTERACTION_PROBABILITY'])
+ agents = create_agents(params['AGENT_SIZE'],
+ params['INTERACTION_PROBABILITY'])
- ##Multi-Agent based GE
- for generation in range(1,(params['GENERATIONS']+1)):
+ ## Multi-Agent based GE
+ for generation in range(1, (params['GENERATIONS'] + 1)):
stats['gen'] = generation
-
- #New generation
+
+ # New generation
agents = params['STEP'](agents)
-
- return [agent.individual[0] for agent in agents]
\ No newline at end of file
+
+ return [agent.individual[0] for agent in agents]
diff --git a/src/algorithm/distributed_algorithm/step.py b/src/algorithm/distributed_algorithm/step.py
index a98fb39c..f7f95529 100644
--- a/src/algorithm/distributed_algorithm/step.py
+++ b/src/algorithm/distributed_algorithm/step.py
@@ -1,4 +1,3 @@
-
def step(agents):
"""
Runs a single generation of the evolutionary algorithm process
@@ -7,11 +6,11 @@ def step(agents):
for agent in agents:
# Sense the environment
agent.sense(agents)
-
+
# Based on the values from the sensor perform action
agent.act()
# Update the state of the agent
- agent.update()
-
- return agents
\ No newline at end of file
+ agent.update()
+
+ return agents
diff --git a/src/algorithm/hill_climbing.py b/src/algorithm/hill_climbing.py
index 70f262f5..384d5399 100644
--- a/src/algorithm/hill_climbing.py
+++ b/src/algorithm/hill_climbing.py
@@ -1,9 +1,8 @@
from algorithm.parameters import params
from fitness.evaluation import evaluate_fitness
-from stats.stats import stats, get_stats
+from stats.stats import get_stats, stats
from utilities.stats import trackers
-
"""Hill-climbing is just about the simplest meta-heuristic there
is. It's of interest in GP/GE because of the lingering suspicion among
many researchers that crossover just doesn't work. This goes back to
@@ -92,15 +91,15 @@ def LAHC_search_loop():
# Find the best individual so far.
best = trackers.best_ever
-
+
# Set history.
Lfa = params['HILL_CLIMBING_HISTORY']
history = [best for _ in range(Lfa)]
# iters is the number of individuals examined so far.
iters = len(individuals)
-
- for generation in range(1, (params['GENERATIONS']+1)):
+
+ for generation in range(1, (params['GENERATIONS'] + 1)):
this_gen = []
@@ -119,16 +118,16 @@ def LAHC_search_loop():
# Find the index of the relevant individual from the late
# acceptance history.
idx = iters % Lfa
-
+
if candidate_best >= history[idx]:
best = candidate_best # Accept the candidate
-
+
else:
pass # reject the candidate
# Set the new best into the history.
history[idx] = best
-
+
# Increment evaluation counter.
iters += 1
@@ -194,7 +193,7 @@ def SCHC_search_loop():
:return: The final population.
"""
-
+
# Calculate maximum number of evaluation iterations.
max_its = params['POPULATION_SIZE'] * params['GENERATIONS']
count_method = params['SCHC_COUNT_METHOD']
@@ -215,11 +214,11 @@ def SCHC_search_loop():
# Set history and counter.
history = params['HILL_CLIMBING_HISTORY']
counter = 0
-
+
# iters is the number of individuals examined/iterations so far.
iters = len(individuals)
-
- for generation in range(1, (params['GENERATIONS']+1)):
+
+ for generation in range(1, (params['GENERATIONS'] + 1)):
this_gen = []
@@ -238,15 +237,15 @@ def SCHC_search_loop():
# count
if count_method == "count_all": # we count all iterations (moves)
counter += 1 # increment the counter
-
+
elif count_method == "acp": # we count accepted moves only
if candidate_best > cost_bound or candidate_best >= best:
counter += 1 # increment the counter
-
+
elif count_method == "imp": # we count improving moves only
if candidate_best > best:
counter += 1 # increment the counter
-
+
else:
s = "algorithm.hill_climbing.SCHC_search_loop\n" \
"Error: Unknown count method: %s" % (count_method)
@@ -255,7 +254,7 @@ def SCHC_search_loop():
# accept
if candidate_best > cost_bound or candidate_best >= best:
best = candidate_best # accept the candidate
-
+
else:
pass # reject the candidate
diff --git a/src/algorithm/mapper.py b/src/algorithm/mapper.py
index 8e26b7b4..79053433 100644
--- a/src/algorithm/mapper.py
+++ b/src/algorithm/mapper.py
@@ -1,6 +1,6 @@
from collections import deque
-import numpy as np
+import numpy as np
from algorithm.parameters import params
from representation.tree import Tree
from utilities.representation.python_filter import python_filter
@@ -36,16 +36,16 @@ def mapper(genome, tree):
# algorithm.mapper.map_ind_from_genome() if we don't need to
# store the whole tree.
phenotype, genome, tree, nodes, invalid, depth, \
- used_codons = map_ind_from_genome(genome)
+ used_codons = map_ind_from_genome(genome)
else:
# Build the tree using algorithm.mapper.map_tree_from_genome().
phenotype, genome, tree, nodes, invalid, depth, \
- used_codons = map_tree_from_genome(genome)
+ used_codons = map_tree_from_genome(genome)
else:
# We have a tree.
-
+
# genome, output, invalid, depth, and nodes can all be
# generated by recursing through the tree once.
@@ -53,7 +53,7 @@ def mapper(genome, tree):
nodes = tree.get_tree_info(params['BNF_GRAMMAR'].non_terminals.keys(),
[], [])
used_codons, phenotype = len(genome), "".join(output)
-
+
if params['BNF_GRAMMAR'].python_mode and not invalid:
# Grammar contains python code
@@ -108,7 +108,7 @@ def map_ind_from_genome(genome):
break
if used_input % n_input == 0 and \
- used_input > 0 and \
+ used_input > 0 and \
any([i[0]["type"] == "NT" for i in unexpanded_symbols]):
# If we have reached the end of the genome and unexpanded
# non-terminals remain, then we need to wrap back to the start
@@ -196,11 +196,11 @@ def map_tree_from_genome(genome):
if invalid:
# Return "None" phenotype if invalid
return None, genome, tree, nodes, invalid, max_depth, \
- used_codons
+ used_codons
else:
return phenotype, genome, tree, nodes, invalid, max_depth, \
- used_codons
+ used_codons
def genome_tree_map(tree, genome, output, index, depth, max_depth, nodes,
diff --git a/src/algorithm/parameters.py b/src/algorithm/parameters.py
index 37e62a92..c81ef645 100644
--- a/src/algorithm/parameters.py
+++ b/src/algorithm/parameters.py
@@ -5,188 +5,187 @@
hostname = gethostname().split('.')
machine_name = hostname[0]
-
"""Algorithm parameters"""
params = {
- # Set default step and search loop functions
- 'SEARCH_LOOP': 'search_loop',
- 'STEP': 'step',
-
- # Evolutionary Parameters
- 'POPULATION_SIZE': 500,
- 'GENERATIONS': 50,
- 'HILL_CLIMBING_HISTORY': 1000,
- 'SCHC_COUNT_METHOD': "count_all",
-
- # Set optional experiment name
- 'EXPERIMENT_NAME': None,
- # Set default number of runs to be done.
- # ONLY USED WITH EXPERIMENT MANAGER.
- 'RUNS': 1,
-
- # Class of problem
- 'FITNESS_FUNCTION': "supervised_learning.regression",
-
- # Select problem dataset
- 'DATASET_TRAIN': "Vladislavleva4/Train.txt",
- 'DATASET_TEST': None,
- 'DATASET_DELIMITER': None,
-
- # Set grammar file
- 'GRAMMAR_FILE': "supervised_learning/Vladislavleva4.bnf",
-
- # Set the number of depths permutations are calculated for
- # (starting from the minimum path of the grammar).
- # Mainly for use with the grammar analyser script.
- 'PERMUTATION_RAMPS': 5,
-
- # Select error metric
- 'ERROR_METRIC': None,
-
- # Optimise constants in the supervised_learning fitness function.
- 'OPTIMIZE_CONSTANTS': False,
-
- # Specify target for target problems
- 'TARGET': "ponyge_rocks",
-
- # Set max sizes of individuals
- 'MAX_TREE_DEPTH': 90, # SET TO 90 DUE TO PYTHON EVAL() STACK LIMIT.
- # INCREASE AT YOUR OWN RISK.
- 'MAX_TREE_NODES': None,
- 'CODON_SIZE': 100000,
- 'MAX_GENOME_LENGTH': None,
- 'MAX_WRAPS': 0,
-
- # INITIALISATION
- # Set initialisation operator.
- 'INITIALISATION': "operators.initialisation.PI_grow",
- # Set the maximum geneome length for initialisation.
- 'INIT_GENOME_LENGTH': 200,
- # Set the maximum tree depth for initialisation.
- 'MAX_INIT_TREE_DEPTH': 10,
- # Set the minimum tree depth for initialisation.
- 'MIN_INIT_TREE_DEPTH': None,
-
- # SELECTION
- # Set selection operator.
- 'SELECTION': "operators.selection.tournament",
- # For tournament selection
- 'TOURNAMENT_SIZE': 2,
- # For truncation selection
- 'SELECTION_PROPORTION': 0.5,
- # Allow for selection of invalid individuals during selection process.
- 'INVALID_SELECTION': False,
-
- # OPERATOR OPTIONS
- # Boolean flag for selecting whether or not mutation is confined to
- # within the used portion of the genome. Default set to True.
- 'WITHIN_USED': True,
-
- # CROSSOVER
- # Set crossover operator.
- 'CROSSOVER': "operators.crossover.variable_onepoint",
- # Set crossover probability.
- 'CROSSOVER_PROBABILITY': 0.75,
- # Prevents crossover from generating invalids.
- 'NO_CROSSOVER_INVALIDS': False,
-
- # MUTATION
- # Set mutation operator.
- 'MUTATION': "operators.mutation.int_flip_per_codon",
- # Set mutation probability (None defaults to 1 over the length of
- # the genome for each codon)
- 'MUTATION_PROBABILITY': None,
- # Set number of mutation events
- 'MUTATION_EVENTS': 1,
- # Prevents mutation from generating invalids.
- 'NO_MUTATION_INVALIDS': False,
-
- # REPLACEMENT
- # Set replacement operator.
- 'REPLACEMENT': "operators.replacement.generational",
- # Set elite size.
- 'ELITE_SIZE': None,
-
- # DEBUGGING
- # Use this to turn on debugging mode. This mode doesn't write any files
- # and should be used when you want to test new methods.
- 'DEBUG': False,
-
- # PRINTING
- # Use this to print out basic statistics for each generation to the
- # command line.
- 'VERBOSE': False,
- # Use this to prevent anything being printed to the command line.
- 'SILENT': False,
-
- # SAVING
- # Save the phenotype of the best individual from each generation. Can
- # generate a lot of files. DEBUG must be False.
- 'SAVE_ALL': False,
- # Save a plot of the evolution of the best fitness result for each
- # generation.
- 'SAVE_PLOTS': True,
-
- # MULTIPROCESSING
- # Multi-core parallel processing of phenotype evaluations.
- 'MULTICORE': False,
- # Set the number of cpus to be used for multiprocessing
- 'CORES': cpu_count(),
-
- # STATE SAVING/LOADING
- # Save the state of the evolutionary run every generation. You can
- # specify how often you want to save the state with SAVE_STATE_STEP.
- 'SAVE_STATE': False,
- # Specify how often the state of the current evolutionary run is
- # saved (i.e. every n-th generation). Requires int value.
- 'SAVE_STATE_STEP': 1,
- # Load an evolutionary run from a saved state. You must specify the
- # full file path to the desired state file. Note that state files have
- # no file type.
- 'LOAD_STATE': None,
-
- # SEEDING
- # Specify a list of PonyGE2 individuals with which to seed the initial
- # population.
- 'SEED_INDIVIDUALS': [],
- # Specify a target seed folder in the 'seeds' directory that contains a
- # population of individuals with which to seed a run.
- 'TARGET_SEED_FOLDER': None,
- # Set a target phenotype string for reverse mapping into a GE
- # individual
- 'REVERSE_MAPPING_TARGET': None,
- # Set Random Seed for all Random Number Generators to be used by
- # PonyGE2, including the standard Python RNG and the NumPy RNG.
- 'RANDOM_SEED': None,
-
- # CACHING
- # The cache tracks unique individuals across evolution by saving a
- # string of each phenotype in a big list of all phenotypes. Saves all
- # fitness information on each individual. Gives you an idea of how much
- # repetition is in standard GE/GP.
- 'CACHE': False,
- # Uses the cache to look up the fitness of duplicate individuals. CACHE
- # must be set to True if you want to use this.
- 'LOOKUP_FITNESS': False,
- # Uses the cache to give a bad fitness to duplicate individuals. CACHE
- # must be True if you want to use this (obviously)
- 'LOOKUP_BAD_FITNESS': False,
- # Removes duplicate individuals from the population by replacing them
- # with mutated versions of the original individual. Hopefully this will
- # encourage diversity in the population.
- 'MUTATE_DUPLICATES': False,
-
- # MULTI-AGENT Parameters
- # True or False for multi-agent
- 'MULTIAGENT': False,
- # Agent Size. Number of agents having their own copy of genetic material
- 'AGENT_SIZE': 100,
- # Interaction Probability. How frequently the agents can interaction with each other
- 'INTERACTION_PROBABILITY': 0.5,
-
- # OTHER
- # Set machine name (useful for doing multiple runs)
- 'MACHINE': machine_name
+ # Set default step and search loop functions
+ 'SEARCH_LOOP': 'search_loop',
+ 'STEP': 'step',
+
+ # Evolutionary Parameters
+ 'POPULATION_SIZE': 500,
+ 'GENERATIONS': 50,
+ 'HILL_CLIMBING_HISTORY': 1000,
+ 'SCHC_COUNT_METHOD': "count_all",
+
+ # Set optional experiment name
+ 'EXPERIMENT_NAME': None,
+ # Set default number of runs to be done.
+ # ONLY USED WITH EXPERIMENT MANAGER.
+ 'RUNS': 1,
+
+ # Class of problem
+ 'FITNESS_FUNCTION': "supervised_learning.regression",
+
+ # Select problem dataset
+ 'DATASET_TRAIN': "Vladislavleva4/Train.txt",
+ 'DATASET_TEST': None,
+ 'DATASET_DELIMITER': None,
+
+ # Set grammar file
+ 'GRAMMAR_FILE': "supervised_learning/Vladislavleva4.bnf",
+
+ # Set the number of depths permutations are calculated for
+ # (starting from the minimum path of the grammar).
+ # Mainly for use with the grammar analyser script.
+ 'PERMUTATION_RAMPS': 5,
+
+ # Select error metric
+ 'ERROR_METRIC': None,
+
+ # Optimise constants in the supervised_learning fitness function.
+ 'OPTIMIZE_CONSTANTS': False,
+
+ # Specify target for target problems
+ 'TARGET': "ponyge_rocks",
+
+ # Set max sizes of individuals
+ 'MAX_TREE_DEPTH': 90, # SET TO 90 DUE TO PYTHON EVAL() STACK LIMIT.
+ # INCREASE AT YOUR OWN RISK.
+ 'MAX_TREE_NODES': None,
+ 'CODON_SIZE': 100000,
+ 'MAX_GENOME_LENGTH': None,
+ 'MAX_WRAPS': 0,
+
+ # INITIALISATION
+ # Set initialisation operator.
+ 'INITIALISATION': "operators.initialisation.PI_grow",
+ # Set the maximum geneome length for initialisation.
+ 'INIT_GENOME_LENGTH': 200,
+ # Set the maximum tree depth for initialisation.
+ 'MAX_INIT_TREE_DEPTH': 10,
+ # Set the minimum tree depth for initialisation.
+ 'MIN_INIT_TREE_DEPTH': None,
+
+ # SELECTION
+ # Set selection operator.
+ 'SELECTION': "operators.selection.tournament",
+ # For tournament selection
+ 'TOURNAMENT_SIZE': 2,
+ # For truncation selection
+ 'SELECTION_PROPORTION': 0.5,
+ # Allow for selection of invalid individuals during selection process.
+ 'INVALID_SELECTION': False,
+
+ # OPERATOR OPTIONS
+ # Boolean flag for selecting whether or not mutation is confined to
+ # within the used portion of the genome. Default set to True.
+ 'WITHIN_USED': True,
+
+ # CROSSOVER
+ # Set crossover operator.
+ 'CROSSOVER': "operators.crossover.variable_onepoint",
+ # Set crossover probability.
+ 'CROSSOVER_PROBABILITY': 0.75,
+ # Prevents crossover from generating invalids.
+ 'NO_CROSSOVER_INVALIDS': False,
+
+ # MUTATION
+ # Set mutation operator.
+ 'MUTATION': "operators.mutation.int_flip_per_codon",
+ # Set mutation probability (None defaults to 1 over the length of
+ # the genome for each codon)
+ 'MUTATION_PROBABILITY': None,
+ # Set number of mutation events
+ 'MUTATION_EVENTS': 1,
+ # Prevents mutation from generating invalids.
+ 'NO_MUTATION_INVALIDS': False,
+
+ # REPLACEMENT
+ # Set replacement operator.
+ 'REPLACEMENT': "operators.replacement.generational",
+ # Set elite size.
+ 'ELITE_SIZE': None,
+
+ # DEBUGGING
+ # Use this to turn on debugging mode. This mode doesn't write any files
+ # and should be used when you want to test new methods.
+ 'DEBUG': False,
+
+ # PRINTING
+ # Use this to print out basic statistics for each generation to the
+ # command line.
+ 'VERBOSE': False,
+ # Use this to prevent anything being printed to the command line.
+ 'SILENT': False,
+
+ # SAVING
+ # Save the phenotype of the best individual from each generation. Can
+ # generate a lot of files. DEBUG must be False.
+ 'SAVE_ALL': False,
+ # Save a plot of the evolution of the best fitness result for each
+ # generation.
+ 'SAVE_PLOTS': True,
+
+ # MULTIPROCESSING
+ # Multi-core parallel processing of phenotype evaluations.
+ 'MULTICORE': False,
+ # Set the number of cpus to be used for multiprocessing
+ 'CORES': cpu_count(),
+
+ # STATE SAVING/LOADING
+ # Save the state of the evolutionary run every generation. You can
+ # specify how often you want to save the state with SAVE_STATE_STEP.
+ 'SAVE_STATE': False,
+ # Specify how often the state of the current evolutionary run is
+ # saved (i.e. every n-th generation). Requires int value.
+ 'SAVE_STATE_STEP': 1,
+ # Load an evolutionary run from a saved state. You must specify the
+ # full file path to the desired state file. Note that state files have
+ # no file type.
+ 'LOAD_STATE': None,
+
+ # SEEDING
+ # Specify a list of PonyGE2 individuals with which to seed the initial
+ # population.
+ 'SEED_INDIVIDUALS': [],
+ # Specify a target seed folder in the 'seeds' directory that contains a
+ # population of individuals with which to seed a run.
+ 'TARGET_SEED_FOLDER': None,
+ # Set a target phenotype string for reverse mapping into a GE
+ # individual
+ 'REVERSE_MAPPING_TARGET': None,
+ # Set Random Seed for all Random Number Generators to be used by
+ # PonyGE2, including the standard Python RNG and the NumPy RNG.
+ 'RANDOM_SEED': None,
+
+ # CACHING
+ # The cache tracks unique individuals across evolution by saving a
+ # string of each phenotype in a big list of all phenotypes. Saves all
+ # fitness information on each individual. Gives you an idea of how much
+ # repetition is in standard GE/GP.
+ 'CACHE': False,
+ # Uses the cache to look up the fitness of duplicate individuals. CACHE
+ # must be set to True if you want to use this.
+ 'LOOKUP_FITNESS': False,
+ # Uses the cache to give a bad fitness to duplicate individuals. CACHE
+ # must be True if you want to use this (obviously)
+ 'LOOKUP_BAD_FITNESS': False,
+ # Removes duplicate individuals from the population by replacing them
+ # with mutated versions of the original individual. Hopefully this will
+ # encourage diversity in the population.
+ 'MUTATE_DUPLICATES': False,
+
+ # MULTI-AGENT Parameters
+ # True or False for multi-agent
+ 'MULTIAGENT': False,
+ # Agent Size. Number of agents having their own copy of genetic material
+ 'AGENT_SIZE': 100,
+ # Interaction Probability. How frequently the agents can interaction with each other
+ 'INTERACTION_PROBABILITY': 0.5,
+
+ # OTHER
+ # Set machine name (useful for doing multiple runs)
+ 'MACHINE': machine_name
}
@@ -218,7 +217,7 @@ def load_params(file_name):
# Everything to the left of the colon is the parameter key,
# everything to the right is the parameter value.
- key, value = line[:split], line[split+1:].strip()
+ key, value = line[:split], line[split + 1:].strip()
# Evaluate parameters.
try:
@@ -230,7 +229,7 @@ def load_params(file_name):
# Set parameter
params[key] = value
-
+
def set_params(command_line_args, create_files=True):
"""
@@ -313,7 +312,7 @@ def set_params(command_line_args, create_files=True):
# Set GENOME_OPERATIONS automatically for faster linear operations.
if (params['CROSSOVER'].representation == "subtree" or
- params['MUTATION'].representation == "subtree"):
+ params['MUTATION'].representation == "subtree"):
params['GENOME_OPERATIONS'] = False
else:
params['GENOME_OPERATIONS'] = True
@@ -342,7 +341,8 @@ def set_params(command_line_args, create_files=True):
# Parse grammar file and set grammar class.
params['BNF_GRAMMAR'] = grammar.Grammar(path.join("..", "grammars",
- params['GRAMMAR_FILE']))
+ params[
+ 'GRAMMAR_FILE']))
# Population loading for seeding runs (if specified)
if params['TARGET_SEED_FOLDER']:
diff --git a/src/algorithm/search_loop.py b/src/algorithm/search_loop.py
index 75c785fa..9475174c 100755
--- a/src/algorithm/search_loop.py
+++ b/src/algorithm/search_loop.py
@@ -1,10 +1,12 @@
from multiprocessing import Pool
+
from algorithm.parameters import params
from fitness.evaluation import evaluate_fitness
-from stats.stats import stats, get_stats
-from utilities.stats import trackers
from operators.initialisation import initialisation
+from stats.stats import get_stats, stats
from utilities.algorithm.initialise_run import pool_init
+from utilities.stats import trackers
+
def search_loop():
"""
@@ -30,7 +32,7 @@ def search_loop():
get_stats(individuals)
# Traditional GE
- for generation in range(1, (params['GENERATIONS']+1)):
+ for generation in range(1, (params['GENERATIONS'] + 1)):
stats['gen'] = generation
# New generation
@@ -51,23 +53,23 @@ def search_loop_from_state():
:return: The final population after the evolutionary process has run for
the specified number of generations.
"""
-
+
individuals = trackers.state_individuals
-
+
if params['MULTICORE']:
# initialize pool once, if multi-core is enabled
params['POOL'] = Pool(processes=params['CORES'], initializer=pool_init,
initargs=(params,)) # , maxtasksperchild=1)
-
+
# Traditional GE
for generation in range(stats['gen'] + 1, (params['GENERATIONS'] + 1)):
stats['gen'] = generation
-
+
# New generation
individuals = params['STEP'](individuals)
-
+
if params['MULTICORE']:
# Close the workers pool (otherwise they'll live on forever).
params['POOL'].close()
-
- return individuals
\ No newline at end of file
+
+ return individuals
diff --git a/src/algorithm/step.py b/src/algorithm/step.py
index c1930113..0034a111 100755
--- a/src/algorithm/step.py
+++ b/src/algorithm/step.py
@@ -5,6 +5,7 @@
from operators.selection import selection
from stats.stats import get_stats
+
def step(individuals):
"""
Runs a single generation of the evolutionary algorithm process:
@@ -35,7 +36,7 @@ def step(individuals):
# Generate statistics for run so far
get_stats(individuals)
-
+
return individuals
@@ -48,7 +49,7 @@ def steady_state_step(individuals):
evolutionary generation will be imposed.
:return: The next generation of the population.
"""
-
+
individuals = steady_state(individuals)
-
- return individuals
\ No newline at end of file
+
+ return individuals
diff --git a/src/fitness/base_ff_classes/base_ff.py b/src/fitness/base_ff_classes/base_ff.py
index c9c5ddb5..32620c67 100644
--- a/src/fitness/base_ff_classes/base_ff.py
+++ b/src/fitness/base_ff_classes/base_ff.py
@@ -19,7 +19,7 @@ class base_ff:
def __init__(self):
pass
-
+
def __call__(self, ind, **kwargs):
"""
@@ -27,29 +27,29 @@ def __call__(self, ind, **kwargs):
:param ind: An individual to be evaluated.
:return: The fitness of the evaluated individual.
"""
-
+
try:
# Evaluate the fitness using the evaluate() function. This function
# can be over-written by classes which inherit from this base
# class.
fitness = self.evaluate(ind, **kwargs)
-
+
except (FloatingPointError, ZeroDivisionError, OverflowError,
MemoryError):
# FP err can happen through eg overflow (lots of pow/exp calls)
# ZeroDiv can happen when using unprotected operators
fitness = base_ff.default_fitness
-
+
# These individuals are valid (i.e. not invalids), but they have
# produced a runtime error.
ind.runtime_error = True
-
+
except Exception as err:
# Other errors should not usually happen (unless we have
# an unprotected operator) so user would prefer to see them.
print(err)
raise
-
+
return fitness
def evaluate(self, ind, **kwargs):
diff --git a/src/fitness/base_ff_classes/ff_template.py b/src/fitness/base_ff_classes/ff_template.py
index f196add3..e45c4e8d 100644
--- a/src/fitness/base_ff_classes/ff_template.py
+++ b/src/fitness/base_ff_classes/ff_template.py
@@ -37,10 +37,10 @@ def __init__(self):
All fitness functions which inherit from the bass fitness function
class must initialise the base class during their own initialisation.
"""
-
+
# Initialise base fitness function class.
super().__init__()
-
+
def evaluate(self, ind, **kwargs):
"""
Default fitness execution call for all fitness functions. When
@@ -57,8 +57,8 @@ def evaluate(self, ind, **kwargs):
:param kwargs: Optional extra arguments.
:return: The fitness of the evaluated individual.
"""
-
+
# Evaluate the fitness of the phenotype
fitness = eval(ind.phenotype)
-
+
return fitness
diff --git a/src/fitness/base_ff_classes/moo_ff.py b/src/fitness/base_ff_classes/moo_ff.py
index aab361bd..faeed253 100644
--- a/src/fitness/base_ff_classes/moo_ff.py
+++ b/src/fitness/base_ff_classes/moo_ff.py
@@ -1,4 +1,5 @@
from math import isnan
+
import numpy as np
np.seterr(all="raise")
@@ -24,17 +25,17 @@ def __init__(self, fitness_functions):
# Set list of individual fitness functions.
self.fitness_functions = fitness_functions
self.num_obj = len(fitness_functions)
-
+
# Initialise individual fitness functions.
for i, ff in enumerate(self.fitness_functions):
self.fitness_functions[i] = ff()
-
+
# Set up list of default fitness values (as per individual fitness
# functions).
self.default_fitness = []
for f in fitness_functions:
self.default_fitness.append(f.default_fitness)
-
+
def __call__(self, ind):
"""
Note that math functions used in the solutions are imported from either
@@ -49,7 +50,7 @@ def __call__(self, ind):
# made by the function multi_objc_eval, implemented by a subclass,
# according to the problem.
fitness = [ff(ind) for ff in self.fitness_functions]
-
+
if any([isnan(i) for i in fitness]):
# Check if any objective fitness value is NaN, if so set default
# fitness.
@@ -68,8 +69,8 @@ def value(fitness_vector, objective_index):
:param objective_index: The index of the desired fitness.
:return: The fitness at the objective index of the fitness vector.
"""
-
+
if not isinstance(fitness_vector, list):
return float("inf")
-
+
return fitness_vector[objective_index]
diff --git a/src/fitness/evaluation.py b/src/fitness/evaluation.py
index caf9f8d8..96cab397 100755
--- a/src/fitness/evaluation.py
+++ b/src/fitness/evaluation.py
@@ -28,7 +28,7 @@ def evaluate_fitness(individuals):
"""
results, pool = [], None
-
+
if params['MULTICORE']:
pool = params['POOL']
@@ -67,7 +67,7 @@ def evaluate_fitness(individuals):
while (not ind.phenotype) or ind.phenotype in cache:
ind = params['MUTATION'](ind)
stats['regens'] += 1
-
+
# Need to overwrite the current individual in the pop.
individuals[name] = ind
ind.name = name
@@ -86,11 +86,11 @@ def evaluate_fitness(individuals):
# Add the evaluated individual to the cache.
cache[ind.phenotype] = ind.fitness
-
+
# Check if individual had a runtime error.
if ind.runtime_error:
runtime_error_cache.append(ind.phenotype)
-
+
return individuals
@@ -112,7 +112,7 @@ def eval_or_append(ind, results, pool):
# Add the individual to the pool of jobs.
results.append(pool.apply_async(ind.evaluate, ()))
return results
-
+
else:
# Evaluate the individual.
ind.evaluate()
@@ -125,11 +125,10 @@ def eval_or_append(ind, results, pool):
# The phenotype string of the individual does not appear
# in the cache, it must be evaluated and added to the
# cache.
-
+
if (isinstance(ind.fitness, list) and not
- any([np.isnan(i) for i in ind.fitness])) or \
+ any([np.isnan(i) for i in ind.fitness])) or \
(not isinstance(ind.fitness, list) and not
- np.isnan(ind.fitness)):
-
+ np.isnan(ind.fitness)):
# All fitnesses are valid.
cache[ind.phenotype] = ind.fitness
diff --git a/src/fitness/minimise_nodes.py b/src/fitness/minimise_nodes.py
index 21ed54bc..2cce93df 100644
--- a/src/fitness/minimise_nodes.py
+++ b/src/fitness/minimise_nodes.py
@@ -10,6 +10,6 @@ class minimise_nodes(base_ff):
def __init__(self):
# Initialise base fitness function class.
super().__init__()
-
+
def evaluate(self, ind, **kwargs):
return ind.nodes
diff --git a/src/fitness/multi_objective/binary_phenotype_to_float.py b/src/fitness/multi_objective/binary_phenotype_to_float.py
index 4e46fe71..33617fd1 100755
--- a/src/fitness/multi_objective/binary_phenotype_to_float.py
+++ b/src/fitness/multi_objective/binary_phenotype_to_float.py
@@ -3,7 +3,6 @@
class binary_phenotype_to_float(base_ff):
-
"""
Fitness function for the first problem (T_1) presented in
[Zitzler2000].
@@ -16,13 +15,12 @@ class binary_phenotype_to_float(base_ff):
def __init__(self):
# Initialise base fitness function class.
super().__init__()
-
+
def evaluate(self, ind, **kwargs):
-
min_value = [0] * 30
max_value = [1] * 30
-
+
real_chromosome = binary_phen_to_float(ind.phenotype, 30, min_value,
max_value)
-
+
return real_chromosome[0]
diff --git a/src/fitness/multi_objective/singlefit_multiobj.py b/src/fitness/multi_objective/singlefit_multiobj.py
index 50965b82..f230169e 100644
--- a/src/fitness/multi_objective/singlefit_multiobj.py
+++ b/src/fitness/multi_objective/singlefit_multiobj.py
@@ -4,8 +4,8 @@
@17/01/18 11:09
"""
-from fitness.base_ff_classes.base_ff import base_ff
import numpy as np
+from fitness.base_ff_classes.base_ff import base_ff
class singlefit_multiobj(base_ff):
@@ -18,7 +18,6 @@ class singlefit_multiobj(base_ff):
multi_objective = True
def __init__(self):
-
# Initialise base fitness function class.
super().__init__()
@@ -29,7 +28,6 @@ def __init__(self):
self.fitness_functions = [dummyfit, dummyfit]
self.default_fitness = [float('nan'), float('nan')]
-
def evaluate(self, ind, **kwargs):
"""Dummy fitness function that generates 2 fitness values"""
phenotype = ind.phenotype
diff --git a/src/fitness/multi_objective/zdt1.py b/src/fitness/multi_objective/zdt1.py
index ceaf4c2a..8a67ea3a 100644
--- a/src/fitness/multi_objective/zdt1.py
+++ b/src/fitness/multi_objective/zdt1.py
@@ -13,24 +13,23 @@ class zdt1(base_ff):
of multiobjective evolutionary algorithms: Empirical results.
Evolutionary computation 8.2 (2000): 173-195.
"""
-
+
def __init__(self):
# Initialise base fitness function class.
super().__init__()
-
+
def evaluate(self, ind, **kwargs):
-
min_value = [0] * 30
max_value = [1] * 30
-
+
real_chromosome = binary_phen_to_float(ind.phenotype, 30, min_value,
max_value)
-
+
summation = 0
for i in range(1, len(real_chromosome)):
summation += real_chromosome[i]
-
+
g = 1 + 9 * summation / (len(real_chromosome) - 1.0)
h = 1 - sqrt(real_chromosome[0] / g)
-
+
return g * h
diff --git a/src/fitness/progsys.py b/src/fitness/progsys.py
index a8860fac..fa8726a9 100644
--- a/src/fitness/progsys.py
+++ b/src/fitness/progsys.py
@@ -1,10 +1,10 @@
-from algorithm.parameters import params
-from fitness.base_ff_classes.base_ff import base_ff
-
-from os import path
-import subprocess
import json
+import subprocess
import sys
+from os import path
+
+from algorithm.parameters import params
+from fitness.base_ff_classes.base_ff import base_ff
class progsys(base_ff):
@@ -27,7 +27,7 @@ class progsys(base_ff):
def __init__(self):
# Initialise base fitness function class.
super().__init__()
-
+
self.training, self.test, self.embed_header, self.embed_footer = \
self.get_data(params['DATASET_TRAIN'], params['DATASET_TEST'],
params['GRAMMAR_FILE'])
@@ -38,9 +38,9 @@ def __init__(self):
"Fitness function only allows sequential evaluation.")
def evaluate(self, ind, **kwargs):
-
+
dist = kwargs.get('dist', 'training')
-
+
program = self.format_program(ind.phenotype,
self.embed_header, self.embed_footer)
data = self.training if dist == "training" else self.test
@@ -49,7 +49,7 @@ def evaluate(self, ind, **kwargs):
'variables': ['cases', 'caseQuality',
'quality']})
- self.eval.stdin.write((eval_json+'\n').encode())
+ self.eval.stdin.write((eval_json + '\n').encode())
self.eval.stdin.flush()
result_json = self.eval.stdout.readline()
@@ -149,7 +149,7 @@ def get_data(self, train, test, grammar):
if insert > 0:
embed_header = embed_code[:insert]
embed_footer = embed_code[insert + len(self.INSERTCODE):]
- with open(train_set, 'r') as train_file,\
+ with open(train_set, 'r') as train_file, \
open(test_set, 'r') as test_file:
return train_file.read(), test_file.read(), \
embed_header, embed_footer
diff --git a/src/fitness/pymax.py b/src/fitness/pymax.py
index 1b28f9ab..265839f1 100644
--- a/src/fitness/pymax.py
+++ b/src/fitness/pymax.py
@@ -16,22 +16,22 @@ class pymax(base_ff):
"""
maximise = True # True as it ever was.
-
+
def __init__(self):
# Initialise base fitness function class.
super().__init__()
-
+
def evaluate(self, ind, **kwargs):
# ind.phenotype will be a string, including function definitions etc.
# When we exec it, it will create a value XXX_output_XXX, but we exec
# inside an empty dict for safety.
-
+
p, d = ind.phenotype, {}
-
+
# Exec the phenotype.
exec(p, d)
-
+
# Get the output
s = d['XXX_output_XXX'] # this is the program's output: a number.
-
+
return s
diff --git a/src/fitness/regex/RegexEval.py b/src/fitness/regex/RegexEval.py
index 493d8054..c9eb1bb8 100644
--- a/src/fitness/regex/RegexEval.py
+++ b/src/fitness/regex/RegexEval.py
@@ -2,11 +2,12 @@
from multiprocessing import Process, Queue
import fitness.regex.testing.RegexTestGenerator as TestGen
-from fitness.regex.testing.RegexTimer import time_regex_test_case
from algorithm.parameters import params
from fitness.base_ff_classes.base_ff import base_ff
+from fitness.regex.testing.RegexTimer import time_regex_test_case
from stats.stats import stats
+
# Author: Brendan Cody-Kenny - codykenny at gmail
@@ -18,25 +19,25 @@ class RegexEval(base_ff):
checked for correctness against known correct answers.
Sum of wall-clock time taken to execute the test strings.
"""
-
+
# these need to be class variables, not object variables
test_cases = []
seed_regex = None
time = True
q = Queue()
pstartup, prunner = None, None
-
+
def __init__(self):
# Initialise base fitness function class.
super().__init__()
-
+
if params['MULTICORE']:
s = "fitness.regex.RegexEval.RegexEval\n" \
"Error: multi-core evaluation cannot be used with RegexEval " \
"fitness function, as this fitness function already manages " \
"processes using the multiprocessing library."
raise Exception(s)
-
+
def call_fitness(self, individual, q):
"""
This method is called when this class is instantiated with an
@@ -64,7 +65,7 @@ def call_fitness(self, individual, q):
# print(e)
# traceback.print_exc()
q.put(RegexEval.default_fitness)
-
+
def calculate_fitness(self, eval_results):
"""
Sum the functionality error with time (and any other fitness penalties
@@ -73,7 +74,7 @@ def calculate_fitness(self, eval_results):
:param eval_results:
:return:
"""
-
+
result_error = 0
time_sum = 0.0
for a_result in eval_results:
@@ -89,7 +90,7 @@ def test_regex(self, compiled_regex):
:param compiled_regex:
:return:
"""
-
+
results = list()
testing_iterations = 1
@@ -111,18 +112,18 @@ def evaluate(self, ind, **kwargs):
# We can't initialise the seed regex when we initialise the
# fitness function as the representation.individual.Individual
# class has not yet been instantiated.
-
+
RegexEval.seed_regex = params['SEED_INDIVIDUALS'][0]
-
+
RegexEval.test_cases = TestGen.generate_test_suite(
RegexEval.seed_regex.phenotype)
-
+
if len(RegexEval.test_cases) == 0:
s = "fitness.regex.RegexEval.RegexEval\n" \
"Error: no regex test cases found! " \
" Please add at least one passing regex test string."
raise Exception(s)
-
+
if RegexEval.pstartup is None:
RegexEval.pstartup = Process(target=self.call_fitness,
name="self.call_fitness")
@@ -132,7 +133,7 @@ def evaluate(self, ind, **kwargs):
RegexEval.prunner = RegexEval.pstartup
RegexEval.pstartup = Process(target=self.call_fitness,
name="self.call_fitness")
-
+
# Set one second time limit for running thread.
self.prunner.join(1)
@@ -146,8 +147,8 @@ def evaluate(self, ind, **kwargs):
# Count individual as a runtime error.
stats['runtime_error'] += 1
-
+
return self.default_fitness
-
+
else:
return self.q.get()
diff --git a/src/fitness/regex/testing/RegexTest.py b/src/fitness/regex/testing/RegexTest.py
index 1353fd41..1c67f0f8 100644
--- a/src/fitness/regex/testing/RegexTest.py
+++ b/src/fitness/regex/testing/RegexTest.py
@@ -1,4 +1,3 @@
-
class RegexTest:
"""
Class which contains a test string and matches
@@ -37,7 +36,7 @@ def find_missing_range(self, a_known_match, match_ranges):
:param match_ranges:
:return:
"""
-
+
start = a_known_match.start()
end = a_known_match.end()
missing = end - start
diff --git a/src/fitness/regex/testing/RegexTestGenerator.py b/src/fitness/regex/testing/RegexTestGenerator.py
index c879e184..7b4fd568 100644
--- a/src/fitness/regex/testing/RegexTestGenerator.py
+++ b/src/fitness/regex/testing/RegexTestGenerator.py
@@ -1,7 +1,7 @@
import re
-from fitness.regex.testing.RegexTimer import time_regex_test_case
from fitness.regex.testing.RegexTest import RegexTest
+from fitness.regex.testing.RegexTimer import time_regex_test_case
def generate_equivalence_test_suite_replacement(a_match, compiled_regex):
@@ -25,10 +25,10 @@ def generate_equivalence_test_suite_replacement(a_match, compiled_regex):
if len(a_match.matches) > 0:
for i in range(0, len(a_match.search_string)):
for char in [a for a in range(ord('0'), ord('9'))] + \
- [ord('a'), ord('Z')]:
+ [ord('a'), ord('Z')]:
new_search_string = a_match.search_string[:i] + \
- chr(char) + \
- a_match.search_string[i + 1:]
+ chr(char) + \
+ a_match.search_string[i + 1:]
a_test_case_string = RegexTest(new_search_string)
vals = time_regex_test_case(compiled_regex, a_test_case_string,
1)
@@ -92,7 +92,7 @@ def generate_test_suite(regex_string):
:param regex_string:
:return:
"""
-
+
# do some test generation
# find a string which the regex is able to match against
# find the minimal variant of this string which does not match
@@ -165,7 +165,7 @@ def generate_test_suite(regex_string):
" ::= A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z",
"A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z",
]
-
+
compiled_regex = re.compile(regex_string)
test_cases = []
for test_string in known_test_strings:
@@ -189,7 +189,7 @@ def add_re_match_to_test(matches, passing_test_string):
:param passing_test_string:
:return:
"""
-
+
for a_match in matches: # this vals[1] business is not good
passing_test_string.matches.append(a_match)
@@ -203,7 +203,7 @@ def generate_tests_if_string_match(compiled_regex, test_string):
:param test_string:
:return:
"""
-
+
test_cases = []
a_test_candidate = RegexTest(test_string)
vals = time_regex_test_case(compiled_regex, a_test_candidate, 1)
diff --git a/src/fitness/regex/testing/RegexTimer.py b/src/fitness/regex/testing/RegexTimer.py
index f50260b2..5b3c3ee7 100644
--- a/src/fitness/regex/testing/RegexTimer.py
+++ b/src/fitness/regex/testing/RegexTimer.py
@@ -23,7 +23,7 @@ def time_regex_test_case(compiled_regex, test_case, iterations):
:param iterations:
:return:
"""
-
+
try:
repeats = 10
search_string = test_case.search_string
@@ -33,21 +33,21 @@ def wrap():
# force (convert to list evals result here)
# https://swizec.com/blog/python-and-lazy-evaluation/swizec/5148
return list(compiled_regex.finditer(search_string))
-
+
t = timeit.Timer(wrap)
repeat_iterations = t.repeat(repeat=repeats, number=iterations)
best_run = list(repeat_iterations[0])
-
+
for repeated_timeit in repeat_iterations:
if best_run[0] > list(repeated_timeit)[0]:
best_run = list(repeated_timeit)
-
+
return_vals = list(best_run)
return_vals.append(iterations)
return_vals.append(test_case)
-
+
except:
traceback.print_exc()
-
+
return return_vals
diff --git a/src/fitness/sequence_match.py b/src/fitness/sequence_match.py
index 02d1d63d..84297bc9 100644
--- a/src/fitness/sequence_match.py
+++ b/src/fitness/sequence_match.py
@@ -1,9 +1,8 @@
-from algorithm.parameters import params
-from fitness.base_ff_classes.base_ff import base_ff
-
+import dtw # https://pypi.python.org/pypi/dtw
import editdistance # https://pypi.python.org/pypi/editdistance
import lzstring # https://pypi.python.org/pypi/lzstring/
-import dtw # https://pypi.python.org/pypi/dtw
+from algorithm.parameters import params
+from fitness.base_ff_classes.base_ff import base_ff
"""
@@ -36,8 +35,8 @@ def succ(n, maxv=6):
:param maxv:
:return:
"""
-
- return min(n+1, maxv)
+
+ return min(n + 1, maxv)
def pred(n, minv=0):
@@ -48,8 +47,8 @@ def pred(n, minv=0):
:param minv:
:return:
"""
-
- return max(n-1, minv)
+
+ return max(n - 1, minv)
def truncate(n, g):
@@ -61,7 +60,7 @@ def truncate(n, g):
:param g:
:return:
"""
-
+
for i in range(n):
yield next(g)
@@ -74,7 +73,7 @@ def dist(t0, x0):
:param x0:
:return:
"""
-
+
return abs(t0 - x0)
@@ -86,11 +85,11 @@ def dtw_dist(s, t):
:param t:
:return:
"""
-
+
s = list(map(int, s))
t = list(map(int, t))
d, M, C, path = dtw.dtw(s, t, dist)
-
+
return d
@@ -105,7 +104,7 @@ def lev_dist(s, t):
:param t:
:return:
"""
-
+
return editdistance.eval(s, t) / len(s)
@@ -117,7 +116,7 @@ def compress(s):
:param s:
:return:
"""
-
+
s = ''.join(map(str, s))
return lzstring.LZString().compress(s)
@@ -130,7 +129,7 @@ def compressibility(s):
:param s:
:return:
"""
-
+
return 1 - len(compress(s)) / len(s)
@@ -143,26 +142,26 @@ def proglen(s):
:param s: A string of a program phenotype.
:return: The length of the program divided by 100.
"""
-
+
return len(s) / 100.0
class sequence_match(base_ff):
-
+
def __init__(self):
"""
Initilise class instance
"""
# Initialise base fitness function class.
super().__init__()
-
+
# --target will be a sequence such as (0, 5, 0, 5)
self.target = eval(params['TARGET'])
-
+
# we assume --extra_parameters is a comma-separated kv sequence, eg:
# "alpha=0.5, beta=0.5, gamma=0.5"
# which we can pass to the dict() constructor
- extra_fit_params = eval("dict("+params['EXTRA_PARAMETERS']+")")
+ extra_fit_params = eval("dict(" + params['EXTRA_PARAMETERS'] + ")")
self.alpha = extra_fit_params['alpha']
self.beta = extra_fit_params['beta']
self.gamma = extra_fit_params['gamma']
@@ -176,7 +175,7 @@ def evaluate(self, ind, **kwargs):
:param ind:
:return:
"""
-
+
p, d = ind.phenotype, {'pred': pred, 'succ': succ}
exec(p, d)
@@ -185,7 +184,7 @@ def evaluate(self, ind, **kwargs):
# Truncate the generator and convert to list
s = list(truncate(len(self.target), s))
-
+
# Set target
t = self.target
@@ -219,7 +218,7 @@ def evaluate(self, ind, **kwargs):
else:
compressibility_v = 0.0
length_v = self.beta * proglen_v + (1 - self.beta) * \
- compressibility_v
+ compressibility_v
else:
length_v = 0.0
diff --git a/src/fitness/string_match.py b/src/fitness/string_match.py
index 2125a8b7..fc163a7e 100755
--- a/src/fitness/string_match.py
+++ b/src/fitness/string_match.py
@@ -11,7 +11,7 @@ class string_match(base_ff):
def __init__(self):
# Initialise base fitness function class.
super().__init__()
-
+
# Set target string.
self.target = params['TARGET']
diff --git a/src/fitness/supervised_learning/boolean_problem.py b/src/fitness/supervised_learning/boolean_problem.py
index e2873a57..2dee1c1b 100644
--- a/src/fitness/supervised_learning/boolean_problem.py
+++ b/src/fitness/supervised_learning/boolean_problem.py
@@ -1,11 +1,11 @@
-from fitness.supervised_learning.supervised_learning import supervised_learning
+import itertools
+import random
+import numpy as np
from algorithm.parameters import params
+from fitness.supervised_learning.supervised_learning import supervised_learning
from utilities.fitness.error_metric import Hamming_error
-import itertools
-import random
-import numpy as np
class boolean_problem(supervised_learning):
"""Fitness function for Boolean problems. We specialise the
@@ -32,10 +32,10 @@ def __init__(self):
# would have done.
target_name = params['EXTRA_PARAMETERS'][0]
-
+
# may be needed if the grammar uses GE_RANGE:dataset_n_vars
n = self.n_vars = int(params['EXTRA_PARAMETERS'][1])
-
+
# Set error metric if it's not set already.
if params['ERROR_METRIC'] is None:
params['ERROR_METRIC'] = Hamming_error
@@ -50,7 +50,7 @@ def __init__(self):
# generate all input combinations for n variables, to become
# the fitness cases
- Ls = [[False,True] for i in range(n)]
+ Ls = [[False, True] for i in range(n)]
X = np.array(list(itertools.product(*Ls)))
# evaluate the target function at the fitness cases
self.training_exp = np.array([target(xi) for xi in X])
@@ -62,10 +62,11 @@ def __init__(self):
# Some target functions. Each just accepts a single instance, eg
# nparity([False, False, True]) -> True
-
+
def boolean_true(x):
return True
+
def comparator(x):
"""Comparator function: input consists of two n-bit numbers. Output is
0 if the first is larger or equal, or 1 if the second is larger."""
@@ -74,38 +75,48 @@ def comparator(x):
# no need to convert from binary. just use list comparison
return x[:n] < x[n:]
+
def multiplexer(x):
"""Multiplexer: a address bits and 2^a data bits. n = a + 2^a. Output
the value of the data bit addressed by the address bits.
"""
- if len(x) == 1 + 2 ** 1 : a = 1 # 2
- elif len(x) == 2 + 2 ** 2 : a = 2 # 6
- elif len(x) == 3 + 2 ** 3 : a = 3 # 11
- elif len(x) == 4 + 2 ** 4 : a = 4 # 20
- elif len(x) == 5 + 2 ** 5 : a = 5 # 37
- elif len(x) == 6 + 2 ** 6 : a = 6 # 70
- else: raise ValueError(x)
- addr = binlist2int(x[:a]) # get address bits, convert to int
- return x[a + addr] # which data bit? offset by a
+ if len(x) == 1 + 2 ** 1:
+ a = 1 # 2
+ elif len(x) == 2 + 2 ** 2:
+ a = 2 # 6
+ elif len(x) == 3 + 2 ** 3:
+ a = 3 # 11
+ elif len(x) == 4 + 2 ** 4:
+ a = 4 # 20
+ elif len(x) == 5 + 2 ** 5:
+ a = 5 # 37
+ elif len(x) == 6 + 2 ** 6:
+ a = 6 # 70
+ else:
+ raise ValueError(x)
+ addr = binlist2int(x[:a]) # get address bits, convert to int
+ return x[a + addr] # which data bit? offset by a
+
def nparity(x):
'Parity function of any number of input variables'
return x.sum() % 2 == 1
+
# special target function: random truth table
def make_random_boolean_fn(n):
"""Make a random Boolean function of n variables."""
outputs = [random.choice([False, True])
- for i in range(2**n)]
+ for i in range(2 ** n)]
+
def f(x):
return outputs[binlist2int(x)]
- return f
+ return f
-### Helper function
+# Helper function
def binlist2int(x):
"""Convert a list of binary digits to integer"""
return int("".join(map(str, map(int, x))), 2)
-
diff --git a/src/fitness/supervised_learning/classification.py b/src/fitness/supervised_learning/classification.py
index 906d6497..2ee068e1 100755
--- a/src/fitness/supervised_learning/classification.py
+++ b/src/fitness/supervised_learning/classification.py
@@ -1,6 +1,5 @@
-from fitness.supervised_learning.supervised_learning import supervised_learning
-
from algorithm.parameters import params
+from fitness.supervised_learning.supervised_learning import supervised_learning
from utilities.fitness.error_metric import f1_score
diff --git a/src/fitness/supervised_learning/if_else_classifier.py b/src/fitness/supervised_learning/if_else_classifier.py
index 2a474609..0853d3b5 100644
--- a/src/fitness/supervised_learning/if_else_classifier.py
+++ b/src/fitness/supervised_learning/if_else_classifier.py
@@ -1,11 +1,13 @@
-from fitness.supervised_learning.supervised_learning import supervised_learning
+import itertools
+import numpy as np
from algorithm.parameters import params
+from fitness.supervised_learning.supervised_learning import supervised_learning
from utilities.fitness.error_metric import Hamming_error
-import itertools
-import random
-import numpy as np
+
+# import random
+
class if_else_classifier(supervised_learning):
"""Fitness function for if-else classifier problems. We
@@ -52,7 +54,7 @@ def __init__(self):
n = self.n_vars = int(params['EXTRA_PARAMETERS'][0])
n_is = self.n_is = int(params['EXTRA_PARAMETERS'][1])
n_os = self.n_os = int(params['EXTRA_PARAMETERS'][2])
-
+
# Set error metric if it's not set already.
if params['ERROR_METRIC'] is None:
params['ERROR_METRIC'] = Hamming_error
@@ -79,4 +81,5 @@ def __init__(self):
def target_classifier(n_vars, n_is, n_os):
def target(x):
return ((x[0] + x[1]) % n_os) + 1
+
return target
diff --git a/src/fitness/supervised_learning/regression.py b/src/fitness/supervised_learning/regression.py
index 63bc331b..6588fb9b 100755
--- a/src/fitness/supervised_learning/regression.py
+++ b/src/fitness/supervised_learning/regression.py
@@ -1,6 +1,5 @@
-from fitness.supervised_learning.supervised_learning import supervised_learning
-
from algorithm.parameters import params
+from fitness.supervised_learning.supervised_learning import supervised_learning
from utilities.fitness.error_metric import rmse
diff --git a/src/fitness/supervised_learning/regression_random_polynomial.py b/src/fitness/supervised_learning/regression_random_polynomial.py
index 2d372893..743f4767 100644
--- a/src/fitness/supervised_learning/regression_random_polynomial.py
+++ b/src/fitness/supervised_learning/regression_random_polynomial.py
@@ -1,11 +1,11 @@
-from fitness.supervised_learning.regression import regression
+import itertools
+import random
+import numpy as np
from algorithm.parameters import params
+from fitness.supervised_learning.regression import regression
from utilities.fitness.error_metric import rmse
-import itertools
-import random
-import numpy as np
class regression_random_polynomial(regression):
"""Fitness function for regression of random polynomials. We
@@ -39,9 +39,9 @@ def __init__(self):
# would have done.
degree, n_vars, n_samples = map(int, params['EXTRA_PARAMETERS'])
-
+
# may be needed if the grammar uses GE_RANGE:dataset_n_vars
- self.n_vars = n_vars
+ self.n_vars = n_vars
# Set error metric if it's not set already.
if params['ERROR_METRIC'] is None:
@@ -55,7 +55,7 @@ def __init__(self):
# generate a set of fitness cases for training
self.training_in = np.random.random((n_vars, n_samples))
self.training_exp = p.eval(self.training_in)
-
+
# if we want a separate test set, generate a set of fitness
# cases for it
if params['DATASET_TEST']:
@@ -65,10 +65,9 @@ def __init__(self):
class Polynomial:
-
"""A polynomial of a given degree and a given number of variables,
with one coefficient for each term."""
-
+
def __init__(self, degree, n_vars, coefs):
"""Constructor for the case where we already have coefficients."""
self.degree = degree
@@ -78,7 +77,8 @@ def __init__(self, degree, n_vars, coefs):
@classmethod
def from_random(cls, degree, n_vars):
"""Constructor for the case where we want random coefficients."""
- coefs = [2*(random.random() - 0.5) for i in Polynomial.terms(degree, n_vars)]
+ coefs = [2 * (random.random() - 0.5) for i in
+ Polynomial.terms(degree, n_vars)]
p = Polynomial(degree, n_vars, coefs)
return p
@@ -93,24 +93,28 @@ def terms(degree, n_vars):
def eval(self, x):
"""Evaluate the polynomial at a set of points x."""
assert x.shape[0] == self.n_vars
- result = np.zeros(x.shape[1]) # same length as a column of x
- for coef, pows in zip(self.coefs, self.terms(self.degree, self.n_vars)):
+ result = np.zeros(x.shape[1]) # same length as a column of x
+ for coef, pows in zip(self.coefs,
+ self.terms(self.degree, self.n_vars)):
tmp = np.ones(x.shape[1])
for (xi, pow) in zip(x, pows):
tmp *= (xi ** pow)
tmp *= coef
result += tmp
return result
-
+
def __str__(self):
"""Pretty-print a polynomial, rounding the coefficients."""
+
def s(pows):
if sum(pows):
return "*" + "*".join("x[%d]**%d" % (i, powi)
- for i, powi in enumerate(pows) if powi > 0)
+ for i, powi in enumerate(pows) if
+ powi > 0)
else:
- return "" # this term is a const so the coef on its own is enough
+ return "" # this term is a const so the coef on its own is enough
+
return " + ".join("%.3f%s" % (coef, s(pows))
for (coef, pows) in
- zip(self.coefs, Polynomial.terms(self.degree, self.n_vars)))
-
+ zip(self.coefs,
+ Polynomial.terms(self.degree, self.n_vars)))
diff --git a/src/fitness/supervised_learning/supervised_learning.py b/src/fitness/supervised_learning/supervised_learning.py
index 7243a501..5d266d5c 100644
--- a/src/fitness/supervised_learning/supervised_learning.py
+++ b/src/fitness/supervised_learning/supervised_learning.py
@@ -1,4 +1,5 @@
import numpy as np
+
np.seterr(all="raise")
from algorithm.parameters import params
diff --git a/src/operators/crossover.py b/src/operators/crossover.py
index 743996e4..f317bcf1 100755
--- a/src/operators/crossover.py
+++ b/src/operators/crossover.py
@@ -1,8 +1,9 @@
-from random import randint, random, sample, choice
+from random import choice, randint, random, sample
from algorithm.parameters import params
from representation import individual
-from representation.latent_tree import latent_tree_crossover, latent_tree_repair
+from representation.latent_tree import latent_tree_crossover, \
+ latent_tree_repair
from utilities.representation.check_methods import check_ind
@@ -20,21 +21,21 @@ def crossover(parents):
# Initialise an empty population.
cross_pop = []
-
+
while len(cross_pop) < params['GENERATION_SIZE']:
-
+
# Randomly choose two parents from the parent population.
inds_in = sample(parents, 2)
# Perform crossover on chosen parents.
inds_out = crossover_inds(inds_in[0], inds_in[1])
-
+
if inds_out is None:
# Crossover failed.
pass
-
+
else:
-
+
# Extend the new population.
cross_pop.extend(inds_out)
@@ -96,7 +97,7 @@ def variable_onepoint(p_0, p_1):
# Uniformly generate crossover points.
max_p_0, max_p_1 = get_max_genome_index(p_0, p_1)
-
+
# Select unique points on each genome for crossover to occur.
pt_0, pt_1 = randint(1, max_p_0), randint(1, max_p_1)
@@ -126,27 +127,27 @@ def fixed_onepoint(p_0, p_1):
:param p_1: Parent 1
:return: A list of crossed-over individuals.
"""
-
+
# Get the chromosomes.
genome_0, genome_1 = p_0.genome, p_1.genome
# Uniformly generate crossover points.
max_p_0, max_p_1 = get_max_genome_index(p_0, p_1)
-
+
# Select the same point on both genomes for crossover to occur.
pt = randint(1, min(max_p_0, max_p_1))
-
+
# Make new chromosomes by crossover: these slices perform copies.
if random() < params['CROSSOVER_PROBABILITY']:
c_0 = genome_0[:pt] + genome_1[pt:]
c_1 = genome_1[:pt] + genome_0[pt:]
else:
c_0, c_1 = genome_0[:], genome_1[:]
-
+
# Put the new chromosomes into new individuals.
ind_0 = individual.Individual(c_0, None)
ind_1 = individual.Individual(c_1, None)
-
+
return [ind_0, ind_1]
@@ -162,7 +163,7 @@ def fixed_twopoint(p_0, p_1):
:param p_1: Parent 1
:return: A list of crossed-over individuals.
"""
-
+
genome_0, genome_1 = p_0.genome, p_1.genome
# Uniformly generate crossover points.
@@ -171,7 +172,7 @@ def fixed_twopoint(p_0, p_1):
# Select the same points on both genomes for crossover to occur.
a, b = randint(1, max_p_0), randint(1, max_p_1)
pt_0, pt_1 = min([a, b]), max([a, b])
-
+
# Make new chromosomes by crossover: these slices perform copies.
if random() < params['CROSSOVER_PROBABILITY']:
c_0 = genome_0[:pt_0] + genome_1[pt_0:pt_1] + genome_0[pt_1:]
@@ -182,7 +183,7 @@ def fixed_twopoint(p_0, p_1):
# Put the new chromosomes into new individuals.
ind_0 = individual.Individual(c_0, None)
ind_1 = individual.Individual(c_1, None)
-
+
return [ind_0, ind_1]
@@ -198,29 +199,29 @@ def variable_twopoint(p_0, p_1):
:param p_1: Parent 1
:return: A list of crossed-over individuals.
"""
-
+
genome_0, genome_1 = p_0.genome, p_1.genome
-
+
# Uniformly generate crossover points.
max_p_0, max_p_1 = get_max_genome_index(p_0, p_1)
-
+
# Select the same points on both genomes for crossover to occur.
a_0, b_0 = randint(1, max_p_0), randint(1, max_p_1)
a_1, b_1 = randint(1, max_p_0), randint(1, max_p_1)
pt_0, pt_1 = min([a_0, b_0]), max([a_0, b_0])
pt_2, pt_3 = min([a_1, b_1]), max([a_1, b_1])
-
+
# Make new chromosomes by crossover: these slices perform copies.
if random() < params['CROSSOVER_PROBABILITY']:
c_0 = genome_0[:pt_0] + genome_1[pt_2:pt_3] + genome_0[pt_1:]
c_1 = genome_1[:pt_2] + genome_0[pt_0:pt_1] + genome_1[pt_3:]
else:
c_0, c_1 = genome_0[:], genome_1[:]
-
+
# Put the new chromosomes into new individuals.
ind_0 = individual.Individual(c_0, None)
ind_1 = individual.Individual(c_1, None)
-
+
return [ind_0, ind_1]
@@ -249,11 +250,11 @@ def do_crossover(tree0, tree1, shared_nodes):
:return: The new derivation trees after subtree crossover has been
performed.
"""
-
+
# Randomly choose a non-terminal from the set of permissible
# intersecting non-terminals.
crossover_choice = choice(shared_nodes)
-
+
# Find all nodes in both trees that match the chosen crossover node.
nodes_0 = tree0.get_target_nodes([], target=[crossover_choice])
nodes_1 = tree1.get_target_nodes([], target=[crossover_choice])
@@ -264,12 +265,12 @@ def do_crossover(tree0, tree1, shared_nodes):
# Check the parents of both chosen subtrees.
p0 = t0.parent
p1 = t1.parent
-
+
if not p0 and not p1:
# Crossover is between the entire tree of both tree0 and tree1.
-
+
return t1, t0
-
+
elif not p0:
# Only t0 is the entire of tree0.
tree0 = t1
@@ -283,7 +284,7 @@ def do_crossover(tree0, tree1, shared_nodes):
# individual, it has no parent.
t0.parent = p1
t1.parent = None
-
+
elif not p1:
# Only t1 is the entire of tree1.
tree1 = t0
@@ -297,24 +298,24 @@ def do_crossover(tree0, tree1, shared_nodes):
# individual, it has no parent.
t1.parent = p0
t0.parent = None
-
+
else:
# The crossover node for both trees is not the entire tree.
-
+
# For the parent nodes of the original subtrees, get the indexes
# of the original subtrees.
i0 = p0.children.index(t0)
i1 = p1.children.index(t1)
-
+
# Swap over the subtrees between parents.
p0.children[i0] = t1
p1.children[i1] = t0
-
+
# Set the parents of the crossed-over subtrees as their new
# parents.
t1.parent = p0
t0.parent = p1
-
+
return tree0, tree1
def intersect(l0, l1):
@@ -328,29 +329,29 @@ def intersect(l0, l1):
:return: The sorted list of all non-terminal nodes that are in both
derivation trees.
"""
-
+
# Find all intersecting elements of both sets l0 and l1.
shared_nodes = l0.intersection(l1)
-
+
# Find only the non-terminals present in the intersecting set of
# labels.
shared_nodes = [i for i in shared_nodes if i in params[
'BNF_GRAMMAR'].non_terminals]
-
+
return sorted(shared_nodes)
if random() > params['CROSSOVER_PROBABILITY']:
# Crossover is not to be performed, return entire individuals.
ind0 = p_1
ind1 = p_0
-
+
else:
# Crossover is to be performed.
-
+
if p_0.invalid:
# The individual is invalid.
tail_0 = []
-
+
else:
# Save tail of each genome.
tail_0 = p_0.genome[p_0.used_codons:]
@@ -362,7 +363,7 @@ def intersect(l0, l1):
else:
# Save tail of each genome.
tail_1 = p_1.genome[p_1.used_codons:]
-
+
# Get the set of labels of non terminals for each tree.
labels1 = p_0.tree.get_node_labels(set())
labels2 = p_1.tree.get_node_labels(set())
@@ -374,11 +375,11 @@ def intersect(l0, l1):
# There are overlapping NTs, cross over parts of trees.
ret_tree0, ret_tree1 = do_crossover(p_0.tree, p_1.tree,
shared_nodes)
-
+
else:
# There are no overlapping NTs, cross over entire trees.
ret_tree0, ret_tree1 = p_1.tree, p_0.tree
-
+
# Initialise new individuals using the new trees.
ind0 = individual.Individual(None, ret_tree0)
ind1 = individual.Individual(None, ret_tree1)
@@ -404,25 +405,25 @@ def get_max_genome_index(ind_0, ind_1):
if params['WITHIN_USED']:
# Get used codons range.
-
+
if ind_0.invalid:
# ind_0 is invalid. Default to entire genome.
max_p_0 = len(ind_0.genome)
-
+
else:
max_p_0 = ind_0.used_codons
-
+
if ind_1.invalid:
# ind_1 is invalid. Default to entire genome.
max_p_1 = len(ind_1.genome)
-
+
else:
max_p_1 = ind_1.used_codons
-
+
else:
# Get length of entire genome.
max_p_0, max_p_1 = len(ind_0.genome), len(ind_1.genome)
-
+
return max_p_0, max_p_1
@@ -455,11 +456,11 @@ def LTGE_crossover(p_0, p_1):
# each key is the length of a path from root
ind_0.depth = max(len(k) for k in g_0)
ind_1.depth = max(len(k) for k in g_1)
-
+
# in LTGE there are no invalid individuals
ind_0.invalid = False
ind_1.invalid = False
-
+
return [ind_0, ind_1]
diff --git a/src/operators/initialisation.py b/src/operators/initialisation.py
index 5cef0c99..5ebed043 100644
--- a/src/operators/initialisation.py
+++ b/src/operators/initialisation.py
@@ -1,13 +1,13 @@
from math import floor
-from os import path, getcwd, listdir
-from random import shuffle, randint
+from os import getcwd, listdir, path
+from random import randint, shuffle
from algorithm.parameters import params
from representation import individual
from representation.derivation import generate_tree, pi_grow
from representation.individual import Individual
-from representation.tree import Tree
from representation.latent_tree import latent_tree_random_ind
+from representation.tree import Tree
from scripts import GE_LR_parser
from utilities.representation.python_filter import python_filter
@@ -34,7 +34,7 @@ def initialisation(size):
individuals.extend(params['SEED_INDIVIDUALS'])
return individuals
-
+
def sample_genome():
"""
@@ -65,10 +65,10 @@ def uniform_tree(size):
:param size: The size of the required population.
:return: A full population composed of randomly generated individuals.
"""
-
+
return [generate_ind_tree(params['MAX_TREE_DEPTH'],
"random") for _ in range(size)]
-
+
def seed_individuals(size):
"""
@@ -78,41 +78,41 @@ def seed_individuals(size):
:param size: The size of the required population.
:return: A full population composed of the seeded individuals.
"""
-
+
# Get total number of seed inds.
no_seeds = len(params['SEED_INDIVIDUALS'])
-
+
# Initialise empty population.
individuals = []
-
+
if no_seeds > 0:
# A list of individuals has been specified as the seed.
-
+
# Divide requested population size by the number of seeds.
- num_per_seed = floor(size/no_seeds)
-
+ num_per_seed = floor(size / no_seeds)
+
for ind in params['SEED_INDIVIDUALS']:
-
+
if not isinstance(ind, individual.Individual):
# The seed object is not a PonyGE individual.
s = "operators.initialisation.seed_individuals\n" \
"Error: SEED_INDIVIDUALS instance is not a PonyGE " \
"individual."
raise Exception(s)
-
+
else:
# Generate num_per_seed identical seed individuals.
individuals.extend([ind.deep_copy() for _ in
range(num_per_seed)])
-
+
return individuals
-
+
else:
# No seed individual specified.
s = "operators.initialisation.seed_individuals\n" \
"Error: No seed individual specified for seed initialisation."
raise Exception(s)
-
+
def rhh(size):
"""
@@ -125,7 +125,7 @@ def rhh(size):
# Calculate the range of depths to ramp individuals from.
depths = range(params['BNF_GRAMMAR'].min_ramp + 1,
- params['MAX_INIT_TREE_DEPTH']+1)
+ params['MAX_INIT_TREE_DEPTH'] + 1)
population = []
if size < 2:
@@ -152,21 +152,20 @@ def rhh(size):
"RHH initialisation requires an even population size. "
"Incrementing population size by 1.")
- if size/2 < len(depths):
+ if size / 2 < len(depths):
# The population size is too small to fully cover all ramping
# depths. Only ramp to the number of depths we can reach.
- depths = depths[:int(size/2)]
+ depths = depths[:int(size / 2)]
# Calculate how many individuals are to be generated by each
# initialisation method.
- times = int(floor((size/2)/len(depths)))
- remainder = int(size/2 - (times * len(depths)))
+ times = int(floor((size / 2) / len(depths)))
+ remainder = int(size / 2 - (times * len(depths)))
# Iterate over depths.
for depth in depths:
# Iterate over number of required individuals per depth.
for i in range(times):
-
# Generate individual using "Grow"
ind = generate_ind_tree(depth, "random")
@@ -213,7 +212,7 @@ def PI_grow(size):
# Calculate the range of depths to ramp individuals from.
depths = range(params['BNF_GRAMMAR'].min_ramp + 1,
- params['MAX_INIT_TREE_DEPTH']+1)
+ params['MAX_INIT_TREE_DEPTH'] + 1)
population = []
if size < 2:
@@ -240,14 +239,13 @@ def PI_grow(size):
# Calculate how many individuals are to be generated by each
# initialisation method.
- times = int(floor(size/len(depths)))
+ times = int(floor(size / len(depths)))
remainder = int(size - (times * len(depths)))
# Iterate over depths.
for depth in depths:
# Iterate over number of required individuals per depth.
for i in range(times):
-
# Generate individual using "Grow"
ind = generate_PI_ind_tree(depth)
@@ -361,24 +359,24 @@ def load_population(target):
if not path.isdir(path_1):
# Seeds folder does not exist.
-
+
s = "scripts.seed_PonyGE2.load_population\n" \
"Error: `seeds` folder does not exist in root directory."
raise Exception(s)
-
+
path_2 = path.join(path_1, target)
if not path.isdir(path_2):
# Target folder does not exist.
-
+
s = "scripts.seed_PonyGE2.load_population\n" \
"Error: target folder " + target + \
" does not exist in seeds directory."
raise Exception(s)
-
+
# Get list of all target individuals in the target folder.
target_inds = [i for i in listdir(path_2) if i.endswith(".txt")]
-
+
# Initialize empty list for seed individuals.
seed_inds = []
@@ -393,19 +391,19 @@ def load_population(target):
# Open file.
with open(file_name, "r") as f:
-
+
# Read file.
raw_content = f.read()
-
+
# Read file.
content = raw_content.split("\n")
-
+
# Check if genotype is already saved in file.
if "Genotype:" in content:
-
+
# Get index location of genotype.
gen_idx = content.index("Genotype:") + 1
-
+
# Get the genotype.
try:
genotype = eval(content[gen_idx])
@@ -414,18 +412,18 @@ def load_population(target):
"Error: Genotype from file " + file_name + \
" not recognized: " + content[gen_idx]
raise Exception(s)
-
+
# Check if phenotype (target string) is already saved in file.
if "Phenotype:" in content:
-
+
# Get index location of genotype.
phen_idx = content.index("Phenotype:") + 1
-
+
# Get the phenotype.
phenotype = content[phen_idx]
-
+
# TODO: Current phenotype is read in as single-line only. Split is performed on "\n", meaning phenotypes that span multiple lines will not be parsed correctly. This must be fixed in later editions.
-
+
elif "Genotype:" not in content:
# There is no explicit genotype or phenotype in the target
# file, read in entire file as phenotype.
@@ -434,7 +432,7 @@ def load_population(target):
if genotype:
# Generate individual from genome.
ind = Individual(genotype, None)
-
+
if phenotype and ind.phenotype != phenotype:
s = "scripts.seed_PonyGE2.load_population\n" \
"Error: Specified genotype from file " + file_name + \
@@ -442,14 +440,14 @@ def load_population(target):
"grammar to ensure all is correct: " + \
params['GRAMMAR_FILE']
raise Exception(s)
-
+
else:
# Set target for GE LR Parser.
params['REVERSE_MAPPING_TARGET'] = phenotype
-
+
# Parse target phenotype.
ind = GE_LR_parser.main()
-
+
# Add new ind to the list of seed individuals.
seed_inds.append(ind)
@@ -461,7 +459,6 @@ def LTGE_initialisation(size):
pop = []
for _ in range(size):
-
# Random genotype
g, ph = latent_tree_random_ind(params['BNF_GRAMMAR'],
params['MAX_TREE_DEPTH'])
@@ -483,6 +480,7 @@ def LTGE_initialisation(size):
pop.append(ind)
return pop
+
# Set ramping attributes for ramped initialisers.
PI_grow.ramping = True
rhh.ramping = True
diff --git a/src/operators/mutation.py b/src/operators/mutation.py
index 8080cfec..1f675e5e 100755
--- a/src/operators/mutation.py
+++ b/src/operators/mutation.py
@@ -1,4 +1,4 @@
-from random import randint, random, choice
+from random import choice, randint, random
from algorithm.parameters import params
from representation import individual
@@ -84,7 +84,7 @@ def int_flip_per_codon(ind):
else:
# Default mutation events per individual is 1. Raising this number
# will influence the mutation probability for each codon.
- p_mut = params['MUTATION_EVENTS']/eff_length
+ p_mut = params['MUTATION_EVENTS'] / eff_length
# Mutation probability works per-codon over the portion of the
# genome as defined by the within_used flag.
@@ -117,7 +117,7 @@ def int_flip_per_ind(ind):
return ind
for _ in range(params['MUTATION_EVENTS']):
- idx = randint(0, eff_length-1)
+ idx = randint(0, eff_length - 1)
ind.genome[idx] = randint(0, params['CODON_SIZE'])
# Re-build a new individual with the newly mutated genetic information.
@@ -148,7 +148,7 @@ def subtree_mutate(ind_tree):
# Find the list of nodes we can mutate from.
targets = ind_tree.get_target_nodes([], target=params[
- 'BNF_GRAMMAR'].non_terminals)
+ 'BNF_GRAMMAR'].non_terminals)
# Pick a node.
new_tree = choice(targets)
@@ -216,16 +216,16 @@ def get_effective_length(ind):
def LTGE_mutation(ind):
"""Mutation in the LTGE representation."""
-
+
# mutate and repair.
- g, ph = latent_tree_repair(latent_tree_mutate(ind.genome),
+ g, ph = latent_tree_repair(latent_tree_mutate(ind.genome),
params['BNF_GRAMMAR'], params['MAX_TREE_DEPTH'])
# wrap up in an Individual and fix up various Individual attributes
ind = individual.Individual(g, None, False)
ind.phenotype = ph
-
+
# number of nodes is the number of decisions in the genome
ind.nodes = ind.used_codons = len(g)
@@ -234,7 +234,7 @@ def LTGE_mutation(ind):
# in LTGE there are no invalid individuals
ind.invalid = False
-
+
return ind
diff --git a/src/operators/replacement.py b/src/operators/replacement.py
index 0538796f..71d7d092 100755
--- a/src/operators/replacement.py
+++ b/src/operators/replacement.py
@@ -1,7 +1,7 @@
-from fitness.evaluation import evaluate_fitness
from algorithm.parameters import params
-from operators.mutation import mutation
+from fitness.evaluation import evaluate_fitness
from operators.crossover import crossover_inds
+from operators.mutation import mutation
from operators.selection import selection
from utilities.algorithm.NSGA2 import compute_pareto_metrics
@@ -35,12 +35,12 @@ def generational(new_pop, old_pop):
# Sort both populations.
old_pop.sort(reverse=True)
new_pop.sort(reverse=True)
-
+
# Append the best ELITE_SIZE individuals from the old population to the
# new population.
for ind in old_pop[:params['ELITE_SIZE']]:
new_pop.insert(0, ind)
-
+
# Return the top POPULATION_SIZE individuals of the new pop, including
# elites.
return new_pop[:params['POPULATION_SIZE']]
@@ -72,13 +72,13 @@ def steady_state(individuals):
ind_counter = 0
while ind_counter < params['POPULATION_SIZE']:
-
+
# Select parents from the original population.
parents = selection(individuals)
# Perform crossover on selected parents.
cross_pop = crossover_inds(parents[0], parents[1])
-
+
if cross_pop is None:
# Crossover failed.
pass
@@ -86,16 +86,16 @@ def steady_state(individuals):
else:
# Mutate the new population.
new_pop = mutation(cross_pop)
-
+
# Evaluate the fitness of the new population.
new_pop = evaluate_fitness(new_pop)
-
+
# Sort the original population
individuals.sort(reverse=True)
-
+
# Combine both populations
total_pop = individuals[:-len(new_pop)] + new_pop
-
+
# Increment the ind counter
ind_counter += params['GENERATION_SIZE']
@@ -130,25 +130,25 @@ def nsga2_replacement(new_pop, old_pop):
while len(temp_pop) < pop_size:
# Populate the replacement population
-
+
if len(pareto.fronts[i]) <= pop_size - len(temp_pop):
temp_pop.extend(pareto.fronts[i])
-
+
else:
# Sort the current pareto front with respect to crowding distance.
pareto.fronts[i] = sorted(pareto.fronts[i],
key=lambda item:
pareto.crowding_distance[item])
-
+
# Get number of individuals to add in temp to achieve the pop_size
diff_size = pop_size - len(temp_pop)
-
+
# Extend the replacement population
temp_pop.extend(pareto.fronts[i][:diff_size])
-
+
# Increment counter.
i += 1
-
+
return temp_pop
diff --git a/src/operators/selection.py b/src/operators/selection.py
index 1ef05bd6..3f4fff86 100755
--- a/src/operators/selection.py
+++ b/src/operators/selection.py
@@ -113,18 +113,18 @@ def pareto_tournament(population, pareto, tournament_size):
:param tournament_size: The size of the tournament.
:return: The selected individuals.
"""
-
+
# Initialise no best solution.
best = None
-
+
# Randomly sample *tournament_size* participants.
participants = sample(population, tournament_size)
-
+
for participant in participants:
if best is None or crowded_comparison_operator(participant, best,
pareto):
best = participant
-
+
return best
diff --git a/src/operators/subtree_parse.py b/src/operators/subtree_parse.py
index 25e157f2..5fe4caf3 100644
--- a/src/operators/subtree_parse.py
+++ b/src/operators/subtree_parse.py
@@ -4,7 +4,7 @@
from algorithm.parameters import params
from representation import individual, tree
-from utilities.representation.check_methods import get_output, generate_codon
+from utilities.representation.check_methods import generate_codon, get_output
from utilities.stats import trackers
@@ -202,7 +202,8 @@ def check_reductions(alt_cs, pre, aft, idx, children):
# Get portion of target string to match.
end_point = aft + len(check)
- target = params['TARGET'][aft:end_point]
+ target = params['TARGET'][
+ aft:end_point]
if target == check:
# The current terminal matches the same
@@ -224,8 +225,8 @@ def check_reductions(alt_cs, pre, aft, idx, children):
# Recurse to find the next piece of
# the puzzle.
check_reductions(alt_cs, pre,
- aft, idx,
- children)
+ aft, idx,
+ children)
elif child[1] == "NT":
# Check to see if there are any snippets
@@ -250,12 +251,14 @@ def check_reductions(alt_cs, pre, aft, idx, children):
# Increment appropriate phenotype
# counter.
- aft_c = aft + str_len[1] - str_len[0]
+ aft_c = aft + str_len[1] - str_len[
+ 0]
# Add to children.
children[child_idx] = [match[2],
- trackers.snippets[
- match[2]]]
+ trackers.snippets[
+ match[
+ 2]]]
if alt_cs:
# Recurse to find the next piece of
@@ -303,8 +306,8 @@ def check_reductions(alt_cs, pre, aft, idx, children):
# Recurse to find the next piece of
# the puzzle.
check_reductions(alt_cs, pre,
- aft, idx,
- children)
+ aft, idx,
+ children)
elif child[1] == "NT":
# Check to see if there are any snippets
@@ -329,12 +332,14 @@ def check_reductions(alt_cs, pre, aft, idx, children):
# Increment appropriate phenotype
# counter.
- pre_c = pre - str_len[1] + str_len[0]
+ pre_c = pre - str_len[1] + str_len[
+ 0]
# Add to children.
children[child_idx] = [match[2],
- trackers.snippets[
- match[2]]]
+ trackers.snippets[
+ match[
+ 2]]]
if alt_cs:
# Recurse to find the next piece of
@@ -359,7 +364,8 @@ def check_reductions(alt_cs, pre, aft, idx, children):
if child_idx > loc:
# This T comes after the original NT.
start_point = aft
- end_point = start_point + len(check)
+ end_point = start_point + len(
+ check)
else:
# This T comes before the original NT.
@@ -398,8 +404,8 @@ def check_reductions(alt_cs, pre, aft, idx, children):
# Recurse to find the next piece of
# the puzzle.
check_reductions(alt_cs, pre,
- aft, idx,
- children)
+ aft, idx,
+ children)
elif child[1] == "NT":
# Check to see if there are any snippets
@@ -409,13 +415,15 @@ def check_reductions(alt_cs, pre, aft, idx, children):
# Get portion of target string to match.
if child_idx > loc:
# This NT comes after the original NT.
- matches = [v for v in sorted_keys if
+ matches = [v for v in sorted_keys
+ if
v[0][0] == aft and
v[1] == child[0]]
else:
# This NT comes before the original NT.
- matches = [v for v in sorted_keys if
+ matches = [v for v in sorted_keys
+ if
v[0][1] == pre and
v[1] == child[0]]
@@ -434,24 +442,27 @@ def check_reductions(alt_cs, pre, aft, idx, children):
if child_idx > loc:
# This NT comes after the original
# NT.
- aft_c = aft + str_len[1] - str_len[0]
+ aft_c = aft + str_len[1] - \
+ str_len[0]
else:
# This NT comes before the original
# NT.
- pre_c = pre - str_len[1] + str_len[0]
+ pre_c = pre - str_len[1] + \
+ str_len[0]
# Add to children.
children[child_idx] = [match[2],
- trackers.snippets[
- match[2]]]
+ trackers.snippets[
+ match[
+ 2]]]
if alt_cs:
# Recurse to find the next piece of
# the puzzle.
check_reductions(alt_cs, pre_c,
- aft_c, idx,
- children)
+ aft_c, idx,
+ children)
elif all([i != [] for i in children]):
# We have compiled a full set of potential
@@ -529,8 +540,7 @@ def delete_snippet(self):
"""
if self.parent and self.snippet and self.snippet in trackers.snippets and \
- len(params['BNF_GRAMMAR'].concat_NTs[self.root]) == 1:
-
+ len(params['BNF_GRAMMAR'].concat_NTs[self.root]) == 1:
# Delete this snippet as it's (hopefully) useless now.
del trackers.snippets[self.snippet]
diff --git a/src/representation/derivation.py b/src/representation/derivation.py
index 703aec3e..3d4892bc 100644
--- a/src/representation/derivation.py
+++ b/src/representation/derivation.py
@@ -1,8 +1,9 @@
-from random import choice, randrange, randint
+from random import choice, randint, randrange
from algorithm.parameters import params
-from utilities.representation.check_methods import ret_true, get_nodes_and_depth
from representation.tree import Tree
+from utilities.representation.check_methods import get_nodes_and_depth, \
+ ret_true
def generate_tree(tree, genome, output, method, nodes, depth, max_depth,
@@ -22,7 +23,7 @@ def generate_tree(tree, genome, output, method, nodes, depth, max_depth,
:param depth_limit: The maximum depth the tree can expand to.
:return: genome, output, nodes, depth, max_depth.
"""
-
+
# Increment nodes and depth, set depth of current node.
nodes += 1
depth += 1
@@ -34,14 +35,14 @@ def generate_tree(tree, genome, output, method, nodes, depth, max_depth,
if depth_limit:
# Set remaining depth.
remaining_depth = depth_limit - depth
-
+
else:
remaining_depth = depth_limit
-
+
# Find which productions can be used based on the derivation method.
available = legal_productions(method, remaining_depth, tree.root,
productions['choices'])
-
+
# Randomly pick a production choice.
chosen_prod = choice(available)
@@ -51,11 +52,11 @@ def generate_tree(tree, genome, output, method, nodes, depth, max_depth,
codon = randrange(productions['no_choices'],
params['BNF_GRAMMAR'].codon_size,
productions['no_choices']) + prod_index
-
+
# Set the codon for the current node and append codon to the genome.
tree.codon = codon
genome.append(codon)
-
+
# Initialise empty list of children for current node.
tree.children = []
@@ -64,14 +65,14 @@ def generate_tree(tree, genome, output, method, nodes, depth, max_depth,
if symbol["type"] == "T":
# The symbol is a terminal. Append new node to children.
tree.children.append(Tree(symbol["symbol"], tree))
-
+
# Append the terminal to the output list.
output.append(symbol["symbol"])
-
+
elif symbol["type"] == "NT":
# The symbol is a non-terminal. Append new node to children.
tree.children.append(Tree(symbol["symbol"], tree))
-
+
# recurse on the new node.
genome, output, nodes, d, max_depth = \
generate_tree(tree.children[-1], genome, output, method,
@@ -88,7 +89,7 @@ def generate_tree(tree, genome, output, method, nodes, depth, max_depth,
if depth > max_depth:
# Set new maximum depth
max_depth = depth
-
+
return genome, output, nodes, depth, max_depth
@@ -110,14 +111,14 @@ def legal_productions(method, depth_limit, root, productions):
# Get all information about root node
root_info = params['BNF_GRAMMAR'].non_terminals[root]
-
+
if method == "random":
# Randomly build a tree.
-
+
if not depth_limit:
# There is no depth limit, any production choice can be used.
available = productions
-
+
elif depth_limit > params['BNF_GRAMMAR'].max_arity + 1:
# If the depth limit is greater than the maximum arity of the
# grammar, then any production choice can be used.
@@ -127,7 +128,7 @@ def legal_productions(method, depth_limit, root, productions):
# If we have already surpassed the depth limit, then list the
# choices with the shortest terminating path.
available = root_info['min_path']
-
+
else:
# The depth limit is less than or equal to the maximum arity of
# the grammar + 1. We have to be careful in selecting available
@@ -140,17 +141,17 @@ def legal_productions(method, depth_limit, root, productions):
# There are no available choices which do not violate the depth
# limit. List the choices with the shortest terminating path.
available = root_info['min_path']
-
+
elif method == "full":
# Build a "full" tree where every branch extends to the depth limit.
-
+
if not depth_limit:
# There is no depth limit specified for building a Full tree.
# Raise an error as a depth limit HAS to be specified here.
s = "representation.derivation.legal_productions\n" \
"Error: Depth limit not specified for `Full` tree derivation."
raise Exception(s)
-
+
elif depth_limit > params['BNF_GRAMMAR'].max_arity + 1:
# If the depth limit is greater than the maximum arity of the
# grammar, then only recursive production choices can be used.
@@ -168,7 +169,7 @@ def legal_productions(method, depth_limit, root, productions):
# depth limit.
available = [prod for prod in productions if prod['max_path'] ==
depth_limit - 1]
-
+
if not available:
# There are no available choices which extend exactly to the
# depth limit. List the NT choices with the longest terminating
@@ -198,13 +199,13 @@ def pi_random_derivation(tree, max_depth):
# traversal of the tree to build the genome, we need to build it as we
# encounter each node.
genome = []
-
+
while queue:
# Loop until no items remain in the queue.
-
+
# Pick a random item from the queue.
- chosen = randint(0, len(queue)-1)
-
+ chosen = randint(0, len(queue) - 1)
+
# Pop the next item from the queue.
all_node = queue.pop(chosen)
node = all_node[0]
@@ -215,7 +216,7 @@ def pi_random_derivation(tree, max_depth):
# Find the productions possible from the current root.
productions = params['BNF_GRAMMAR'].rules[node.root]
-
+
# Set remaining depth.
remaining_depth = max_depth - node.depth
@@ -247,19 +248,19 @@ def pi_random_derivation(tree, max_depth):
# Create new child.
child = Tree(symbol["symbol"], node)
-
+
# Append new node to children.
node.children.append(child)
if symbol["type"] == "NT":
# The symbol is a non-terminal.
-
+
# Check whether child is recursive
recur_child = ret_true(params['BNF_GRAMMAR'].non_terminals
- [child.root]['recursive'])
-
+ [child.root]['recursive'])
+
# Insert new child into the correct position in the queue.
- queue.insert(chosen+i, [child, recur_child])
+ queue.insert(chosen + i, [child, recur_child])
# genome, output, invalid, depth, and nodes can all be generated by
# recursing through the tree once.
@@ -307,7 +308,7 @@ def pi_grow(tree, max_depth):
# Get maximum depth of overall tree.
_, overall_depth = get_nodes_and_depth(tree)
-
+
# Find the productions possible from the current root.
productions = params['BNF_GRAMMAR'].rules[node.root]
@@ -325,11 +326,11 @@ def pi_grow(tree, max_depth):
productions['choices'])
else:
# Any production choices can be made.
-
+
# Find which productions can be used based on the derivation method.
available = legal_productions("random", remaining_depth, node.root,
productions['choices'])
-
+
# Randomly pick a production choice.
chosen_prod = choice(available)
@@ -345,7 +346,7 @@ def pi_grow(tree, max_depth):
# Insert codon into the genome.
genome.append(codon)
-
+
# Initialise empty list of children for current node.
node.children = []
@@ -360,11 +361,11 @@ def pi_grow(tree, max_depth):
if symbol["type"] == "NT":
# The symbol is a non-terminal.
-
+
# Check whether child is recursive
recur_child = ret_true(params['BNF_GRAMMAR'].non_terminals
[child.root]['recursive'])
-
+
# Insert new child into the correct position in the queue.
queue.insert(chosen + i, [child, recur_child])
@@ -373,5 +374,5 @@ def pi_grow(tree, max_depth):
_, output, invalid, depth, \
nodes = tree.get_tree_info(params['BNF_GRAMMAR'].non_terminals.keys(),
[], [])
-
+
return genome, output, nodes, depth
diff --git a/src/representation/grammar.py b/src/representation/grammar.py
index fe2fcdce..d8fa7d33 100755
--- a/src/representation/grammar.py
+++ b/src/representation/grammar.py
@@ -1,5 +1,5 @@
from math import floor
-from re import match, finditer, DOTALL, MULTILINE
+from re import DOTALL, MULTILINE, finditer, match
from sys import maxsize
from algorithm.parameters import params
@@ -167,7 +167,7 @@ def read_bnf_file(self, file_name):
self.terminals[str(i)] = \
[rule.group('rulename')]
elif rule.group('rulename') not in \
- self.terminals[str(i)]:
+ self.terminals[str(i)]:
self.terminals[str(i)].append(
rule.group('rulename'))
tmp_productions.append({"choice": tmp_production,
@@ -195,7 +195,7 @@ def read_bnf_file(self, file_name):
self.terminals[terminalparts] = \
[rule.group('rulename')]
elif rule.group('rulename') not in \
- self.terminals[terminalparts]:
+ self.terminals[terminalparts]:
self.terminals[terminalparts].append(
rule.group('rulename'))
terminalparts = None
@@ -224,7 +224,7 @@ def read_bnf_file(self, file_name):
self.terminals[terminalparts] = \
[rule.group('rulename')]
elif rule.group('rulename') not in \
- self.terminals[terminalparts]:
+ self.terminals[terminalparts]:
self.terminals[terminalparts].append(
rule.group('rulename'))
tmp_productions.append({"choice": tmp_production,
@@ -387,7 +387,7 @@ def set_grammar_properties(self):
for choice in choices:
# Set the maximum path to a terminal for each produciton choice
choice['max_path'] = max([item["min_steps"] for item in
- choice['choice']])
+ choice['choice']])
# Find shortest path to a terminal for all production choices for
# the current NT. The shortest path will be the minimum of the
@@ -505,7 +505,7 @@ def check_all_permutations(self, depth):
# each NT symbol in the original choice.
if len(child['choice']) == 1 and \
- child['choice'][0]["type"] == "T":
+ child['choice'][0]["type"] == "T":
# If the child choice leads directly to
# a single terminal, increment the
# permutation count.
@@ -517,9 +517,13 @@ def check_all_permutations(self, depth):
# Generate a key for the permutations
# dictionary and increment the
# permutations count there.
- key = [sym['symbol'] for sym in child['choice']]
- if (i - 1) in depth_per_symbol_trees[str(key)].keys():
- symbol_arity_pos += depth_per_symbol_trees[str(key)][i - 1]
+ key = [sym['symbol'] for sym in
+ child['choice']]
+ if (i - 1) in depth_per_symbol_trees[
+ str(key)].keys():
+ symbol_arity_pos += \
+ depth_per_symbol_trees[str(key)][
+ i - 1]
# Multiply original count by new count.
sym_pos *= symbol_arity_pos
@@ -533,7 +537,10 @@ def check_all_permutations(self, depth):
for sy in start_symbols:
key = [sym['symbol'] for sym in sy['choice']]
if str(key) in depth_per_symbol_trees:
- pos += depth_per_symbol_trees[str(key)][depth] if depth in depth_per_symbol_trees[str(key)] else 0
+ pos += depth_per_symbol_trees[str(key)][depth] if depth in \
+ depth_per_symbol_trees[
+ str(
+ key)] else 0
else:
pos += 1
diff --git a/src/representation/individual.py b/src/representation/individual.py
index 3bcdc60e..1830201b 100755
--- a/src/representation/individual.py
+++ b/src/representation/individual.py
@@ -25,7 +25,7 @@ def __init__(self, genome, ind_tree, map_ind=True):
# The individual needs to be mapped from the given input
# parameters.
self.phenotype, self.genome, self.tree, self.nodes, self.invalid, \
- self.depth, self.used_codons = mapper(genome, ind_tree)
+ self.depth, self.used_codons = mapper(genome, ind_tree)
else:
# The individual does not need to be mapped.
@@ -49,9 +49,13 @@ class by their fitness values. Allows for sorting/ordering of a
greater than the comparison individual.
"""
- if np.isnan(self.fitness): return True
- elif np.isnan(other.fitness): return False
- else: return self.fitness < other.fitness if params['FITNESS_FUNCTION'].maximise else other.fitness < self.fitness
+ if np.isnan(self.fitness):
+ return True
+ elif np.isnan(other.fitness):
+ return False
+ else:
+ return self.fitness < other.fitness if params[
+ 'FITNESS_FUNCTION'].maximise else other.fitness < self.fitness
def __le__(self, other):
"""
@@ -67,9 +71,13 @@ class by their fitness values. Allows for sorting/ordering of a
greater than or equal to the comparison individual.
"""
- if np.isnan(self.fitness): return True
- elif np.isnan(other.fitness): return False
- else: return self.fitness <= other.fitness if params['FITNESS_FUNCTION'].maximise else other.fitness <= self.fitness
+ if np.isnan(self.fitness):
+ return True
+ elif np.isnan(other.fitness):
+ return False
+ else:
+ return self.fitness <= other.fitness if params[
+ 'FITNESS_FUNCTION'].maximise else other.fitness <= self.fitness
def __str__(self):
"""
diff --git a/src/representation/latent_tree.py b/src/representation/latent_tree.py
index 0f502c15..403cbedf 100644
--- a/src/representation/latent_tree.py
+++ b/src/representation/latent_tree.py
@@ -27,10 +27,10 @@
"""
+import random
from algorithm.parameters import params
from representation.derivation import legal_productions
-import random
def latent_tree_random_ind(grammar, maxdepth, old_genome=None):
@@ -56,17 +56,17 @@ def _random_ind(gram, genome, depth, s=None, name=None):
name = tuple()
elif s in gram.terminals:
return s
-
+
rule = gram.rules[s]
if old_genome and name in old_genome:
-
+
# A valid entry was found in old_genome. Apply mod rule as
# part of repair: it will have no effect if the value is
# already in the right range.
gi = old_genome[name] % len(rule['choices'])
prod = rule['choices'][gi]
-
+
else:
# No valid entry was found, so choose a production from
@@ -74,10 +74,11 @@ def _random_ind(gram, genome, depth, s=None, name=None):
# finish recursion is less than or equal to max depth
# minus our current depth).
productions = params['BNF_GRAMMAR'].rules[s]
- available = legal_productions("random", depth, s, productions['choices'])
- prod = random.choice(available) # choose production
- gi = productions['choices'].index(prod) # find its index
-
+ available = legal_productions("random", depth, s,
+ productions['choices'])
+ prod = random.choice(available) # choose production
+ gi = productions['choices'].index(prod) # find its index
+
genome[name] = gi
# Join together all the strings for this production. For
@@ -90,7 +91,7 @@ def _random_ind(gram, genome, depth, s=None, name=None):
else
_random_ind(gram,
genome,
- depth-1,
+ depth - 1,
s["symbol"],
name + ((gi, i),)))
for i, s in enumerate(prod['choice']))
@@ -125,7 +126,7 @@ def latent_tree_crossover(g1, g2):
# deep_copy() is actually running, so I don't understand this
# problem. To be on the safe side, we'll keep the copy() here.
# See https://github.com/PonyGE/PonyGE2/issues/89.
- c = g1.copy()
+ c = g1.copy()
for k in g2.keys():
if k in g1:
# k is in both parents so choose randomly.
@@ -144,12 +145,12 @@ def latent_tree_mutate(g):
(possibly) large int to the corresponding small one (ie the one
giving the same production choice) in the range of possible
choices."""
-
+
# FIXME We don't rely on g being a copy, in case the search
# algorithm sometimes mutates individuals which are original
# members of the population.
# See https://github.com/PonyGE/PonyGE2/issues/89.
g = g.copy()
k = random.choice(list(g.keys()))
- g[k] = random.randrange(1000000) # there is no true maxint on py 3
+ g[k] = random.randrange(1000000) # there is no true maxint on py 3
return g
diff --git a/src/representation/tree.py b/src/representation/tree.py
index 20e573ce..430f1a2e 100755
--- a/src/representation/tree.py
+++ b/src/representation/tree.py
@@ -11,7 +11,7 @@ def __init__(self, expr, parent):
:param parent: The parent of the current node. None if node is tree
root.
"""
-
+
self.parent = parent
self.codon = None
self.depth = 1
@@ -25,26 +25,26 @@ def __str__(self):
:return: A string of the current tree.
"""
-
+
# Initialise the output string.
result = "("
-
+
# Append the root of the current node to the output string.
result += str(self.root)
-
+
for child in self.children:
# Iterate across all children.
-
+
if len(child.children) > 0:
# Recurse through all children.
result += " " + str(child)
-
+
else:
# Child is a terminal, append root to string.
result += " " + str(child.root)
-
+
result += ")"
-
+
return result
def __copy__(self):
@@ -56,7 +56,7 @@ def __copy__(self):
# Copy current tree by initialising a new instance of the tree class.
tree_copy = Tree(self.root, self.parent)
-
+
# Set node parameters.
tree_copy.codon, tree_copy.depth = self.codon, self.depth
@@ -65,10 +65,10 @@ def __copy__(self):
for child in self.children:
# Recurse through all children.
new_child = child.__copy__()
-
+
# Set the parent of the copied child as the copied parent.
new_child.parent = tree_copy
-
+
# Append the copied child to the copied parent.
tree_copy.children.append(new_child)
@@ -85,21 +85,21 @@ class by their attributes. Returns True if self == other.
# Get attributes of self and other.
a_self, a_other = vars(self), vars(other)
-
+
# Don't look at the children as they are class instances themselves.
taboo = ["parent", "children", "snippet", "id"]
self_no_kids = {k: v for k, v in a_self.items() if k not in taboo}
other_no_kids = {k: v for k, v in a_other.items() if k not in taboo}
-
+
# Compare attributes
if self_no_kids != other_no_kids:
# Attributes are not the same.
return False
-
+
else:
# Attributes are the same
child_list = [self.children, other.children]
-
+
if len(list(filter(lambda x: x is not None, child_list))) % 2 != 0:
# One contains children, the other doesn't.
return False
@@ -124,22 +124,22 @@ def get_target_nodes(self, array, target=None):
:param target: The target nodes to match.
:return: The array of all nodes that match the target.
"""
-
+
if self.root in target:
# Check if the current node matches the target.
-
+
# Add the current node to the array.
array.append(self)
-
+
# Find all non-terminal children of the current node.
NT_kids = [kid for kid in self.children if kid.root in
params['BNF_GRAMMAR'].non_terminals]
-
+
for child in NT_kids:
if NT_kids:
# Recursively call function on any non-terminal children.
array = child.get_target_nodes(array, target=target)
-
+
return array
def get_node_labels(self, labels):
@@ -149,14 +149,14 @@ def get_node_labels(self, labels):
:param labels: The set of roots of all nodes in the tree.
:return: The set of roots of all nodes in the tree.
"""
-
+
# Add the current root to the set of all labels.
labels.add(self.root)
for child in self.children:
# Recurse on all children.
labels = child.get_node_labels(labels)
-
+
return labels
def get_tree_info(self, nt_keys, genome, output, invalid=False,
@@ -178,16 +178,16 @@ def get_tree_info(self, nt_keys, genome, output, invalid=False,
# Increment number of nodes in tree and set current node id.
nodes += 1
-
+
if self.parent:
# If current node has a parent, increment current depth from
# parent depth.
self.depth = self.parent.depth + 1
-
+
else:
# Current node is tree root, set depth to 1.
self.depth = 1
-
+
if self.depth > max_depth:
# Set new max tree depth.
max_depth = self.depth
@@ -199,7 +199,7 @@ def get_tree_info(self, nt_keys, genome, output, invalid=False,
# Find all non-terminal children of current node.
NT_children = [child for child in self.children if child.root in
nt_keys]
-
+
if not NT_children:
# The current node has only terminal children, increment number
# of tree nodes.
@@ -222,11 +222,11 @@ def get_tree_info(self, nt_keys, genome, output, invalid=False,
# If the current child has no children it is a terminal.
# Append it to the phenotype output.
output.append(child.root)
-
+
if child.root in nt_keys:
# Current non-terminal node has no children; invalid tree.
invalid = True
-
+
else:
# The current child has children, recurse.
genome, output, invalid, max_depth, nodes = \
diff --git a/src/scripts/GE_LR_parser.py b/src/scripts/GE_LR_parser.py
index 290e0793..a7d08f4c 100644
--- a/src/scripts/GE_LR_parser.py
+++ b/src/scripts/GE_LR_parser.py
@@ -1,4 +1,5 @@
from sys import path
+
path.append("../src")
from utilities.algorithm.general import check_python_version
@@ -66,7 +67,7 @@ def parse_terminals(target):
# particular rule.
# Generate a key for the snippets repository.
- key = " ".join([str([idx, idx+len(T)]), NT])
+ key = " ".join([str([idx, idx + len(T)]), NT])
# Get index of production choice.
index = [[sym['symbol'] for sym in choice['choice']] for
@@ -208,7 +209,7 @@ def reduce(solution):
# This is a terminal, decrement by length of T.
# Check output of target string.
- check = target[pre-len(NTs[item][0]):pre]
+ check = target[pre - len(NTs[item][0]):pre]
if check == NTs[item][0]:
# We have a match.
@@ -252,7 +253,7 @@ def reduce(solution):
break
# Step 2: reduce everything after the loc.
- for i, item in enumerate(alt_cs[loc+1:]):
+ for i, item in enumerate(alt_cs[loc + 1:]):
if NTs[item][1] == "T":
# This is a terminal, decrement by length of T.
@@ -348,7 +349,6 @@ def main():
if __name__ == '__main__':
-
# Set parameters
set_params(sys.argv[1:], create_files=False)
diff --git a/src/scripts/baselines.py b/src/scripts/baselines.py
index 1d040f92..81cd77be 100755
--- a/src/scripts/baselines.py
+++ b/src/scripts/baselines.py
@@ -3,8 +3,7 @@
from collections import Counter
import numpy as np
-from sklearn.linear_model import LinearRegression, ElasticNet
-
+from sklearn.linear_model import ElasticNet, LinearRegression
from utilities.fitness.get_data import get_data
@@ -19,8 +18,9 @@ def pprint(a, format_string='{0:.2f}'):
:param format_string: The desired precision level.
:return: A formatted array.
"""
-
- return "[" + ", ".join(format_string.format(v,i) for i,v in enumerate(a)) + "]"
+
+ return "[" + ", ".join(
+ format_string.format(v, i) for i, v in enumerate(a)) + "]"
def fit_maj_class(train_X, train_y, test_X):
@@ -32,25 +32,25 @@ def fit_maj_class(train_X, train_y, test_X):
:param test_X: An array of input (X) testint data.
:return:
"""
-
+
# Set training Y data to int type.
train_y = train_y.astype(int)
-
+
# Get all classes from training Y data, often just {0, 1} or {-1, 1}.
classes = set(train_y)
-
+
# Get majority class.
maj = Counter(train_y).most_common(1)[0][0]
-
+
# Generate model.
model = "Majority class %d" % maj
-
+
# Generate training and testing output values.
yhat_train = maj * np.ones(len(train_y))
yhat_test = maj * np.ones(len(test_y))
-
+
return model, yhat_train, yhat_test
-
+
def fit_const(train_X, train_y, test_X):
"""
@@ -65,7 +65,7 @@ def fit_const(train_X, train_y, test_X):
yhat_train = np.ones(len(train_y)) * mn
yhat_test = np.ones(len(test_y)) * mn
model = "Const %.2f" % mn
-
+
return model, yhat_train, yhat_test
@@ -83,10 +83,10 @@ def fit_lr(train_X, train_y, test_X):
yhat_train = lr.predict(train_X)
yhat_test = lr.predict(test_X)
model = "LR int %.2f coefs %s" % (lr.intercept_, pprint(lr.coef_))
-
+
return model, yhat_train, yhat_test
-
+
def fit_enet(train_X, train_y, test_X):
"""
Use linear regression to predict. Elastic net is LR with L1 and L2
@@ -99,13 +99,14 @@ def fit_enet(train_X, train_y, test_X):
"""
enet = ElasticNet()
enet.fit(train_X, train_y)
- model = "ElasticNet int %.2f coefs %s" % (enet.intercept_, pprint(enet.coef_))
+ model = "ElasticNet int %.2f coefs %s" % (
+ enet.intercept_, pprint(enet.coef_))
yhat_train = enet.predict(train_X)
yhat_test = enet.predict(test_X)
-
+
return model, yhat_train, yhat_test
-
+
if __name__ == "__main__":
dataset_name = sys.argv[1]
diff --git a/src/scripts/experiment_manager.py b/src/scripts/experiment_manager.py
index 230e0216..a2d188db 100644
--- a/src/scripts/experiment_manager.py
+++ b/src/scripts/experiment_manager.py
@@ -4,6 +4,7 @@
Copyright (c) 2014 Michael Fenton
Hereby licensed under the GNU GPL v3."""
from sys import path
+
path.append("../src")
from utilities.algorithm.general import check_python_version
diff --git a/src/scripts/grammar_analyser.py b/src/scripts/grammar_analyser.py
index 56a01719..f8eb03eb 100644
--- a/src/scripts/grammar_analyser.py
+++ b/src/scripts/grammar_analyser.py
@@ -1,4 +1,5 @@
from sys import path
+
path.append("../src")
from utilities.algorithm.general import check_python_version
@@ -35,24 +36,24 @@ def main(command_line_args):
grammar = Grammar(os.path.join("..", "grammars", params['GRAMMAR_FILE']))
print("\nSpecified grammar:", params['GRAMMAR_FILE'])
-
+
# Initialise zero maximum branching factor for grammar
max_b_factor = 0
-
+
print("\nBranching factor for each non-terminal:")
-
+
for NT in sorted(grammar.non_terminals.keys()):
-
+
# Get branching factor for current NT.
b_factor = grammar.non_terminals[NT]['b_factor']
-
+
# Print info.
print("", NT, " \t:", b_factor)
-
+
# Set maximum branching factor.
if b_factor > max_b_factor:
max_b_factor = b_factor
-
+
print("\nMaximum branching factor of the grammar:", max_b_factor)
# Initialise counter for the total number of solutions.
@@ -61,7 +62,6 @@ def main(command_line_args):
print("\nNumber of unique possible solutions for a range of depths:\n")
for depth in grammar.permutations:
-
# Get number of solutions possible at current depth
solutions = grammar.permutations[depth]
@@ -70,14 +70,13 @@ def main(command_line_args):
# Increment total number of solutions.
total_solutions += solutions
-
+
print("\nTotal number of unique possible solutions that can be generated"
"up to and including a depth of %d: %s" %
(depth, sci_notation(total_solutions)))
-
-if __name__ == "__main__":
+if __name__ == "__main__":
# Do not write or save any files.
params['DEBUG'] = True
diff --git a/src/scripts/python_script_evaluation.py b/src/scripts/python_script_evaluation.py
index 787608e4..f9dd826d 100644
--- a/src/scripts/python_script_evaluation.py
+++ b/src/scripts/python_script_evaluation.py
@@ -1,10 +1,13 @@
import json
+import logging
import multiprocessing as mp
import sys
from queue import Empty
from types import ModuleType
-import logging
-logging.basicConfig(filename='python_log.txt', format='%(asctime)s:%(process)d:%(thread)d:%(message)s', level=logging.INFO) # set to DEBUG for debug info ;)
+
+logging.basicConfig(filename='python_log.txt',
+ format='%(asctime)s:%(process)d:%(thread)d:%(message)s',
+ level=logging.INFO) # set to DEBUG for debug info ;)
class Worker(mp.Process):
@@ -18,7 +21,8 @@ def run(self):
# START LINUX: used to receive Memory Error faster in Linux
try:
import resource
- resource.setrlimit(resource.RLIMIT_AS, (2 ** 30, 2 ** 30)) # 2 ** 30 == 1GB in bytes
+ resource.setrlimit(resource.RLIMIT_AS,
+ (2 ** 30, 2 ** 30)) # 2 ** 30 == 1GB in bytes
except ImportError:
pass
# END LINUX:
@@ -36,10 +40,13 @@ def run(self):
if exception:
self.produce.put({'exception': exception})
else:
- self.produce.put({key: value for key, value in help_globals.items()
- if not callable(value) and # cannot be a function
- not isinstance(value, ModuleType) and # cannot be a module
- key not in ['__builtins__', 'stop']}) # cannot be builtins or synchronized objects
+ self.produce.put(
+ {key: value for key, value in help_globals.items()
+ if not callable(value) and # cannot be a function
+ not isinstance(value,
+ ModuleType) and # cannot be a module
+ key not in ['__builtins__',
+ 'stop']}) # cannot be builtins or synchronized objects
del help_globals
else:
break
@@ -47,6 +54,7 @@ def run(self):
def stop_current(self):
self.stop.value = True
+
if __name__ == '__main__':
consume = mp.Queue()
produce = mp.Queue()
@@ -104,6 +112,7 @@ def stop_current(self):
ret_message_dict = {}
for v in message_dict['variables']:
if v in results:
- ret_message_dict[v] = list(results[v]) if isinstance(results[v], set) else results[v]
+ ret_message_dict[v] = list(results[v]) if isinstance(
+ results[v], set) else results[v]
print(json.dumps(ret_message_dict), flush=True)
logging.debug('Sent output normal')
diff --git a/src/scripts/stats_parser.py b/src/scripts/stats_parser.py
index 5d34cf0f..74bd7ad9 100644
--- a/src/scripts/stats_parser.py
+++ b/src/scripts/stats_parser.py
@@ -1,4 +1,5 @@
from sys import path
+
path.append("../src")
from utilities.algorithm.general import check_python_version
@@ -10,11 +11,13 @@
from os import getcwd, listdir, path, sep
import matplotlib
import numpy as np
+
np.seterr(all="raise")
import pandas as pd
matplotlib.use('Agg')
import matplotlib.pyplot as plt
+
plt.rc('font', family='Times New Roman')
@@ -24,12 +27,12 @@ def help_message():
:return: Nothing
"""
-
+
lines_1 = ["Welcome to PonyGE's post-run stats parser.",
"-------------------",
"The following are the available command line args.",
"You must specify an experiment name."]
-
+
lines_2 = [["\t--help:", "Shows this help message."],
["\t--experiment_name:", "The name of the containing folder in "
"which target runs are saved, e.g. "
@@ -56,7 +59,7 @@ def parse_opts(command_line_args):
stats.stats.stats dictionary.
graph: an optional boolean flag for graphing specified stats.
"""
-
+
try:
opts, args = getopt.getopt(command_line_args[1:], "",
["help", "experiment_name="])
@@ -76,18 +79,18 @@ def parse_opts(command_line_args):
raise Exception(s)
experiment_name = None
-
+
# iterate over all arguments in the option parser.
for opt, arg in opts:
if opt == "--help":
# Print help message.
help_message()
exit()
-
+
elif opt == "--experiment_name":
# Set experiment name (i.e. containing folder for multiple runs).
experiment_name = arg
-
+
return experiment_name
@@ -119,20 +122,20 @@ def parse_stats_from_runs(experiment_name):
# Since results files are not kept in source directory, need to escape
# one folder.
file_path = path.join(getcwd(), "..", "results")
-
+
# Check for use of experiment manager.
if experiment_name:
file_path = path.join(file_path, experiment_name)
-
+
else:
s = "scripts.parse_stats.parse_stats_from_runs\n" \
"Error: experiment name not specified."
raise Exception(s)
-
+
# Find list of all runs contained in the specified folder.
runs = [run for run in listdir(file_path) if
path.isdir(path.join(file_path, run))]
-
+
# Place to store the header for full stats file.
header = ""
@@ -145,12 +148,12 @@ def parse_stats_from_runs(experiment_name):
# Load in data and get the names of all stats.
stats = list(pd.read_csv(ping_file, sep="\t"))
-
+
# Make list of stats we do not wish to parse.
no_parse_list = ["gen", "total_inds", "time_adjust"]
-
+
for stat in [stat for stat in stats if stat not in no_parse_list and
- not stat.startswith("Unnamed")]:
+ not stat.startswith("Unnamed")]:
# Iterate over all stats.
print("Parsing", stat)
summary_stats = []
@@ -181,32 +184,32 @@ def parse_stats_from_runs(experiment_name):
try:
# Generate numpy array of all stats
summary_stats = np.array(summary_stats)
-
+
# Append Stat to header.
header = header + stat + "_mean,"
-
+
summary_stats_mean = np.nanmean(summary_stats, axis=0)
full_stats.append(summary_stats_mean)
-
+
# Append Stat to header.
header = header + stat + "_std,"
summary_stats_std = np.nanstd(summary_stats, axis=0)
full_stats.append(summary_stats_std)
summary_stats = np.transpose(summary_stats)
-
+
# Save stats as a .csv file.
np.savetxt(path.join(file_path, (stat + ".csv")), summary_stats,
delimiter=",")
-
+
# Graph stat by calling graphing function.
save_average_plot_across_runs(path.join(file_path, (stat +
".csv")))
-
+
except FloatingPointError:
print("scripts.stats_parser.parse_stats_from_runs\n"
"Warning: FloatingPointError encountered while parsing %s "
"stats." % (stat))
-
+
# Convert and rotate full stats
full_stats = np.array(full_stats)
full_stats = np.transpose(full_stats)
@@ -239,52 +242,52 @@ def save_average_plot_across_runs(filename):
of each generation of multiple runs. Must be comma separated.
:return: Nothing.
"""
-
+
# Get stat name from the filename. Used later for saving graph.
stat_name = filename.split(sep)[-1].split(".")[0]
-
+
# Load in data.
data = np.genfromtxt(filename, delimiter=',')[:, :-1]
-
+
# Generate average and standard deviations of loaded data.
ave = np.nanmean(data, axis=1)
std = np.nanstd(data, axis=1)
-
+
# Calculate max and min of standard deviation.
stdmax = ave + std
stdmin = ave - std
-
+
# Generate generation range over which data is to be graphed.
max_gens = len(ave)
r = range(1, max_gens + 1)
-
+
# Initialise figure plot.
fig = plt.figure()
ax1 = fig.add_subplot(1, 1, 1)
-
+
# Plot data and standard deviation infill.
ax1.plot(r, ave, color="blue")
ax1.fill_between(r, stdmin, stdmax, color="DodgerBlue", alpha=0.5)
# Set x-axis limits.
plt.xlim(0, max_gens + 1)
-
+
# Set title and axes.
plt.title("Average " + stat_name)
plt.xlabel('Generation', fontsize=14)
plt.ylabel('Average ' + stat_name, fontsize=14)
-
+
# Save graph under the same name as the original .csv file but with a
# .pdf extension instead.
new_filename = filename[:-3] + "pdf"
plt.savefig(str(new_filename))
-
+
plt.close()
if __name__ == "__main__":
# Get experiment name and graphing flag from command line parser.
experiment_name = parse_opts(sys.argv)
-
+
# Call statistics parser for experiment name.
parse_stats_from_runs(experiment_name)
diff --git a/src/stats/stats.py b/src/stats/stats.py
index d19e1ce6..5d9ba555 100644
--- a/src/stats/stats.py
+++ b/src/stats/stats.py
@@ -1,44 +1,43 @@
from copy import copy
from sys import stdout
from time import time
-import numpy as np
+import numpy as np
from algorithm.parameters import params
from utilities.algorithm.NSGA2 import compute_pareto_metrics
from utilities.algorithm.state import create_state
from utilities.stats import trackers
-from utilities.stats.save_plots import save_plot_from_data, \
- save_pareto_fitness_plot
-from utilities.stats.file_io import save_stats_to_file, save_stats_headers, \
- save_best_ind_to_file, save_first_front_to_file
-
+from utilities.stats.file_io import save_best_ind_to_file, \
+ save_first_front_to_file, save_stats_headers, save_stats_to_file
+from utilities.stats.save_plots import save_pareto_fitness_plot, \
+ save_plot_from_data
"""Algorithm statistics"""
stats = {
- "gen": 0,
- "total_inds": 0,
- "regens": 0,
- "invalids": 0,
- "runtime_error": 0,
- "unique_inds": len(trackers.cache),
- "unused_search": 0,
- "ave_genome_length": 0,
- "max_genome_length": 0,
- "min_genome_length": 0,
- "ave_used_codons": 0,
- "max_used_codons": 0,
- "min_used_codons": 0,
- "ave_tree_depth": 0,
- "max_tree_depth": 0,
- "min_tree_depth": 0,
- "ave_tree_nodes": 0,
- "max_tree_nodes": 0,
- "min_tree_nodes": 0,
- "ave_fitness": 0,
- "best_fitness": 0,
- "time_taken": 0,
- "total_time": 0,
- "time_adjust": 0
+ "gen": 0,
+ "total_inds": 0,
+ "regens": 0,
+ "invalids": 0,
+ "runtime_error": 0,
+ "unique_inds": len(trackers.cache),
+ "unused_search": 0,
+ "ave_genome_length": 0,
+ "max_genome_length": 0,
+ "min_genome_length": 0,
+ "ave_used_codons": 0,
+ "max_used_codons": 0,
+ "min_used_codons": 0,
+ "ave_tree_depth": 0,
+ "max_tree_depth": 0,
+ "min_tree_depth": 0,
+ "ave_tree_nodes": 0,
+ "max_tree_nodes": 0,
+ "min_tree_nodes": 0,
+ "ave_fitness": 0,
+ "best_fitness": 0,
+ "time_taken": 0,
+ "total_time": 0,
+ "time_adjust": 0
}
@@ -69,7 +68,7 @@ def get_stats(individuals, end=False):
get_soo_stats(individuals, end)
if params['SAVE_STATE'] and not params['DEBUG'] and \
- stats['gen'] % params['SAVE_STATE_STEP'] == 0:
+ stats['gen'] % params['SAVE_STATE_STEP'] == 0:
# Save the state of the current evolutionary run.
create_state(individuals)
@@ -111,13 +110,12 @@ def get_soo_stats(individuals, end):
elif not params['SILENT']:
# Print simple display output.
- perc = stats['gen'] / (params['GENERATIONS']+1) * 100
+ perc = stats['gen'] / (params['GENERATIONS'] + 1) * 100
stdout.write("Evolution: %d%% complete\r" % perc)
stdout.flush()
# Generate test fitness on regression problems
if hasattr(params['FITNESS_FUNCTION'], "training_test") and end:
-
# Save training fitness.
trackers.best_ever.training_fitness = copy(trackers.best_ever.fitness)
@@ -200,7 +198,6 @@ def get_moo_stats(individuals, end):
# Get best fitness for each objective.
for o, ff in \
enumerate(params['FITNESS_FUNCTION'].fitness_functions):
-
# Get sorted list of all fitness values for objective "o"
fits = sorted(all_arr[o], reverse=ff.maximise)
@@ -301,7 +298,7 @@ def update_stats(individuals, end):
if params['CACHE']:
stats['unique_inds'] = len(trackers.cache)
stats['unused_search'] = 100 - stats['unique_inds'] / \
- stats['total_inds'] * 100
+ stats['total_inds'] * 100
# Genome Stats
genome_lengths = [len(i.genome) for i in individuals]
diff --git a/src/utilities/algorithm/NSGA2.py b/src/utilities/algorithm/NSGA2.py
index 2dbb9143..adeafe00 100644
--- a/src/utilities/algorithm/NSGA2.py
+++ b/src/utilities/algorithm/NSGA2.py
@@ -1,7 +1,7 @@
from collections import defaultdict
-from numpy import isnan
from algorithm.parameters import params
+from numpy import isnan
from utilities.fitness.math_functions import percentile
@@ -266,7 +266,7 @@ def get_population_iqr(population, n_objectives):
# Sort the population with respect to the current objective.
sorted_pop = sorted(population, key=lambda ind:
- params['FITNESS_FUNCTION'].value(ind.fitness, m),
+ params['FITNESS_FUNCTION'].value(ind.fitness, m),
reverse=params['FITNESS_FUNCTION'].
fitness_functions[m].maximise)
diff --git a/src/utilities/algorithm/command_line_parser.py b/src/utilities/algorithm/command_line_parser.py
index a8e5ad42..24cb5811 100644
--- a/src/utilities/algorithm/command_line_parser.py
+++ b/src/utilities/algorithm/command_line_parser.py
@@ -9,7 +9,7 @@ class SortingHelpFormatter(argparse.HelpFormatter):
this custom class, arguments will be printed in the order in which they are
defined.
"""
-
+
def add_arguments(self, actions):
actions = sorted(actions, key=attrgetter('option_strings'))
super(SortingHelpFormatter, self).add_arguments(actions)
@@ -69,7 +69,7 @@ class ListAction(argparse.Action):
def __init__(self, option_strings, **kwargs):
super(ListAction, self).__init__(option_strings, **kwargs)
-
+
def __call__(self, parser, namespace, value, option_string=None):
if type(eval(value)) != list or any([type(i) != int for i in
eval(value)]):
@@ -103,10 +103,10 @@ class CatchTabStr(argparse.Action):
"""
Class for checking raw string arguments to catch "tab" inputs.
"""
-
+
def __init__(self, option_strings, **kwargs):
super(CatchTabStr, self).__init__(option_strings, **kwargs)
-
+
def __call__(self, parser, namespace, value, option_string=None):
if repr(value) == repr("\\t"):
value = "\t"
@@ -144,7 +144,7 @@ def __call__(self, parser, namespace, value, option_string=None):
dest='HILL_CLIMBING_HISTORY',
type=int,
help='Sets the history-length for late-acceptance'
- 'and step-counting hill-climbing.')
+ 'and step-counting hill-climbing.')
parser.add_argument('--schc_count_method',
dest='SCHC_COUNT_METHOD',
type=str,
@@ -250,7 +250,7 @@ def __call__(self, parser, namespace, value, option_string=None):
help='Boolean flag for selecting whether or not '
'mutation is confined to within the used portion '
'of the genome. Default set to True.')
-
+
# CROSSOVER
parser.add_argument('--crossover',
dest='CROSSOVER',
@@ -354,7 +354,7 @@ def __call__(self, parser, namespace, value, option_string=None):
type=int,
help='Specify the number of cores to be used for '
'multi-core evaluation. Requires int.')
-
+
# REPLACEMENT
parser.add_argument('--replacement',
dest='REPLACEMENT',
@@ -467,14 +467,13 @@ def __call__(self, parser, namespace, value, option_string=None):
'desired state file. Note that state files have '
'no file type.')
-
# MULTIAGENT
parser.add_argument('--multiagent',
dest='MULTIAGENT',
action='store_true',
- default=None,
+ default=None,
help='This enable the multi-agent mode. If this mode is'
- ' enabled the search_loop and step parameter are'
+ ' enabled the search_loop and step parameter are'
' overridden with search_multiagent and step_multiagent'
' respectively')
parser.add_argument('--agent_size',
@@ -511,11 +510,11 @@ def __init__(self, option_strings, CACHE=None, LOOKUP_FITNESS=None,
def __call__(self, parser, namespace, values, option_string=None):
setattr(namespace, 'CACHE', self.CACHE)
if 'LOOKUP_FITNESS' not in namespace or \
- getattr(namespace, 'LOOKUP_FITNESS') is not False:
+ getattr(namespace, 'LOOKUP_FITNESS') is not False:
# able to overwrite if True or None
setattr(namespace, 'LOOKUP_FITNESS', self.LOOKUP_FITNESS)
if self.LOOKUP_BAD_FITNESS and \
- 'LOOKUP_BAD_FITNESS' not in namespace:
+ 'LOOKUP_BAD_FITNESS' not in namespace:
setattr(namespace, 'LOOKUP_BAD_FITNESS',
self.LOOKUP_BAD_FITNESS)
if self.MUTATE_DUPLICATES and 'MUTATE_DUPLICATES' not in namespace:
@@ -569,10 +568,10 @@ def __call__(self, parser, namespace, values, option_string=None):
# Set "None" values correctly.
for key in sorted(cmd_args.keys()):
# Check all specified arguments.
-
+
if type(cmd_args[key]) == str and cmd_args[key].lower() == "none":
# Allow for people not using correct capitalisation.
-
+
cmd_args[key] = None
return cmd_args, unknown
diff --git a/src/utilities/algorithm/general.py b/src/utilities/algorithm/general.py
index fe8390fe..778252cf 100644
--- a/src/utilities/algorithm/general.py
+++ b/src/utilities/algorithm/general.py
@@ -12,4 +12,4 @@ def check_python_version():
version_info.major == 3):
s = "\nError: Python version not supported.\n" \
" Must use at least Python 3.5."
- raise Exception(s)
\ No newline at end of file
+ raise Exception(s)
diff --git a/src/utilities/algorithm/initialise_run.py b/src/utilities/algorithm/initialise_run.py
index 65713dca..4dbb9a32 100644
--- a/src/utilities/algorithm/initialise_run.py
+++ b/src/utilities/algorithm/initialise_run.py
@@ -130,8 +130,9 @@ class name matches the file name.
try:
# Import module and attribute and save.
- params[op] = return_attr_from_module(module_name,
- attr_name)
+ params[op] = return_attr_from_module(
+ module_name,
+ attr_name)
except Exception:
s = "utilities.algorithm.initialise_run." \
@@ -156,14 +157,16 @@ class name matches the file name.
# step_distributed respectively
if params['MULTIAGENT'] and \
- ( op == 'SEARCH_LOOP' or op == 'STEP' ) :
+ (op == 'SEARCH_LOOP' or op == 'STEP'):
# Define the directory structure for the multi-agent search
# loop and step
- multiagent_ops = {'search_loop':'distributed_algorithm.search_loop' \
- ,'step':'distributed_algorithm.step'}
+ multiagent_ops = {
+ 'search_loop': 'distributed_algorithm.search_loop' \
+ , 'step': 'distributed_algorithm.step'}
# Get module and attribute names
- module_name = ".".join([special_ops, multiagent_ops[op.lower()]])
+ module_name = ".".join(
+ [special_ops, multiagent_ops[op.lower()]])
attr_name = split_name[-1]
else:
@@ -207,7 +210,6 @@ def get_fit_func_imports():
# List of multiple fitness functions given.
for i, name in enumerate(params[op]):
-
# Split import name based on "." to find nested modules.
split_name = name.strip().split(".")
diff --git a/src/utilities/algorithm/state.py b/src/utilities/algorithm/state.py
index ed45e610..a9df8e8d 100644
--- a/src/utilities/algorithm/state.py
+++ b/src/utilities/algorithm/state.py
@@ -1,6 +1,6 @@
import pickle
-from os import path
import random
+from os import path
def create_state(individuals):
@@ -13,7 +13,7 @@ def create_state(individuals):
:param individuals: A population of individuals to be saved.
:return: The complete state of a run.
"""
-
+
from algorithm.parameters import params
from stats.stats import stats
from utilities.stats import trackers
@@ -24,7 +24,7 @@ def create_state(individuals):
# Get random state.
random_state = random.getstate()
-
+
# Create a picklable version of the params dictionary. Since the params
# dictionary contains functions and class instances, we need to replace
# these with the names of their respective modules, since module
@@ -40,7 +40,7 @@ def create_state(individuals):
state = {"trackers": pickle_trackers, "params": pickle_params,
"stats": stats, "individuals": individuals,
"random_state": random_state, "time": state_time}
-
+
save_state(state)
@@ -52,10 +52,10 @@ def save_state(state):
:param state: A dictionary describing the current state of a run.
:return: Nothing.
"""
-
+
# Create pickle file
state_file = open(path.join(state['params']['FILE_PATH'], "state"), "wb")
-
+
# Save state information
pickle.dump(state, state_file)
@@ -71,19 +71,19 @@ def load_state(state):
a run.
:return: The loaded state of a run.
"""
-
+
# Open pickle file
state_file = open(state, "rb")
-
+
# Get state information
loaded_state = pickle.load(state_file)
# Close file.
state_file.close()
-
+
# Set state.
individuals = set_state(loaded_state)
-
+
# Return individuals.
return individuals
@@ -110,11 +110,11 @@ def set_state(state):
# Set random state.
random.setstate(state['random_state'])
-
+
# Set stats.
for stat in state['stats']:
stats[stat] = state['stats'][stat]
-
+
# Set trackers.
for tracker in state['trackers']:
setattr(trackers, tracker, state['trackers'][tracker])
@@ -126,7 +126,7 @@ def set_state(state):
# Set correct param imports for specified function options, including
# error metrics and fitness functions.
set_param_imports()
-
+
# Set time adjustment to account for old time.
stats['time_adjust'] = time() - state['time']
@@ -142,7 +142,7 @@ def check_name(obj):
:param obj: An object for which we want to find the name.
:return: The name of the object
"""
-
+
try:
return obj.__name__
except AttributeError:
diff --git a/src/utilities/fitness/error_metric.py b/src/utilities/fitness/error_metric.py
index 8c99c831..3f8e5c4f 100755
--- a/src/utilities/fitness/error_metric.py
+++ b/src/utilities/fitness/error_metric.py
@@ -15,6 +15,7 @@ def mae(y, yhat):
return np.mean(np.abs(y - yhat))
+
# Set maximise attribute for mae error metric.
mae.maximise = False
@@ -30,6 +31,7 @@ def rmse(y, yhat):
return np.sqrt(np.mean(np.square(y - yhat)))
+
# Set maximise attribute for rmse error metric.
rmse.maximise = False
@@ -45,6 +47,7 @@ def mse(y, yhat):
return np.mean(np.square(y - yhat))
+
# Set maximise attribute for mse error metric.
mse.maximise = False
@@ -74,6 +77,7 @@ def hinge(y, yhat):
# depend on the size of the dataset.
return np.mean(np.maximum(0, 1 - y * yhat))
+
# Set maximise attribute for hinge error metric.
hinge.maximise = False
@@ -117,6 +121,8 @@ def f1_score(y, yhat):
# return 0. We can ignore that warning and happily return 0.
warnings.simplefilter("ignore")
return sklearn_f1_score(y, yhat, average="weighted")
+
+
# Set maximise attribute for f1_score error metric.
f1_score.maximise = True
@@ -128,4 +134,6 @@ def Hamming_error(y, yhat):
Assumes both y and yhat are binary or integer-valued.
"""
return np.sum(y != yhat)
+
+
Hamming_error.maximise = False
diff --git a/src/utilities/fitness/get_data.py b/src/utilities/fitness/get_data.py
index c0066ce9..e63878a7 100644
--- a/src/utilities/fitness/get_data.py
+++ b/src/utilities/fitness/get_data.py
@@ -1,7 +1,6 @@
from os import path
import numpy as np
-
from algorithm.parameters import params
@@ -20,7 +19,7 @@ def get_Xy_train_test_separate(train_filename, test_filename, skip_header=0):
if params['DATASET_DELIMITER']:
# Dataset delimiter has been explicitly specified.
delimiter = params['DATASET_DELIMITER']
-
+
else:
# Try to auto-detect the field separator (i.e. delimiter).
f = open(train_filename)
@@ -28,7 +27,7 @@ def get_Xy_train_test_separate(train_filename, test_filename, skip_header=0):
if line.startswith("#") or len(line) < 2:
# Skip excessively short lines or commented out lines.
continue
-
+
else:
# Set the delimiter.
if "\t" in line:
@@ -44,22 +43,23 @@ def get_Xy_train_test_separate(train_filename, test_filename, skip_header=0):
delimiter = ":"
break
else:
- print("utilities.fitness.get_data.get_Xy_train_test_separate\n"
- "Warning: Dataset delimiter not found. "
- "Defaulting to whitespace delimiter.")
+ print(
+ "utilities.fitness.get_data.get_Xy_train_test_separate\n"
+ "Warning: Dataset delimiter not found. "
+ "Defaulting to whitespace delimiter.")
delimiter = " "
break
f.close()
-
+
# Read in all training data.
train_Xy = np.genfromtxt(train_filename, skip_header=skip_header,
delimiter=delimiter)
-
+
try:
# Separate out input (X) and output (y) data.
train_X = train_Xy[:, :-1].transpose() # all columns but last
train_y = train_Xy[:, -1].transpose() # last column
-
+
except IndexError:
s = "utilities.fitness.get_data.get_Xy_train_test_separate\n" \
"Error: specified delimiter '%s' incorrectly parses training " \
@@ -89,20 +89,20 @@ def get_data(train, test):
:param test: The desired testing dataset.
:return: The parsed data contained in the dataset files.
"""
-
+
# Get the path to the training dataset.
train_set = path.join("..", "datasets", train)
-
+
if test:
# Get the path to the testing dataset.
test_set = path.join("..", "datasets", test)
-
+
else:
# There is no testing dataset used.
test_set = None
-
+
# Read in the training and testing datasets from the specified files.
training_in, training_out, test_in, \
test_out = get_Xy_train_test_separate(train_set, test_set, skip_header=1)
-
+
return training_in, training_out, test_in, test_out
diff --git a/src/utilities/fitness/math_functions.py b/src/utilities/fitness/math_functions.py
index 82a3613e..9696b625 100644
--- a/src/utilities/fitness/math_functions.py
+++ b/src/utilities/fitness/math_functions.py
@@ -1,5 +1,7 @@
from math import ceil
+
import numpy as np
+
np.seterr(all="raise")
@@ -16,7 +18,7 @@ def return_one_percent(num, pop_size):
"""
# Calculate one percent of the given population size.
- percent = int(round(pop_size/100))
+ percent = int(round(pop_size / 100))
# Return the biggest number.
if percent < num:
@@ -48,7 +50,7 @@ def aq(a, b):
:return: np.array analytic quotient, analogous to a / b.
"""
- return a / np.sqrt(1.0 + b**2.0)
+ return a / np.sqrt(1.0 + b ** 2.0)
def pdiv(x, y):
@@ -114,7 +116,7 @@ def ppow(x, y):
:return: np.array x**y, but protected
"""
- return np.abs(x)**y
+ return np.abs(x) ** y
def ppow2(x, y):
@@ -246,5 +248,5 @@ def sci_notation(n, prec=3):
"""
base = 10
exponent = ilog(n, base)
- mantissa = n / base**exponent
+ mantissa = n / base ** exponent
return '{0:.{1}f}e{2:+d}'.format(mantissa, prec, exponent)
diff --git a/src/utilities/fitness/optimize_constants.py b/src/utilities/fitness/optimize_constants.py
index 8e7c47db..86e5556b 100644
--- a/src/utilities/fitness/optimize_constants.py
+++ b/src/utilities/fitness/optimize_constants.py
@@ -1,7 +1,6 @@
-import scipy
-
import re
+import scipy
from algorithm.parameters import params
from utilities.fitness.math_functions import *
@@ -23,7 +22,7 @@ def optimize_constants(x, y, ind):
# Parse the phenotype to make the constants consecutive.
s, n_consts = make_consts_consecutive(ind.phenotype)
-
+
# Create new consecutive constant attribute for individual.
ind.phenotype_consec_consts = s
@@ -46,9 +45,9 @@ def optimize_constants(x, y, ind):
# Maybe other minimizers do better with some other choices? There are other
# methods to try out.
init = [0.0] * n_consts
-
+
res = scipy.optimize.minimize(obj, init, method="L-BFGS-B")
-
+
# the result is accessed like a dict
ind.opt_consts = res['x'] # the optimum values of the constants
@@ -92,8 +91,8 @@ def replace_consts_with_values(s, c):
phenotype string.
:return: The phenotype string with the constants replaced.
"""
-
+
for i in range(len(c)):
s = s.replace("c[%d]" % i, str(c[i]))
-
+
return s
diff --git a/src/utilities/representation/check_methods.py b/src/utilities/representation/check_methods.py
index cb8d47cf..a3dd197a 100644
--- a/src/utilities/representation/check_methods.py
+++ b/src/utilities/representation/check_methods.py
@@ -1,6 +1,6 @@
+import numpy as np
from algorithm.parameters import params
from representation import individual
-import numpy as np
def check_ind(ind, check):
@@ -43,24 +43,24 @@ def check_genome_mapping(ind):
:param ind: An instance of the representation.individual.Individual class.
:return: Nothing.
"""
-
+
# Re-map individual using fast genome mapper to check everything is ok
new_ind = individual.Individual(ind.genome, None)
-
+
# Get attributes of both individuals.
attributes_0 = vars(ind)
attributes_1 = vars(new_ind)
-
+
if params['GENOME_OPERATIONS']:
# If this parameter is set then the new individual will have no tree.
attributes_0['tree'] = None
-
+
else:
if attributes_0['tree'] != attributes_1['tree']:
s = "utilities.representation.check_methods.check_ind.\n" \
"Error: Individual trees do not match."
raise Exception(s)
-
+
# Check that all attributes match across both individuals.
for a_0 in sorted(attributes_0.keys()):
for a_1 in sorted(attributes_1.keys()):
@@ -69,7 +69,6 @@ def check_genome_mapping(ind):
type(attributes_1[a_1]) is float and
np.isnan(attributes_0[a_0]) and
np.isnan(attributes_1[a_1])):
-
s = "utilities.representation.check_methods." \
"check_genome_mapping\n" \
"Error: Individual attributes do not match genome-" \
@@ -80,7 +79,7 @@ def check_genome_mapping(ind):
" %s :\t %s" % \
(a_0, attributes_0[a_0], a_1, attributes_1[a_1])
raise Exception(s)
-
+
def check_ind_from_parser(ind, target):
"""
@@ -95,7 +94,7 @@ def check_ind_from_parser(ind, target):
# Re-map individual using genome mapper to check everything is ok.
new_ind = individual.Individual(ind.genome, None)
-
+
# Check phenotypes are the same.
if new_ind.phenotype != ind.phenotype:
s = "utilities.representation.check_methods.check_ind_from_parser\n" \
@@ -105,7 +104,7 @@ def check_ind_from_parser(ind, target):
" Derived genome: \t %s" % \
(ind.phenotype, new_ind.phenotype, ind.genome)
raise Exception(s)
-
+
# Check the phenotype matches the target string.
elif ind.phenotype != target:
s = "utilities.representation.check_methods.check_ind_from_parser\n" \
@@ -113,7 +112,7 @@ def check_ind_from_parser(ind, target):
" Target: \t %s\n" \
" Solution: \t %s" % (target, ind.phenotype)
raise Exception(s)
-
+
else:
# Check the tree matches the phenotype.
check_genome_mapping(ind)
@@ -131,17 +130,17 @@ def check_genome_from_tree(ind_tree):
if ind_tree.children:
# This node has children and thus must have an associated codon.
-
+
if not ind_tree.codon:
s = "utilities.representation.check_methods." \
"check_genome_from_tree\n" \
"Error: Node with children has no codon.\n" \
" %s" % (str(ind_tree.children))
raise Exception(s)
-
+
# Check production choices for node root.
productions = params['BNF_GRAMMAR'].rules[ind_tree.root]['choices']
-
+
# Select choice based on node codon.
selection = ind_tree.codon % len(productions)
chosen_prod = productions[selection]
@@ -153,7 +152,7 @@ def check_genome_from_tree(ind_tree):
# Build list of the roots of all node children.
for kid in ind_tree.children:
roots.append(kid.root)
-
+
# Match production roots with children roots.
if roots != prods:
s = "utilities.representation.check_methods." \
@@ -162,7 +161,7 @@ def check_genome_from_tree(ind_tree):
" Codon productions:\t%s\n " \
" Actual children:\t%s" % (str(prods), str(roots))
raise Exception(s)
-
+
for kid in ind_tree.children:
# Recurse over all children.
check_genome_from_tree(kid)
@@ -177,7 +176,7 @@ def check_expansion(tree, nt_keys):
:param nt_keys: The list of all non-terminals.
:return: True if tree is not fully expanded, else False.
"""
-
+
check = False
if tree.root in nt_keys:
# Current node is a NT and should have children
@@ -186,15 +185,15 @@ def check_expansion(tree, nt_keys):
for child in tree.children:
# Recurse over all children.
check = child.check_expansion(nt_keys)
-
+
if check:
# End recursion.
break
-
+
else:
# Current node is not completely expanded.
check = True
-
+
return check
@@ -206,15 +205,15 @@ def build_genome(tree, genome):
:param genome: The list of all codons in a subtree.
:return: The fully built genome of a subtree.
"""
-
+
if tree.codon:
# If the current node has a codon, append it to the genome.
genome.append(tree.codon)
-
+
for child in tree.children:
# Recurse on all children.
genome = child.build_genome(genome)
-
+
return genome
@@ -236,29 +235,29 @@ def get_nodes_and_depth(tree, nodes=0, max_depth=0):
tree.depth = tree.parent.depth + 1
else:
tree.depth = 1
-
+
# Check the recorded max_depth.
if tree.depth > max_depth:
max_depth = tree.depth
-
+
# Create list of all non-terminal children of current node.
NT_kids = [kid for kid in tree.children if kid.root in
params['BNF_GRAMMAR'].non_terminals]
-
+
if not NT_kids and get_output(tree):
# Current node has only terminal children.
nodes += 1
-
+
# Terminal children increase the current node depth by one.
# Check the recorded max_depth.
if tree.depth + 1 > max_depth:
max_depth = tree.depth + 1
-
+
else:
for child in NT_kids:
# Recurse over all children.
nodes, max_depth = get_nodes_and_depth(child, nodes, max_depth)
-
+
return nodes, max_depth
@@ -270,7 +269,7 @@ def get_max_tree_depth(tree, max_depth=1):
:param max_depth: The maximum depth of the tree.
:return: The maximum depth of the tree.
"""
-
+
curr_depth = get_current_depth(tree)
if curr_depth > max_depth:
max_depth = curr_depth
@@ -287,22 +286,22 @@ def get_current_depth(tree):
:param tree: An individual's derivation tree.
:return: The depth of the current node.
"""
-
+
# Set the initial depth at 1.
depth = 1
-
+
# Set the current parent.
current_parent = tree.parent
-
+
while current_parent is not None:
# Recurse until the root node of the tree has been reached.
-
+
# Increment depth.
depth += 1
-
+
# Set new parent.
current_parent = current_parent.parent
-
+
return depth
@@ -316,7 +315,7 @@ def get_output(ind_tree):
:param ind_tree: a full tree for which the phenotype string is to be built.
:return: The complete built phenotype string of an individual.
"""
-
+
def build_output(tree):
"""
Recursively adds all node roots to a list which can be joined to
@@ -324,21 +323,21 @@ def build_output(tree):
:return: The list of all node roots.
"""
-
+
output = []
for child in tree.children:
if not child.children:
# If the current child has no children it is a terminal.
# Append it to the output.
output.append(child.root)
-
+
else:
# Otherwise it is a non-terminal. Recurse on all
# non-terminals.
output += build_output(child)
-
+
return output
-
+
return "".join(build_output(ind_tree))
@@ -391,16 +390,16 @@ def check_tree(tree):
:param tree: A tree.
:return: Nothing.
"""
-
+
if tree.children:
-
+
if not tree.codon:
s = "utilities.representation.check_methods.check_tree\n" \
"Error: Node with children has no associated codon."
raise Exception(s)
-
+
for child in tree.children:
-
+
if child.parent != tree:
s = "utilities.representation.check_methods.check_tree\n" \
"Error: Child doesn't belong to parent.\n" \
diff --git a/src/utilities/representation/python_filter.py b/src/utilities/representation/python_filter.py
index 931471a0..5b968bed 100644
--- a/src/utilities/representation/python_filter.py
+++ b/src/utilities/representation/python_filter.py
@@ -9,7 +9,7 @@ def python_filter(txt):
tmp = txt[:]
i = 0
while i < len(tmp):
- tok = tmp[i:i+2]
+ tok = tmp[i:i + 2]
if tok == "{:":
indent_level += 1
elif tok == ":}":
diff --git a/src/utilities/stats/clean_stats.py b/src/utilities/stats/clean_stats.py
index ee6e4990..62bdffe1 100644
--- a/src/utilities/stats/clean_stats.py
+++ b/src/utilities/stats/clean_stats.py
@@ -1,5 +1,5 @@
-from stats.stats import stats
from algorithm.parameters import params
+from stats.stats import stats
def clean_stats():
@@ -9,10 +9,10 @@ def clean_stats():
:return: Nothing.
"""
-
+
if not params['CACHE']:
stats.pop('unique_inds')
stats.pop('unused_search')
-
+
if not params['MUTATE_DUPLICATES']:
stats.pop('regens')
diff --git a/src/utilities/stats/file_io.py b/src/utilities/stats/file_io.py
index d4438bba..cc0b57d3 100644
--- a/src/utilities/stats/file_io.py
+++ b/src/utilities/stats/file_io.py
@@ -1,6 +1,6 @@
-from os import path, getcwd, makedirs
-from shutil import rmtree
from copy import copy
+from os import getcwd, makedirs, path
+from shutil import rmtree
from algorithm.parameters import params
from utilities.stats import trackers
@@ -94,11 +94,10 @@ def save_first_front_to_file(stats, end=False, name="first"):
orig_file_path = copy(params['FILE_PATH'])
# Define the new file path.
- params['FILE_PATH'] = path.join(orig_file_path, str(name)+"_front")
+ params['FILE_PATH'] = path.join(orig_file_path, str(name) + "_front")
# Check if the front folder exists already
if path.exists(params['FILE_PATH']):
-
# Remove previous files.
rmtree(params['FILE_PATH'])
@@ -142,7 +141,7 @@ def generate_folders_and_files():
if not path.isdir(path.join(params['FILE_PATH'],
str(params['TIME_STAMP']))):
makedirs(path.join(params['FILE_PATH'],
- str(params['TIME_STAMP'])))
+ str(params['TIME_STAMP'])))
params['FILE_PATH'] = path.join(params['FILE_PATH'],
str(params['TIME_STAMP']))
@@ -165,7 +164,6 @@ def save_params_to_file():
col_width = max(len(param) for param in params.keys())
for param in sorted(params.keys()):
-
# Create whitespace buffer for pretty printing/saving.
spaces = [" " for _ in range(col_width - len(param))]
savefile.write(str(param) + ": " + "".join(spaces) +
diff --git a/src/utilities/stats/save_plots.py b/src/utilities/stats/save_plots.py
index 38cf3f8c..0ba232b6 100644
--- a/src/utilities/stats/save_plots.py
+++ b/src/utilities/stats/save_plots.py
@@ -1,12 +1,13 @@
-import matplotlib
-import pandas as pd
from os import path, pathsep
-import numpy as np
+import matplotlib
+import numpy as np
+import pandas as pd
from utilities.stats.trackers import first_pareto_list
matplotlib.use('Agg')
import matplotlib.pyplot as plt
+
plt.rc('font', family='Times New Roman')
@@ -48,7 +49,8 @@ def save_pareto_fitness_plot():
# Set up colorbar instead of legend. Normalise axis to scale of data.
sm = plt.cm.ScalarMappable(cmap="jet",
- norm=plt.Normalize(vmin=0, vmax=len(first_pareto_list) - 1))
+ norm=plt.Normalize(vmin=0, vmax=len(
+ first_pareto_list) - 1))
# Fake up the array of the scalar mappable.
sm._A = []