Browse files

First commit, end of the chapter source code

  • Loading branch information...
0 parents commit 486418e40f99f9039af2ea3dbb8c96e9eb84c400 @CorticalComputer committed Nov 1, 2012
Showing with 18,928 additions and 0 deletions.
  1. +554 −0 Ch_11/LICENSE.txt
  2. +48 −0 Ch_11/actuator.erl
  3. +53 −0 Ch_11/benchmarker.erl
  4. +57 −0 Ch_11/cortex.erl
  5. +282 −0 Ch_11/exoself.erl
  6. +19 −0 Ch_11/fitness_postprocessor.erl
  7. +165 −0 Ch_11/functions.erl
  8. +854 −0 Ch_11/genome_mutator.erl
  9. +439 −0 Ch_11/genotype.erl
  10. +40 −0 Ch_11/morphology.erl
  11. +110 −0 Ch_11/neuron.erl
  12. +13 −0 Ch_11/plasticity.erl
  13. +154 −0 Ch_11/polis.erl
  14. +534 −0 Ch_11/population_monitor.erl
  15. +161 −0 Ch_11/records.hrl
  16. +52 −0 Ch_11/scape.erl
  17. +106 −0 Ch_11/selection_algorithm.erl
  18. +54 −0 Ch_11/sensor.erl
  19. +63 −0 Ch_11/signal_aggregator.erl
  20. +28 −0 Ch_11/tot_topological_mutations.erl
  21. +50 −0 Ch_11/tuning_duration.erl
  22. +123 −0 Ch_11/tuning_selection.erl
  23. +554 −0 Ch_12and13/LICENSE.txt
  24. +49 −0 Ch_12and13/actuator.erl
  25. +244 −0 Ch_12and13/benchmarker.erl
  26. +146 −0 Ch_12and13/benchmarks/DiversityVsEvaluations.gp
  27. BIN Ch_12and13/benchmarks/DiversityVsEvaluations.png
  28. +146 −0 Ch_12and13/benchmarks/FitnessVsEvaluations.gp
  29. BIN Ch_12and13/benchmarks/FitnessVsEvaluations.png
  30. +8 −0 Ch_12and13/benchmarks/export.gp
  31. +136 −0 Ch_12and13/benchmarks/graph_xor_mimic_report_Graphs
  32. +645 −0 Ch_12and13/benchmarks/report_Trace_Acc
  33. +57 −0 Ch_12and13/cortex.erl
  34. +284 −0 Ch_12and13/exoself.erl
  35. +19 −0 Ch_12and13/fitness_postprocessor.erl
  36. +165 −0 Ch_12and13/functions.erl
  37. +854 −0 Ch_12and13/genome_mutator.erl
  38. +491 −0 Ch_12and13/genotype.erl
  39. +40 −0 Ch_12and13/morphology.erl
  40. +111 −0 Ch_12and13/neuron.erl
  41. +13 −0 Ch_12and13/plasticity.erl
  42. +155 −0 Ch_12and13/polis.erl
  43. +714 −0 Ch_12and13/population_monitor.erl
  44. +191 −0 Ch_12and13/records.hrl
  45. +52 −0 Ch_12and13/scape.erl
  46. +106 −0 Ch_12and13/selection_algorithm.erl
  47. +54 −0 Ch_12and13/sensor.erl
  48. +63 −0 Ch_12and13/signal_aggregator.erl
  49. +28 −0 Ch_12and13/tot_topological_mutations.erl
  50. +50 −0 Ch_12and13/tuning_duration.erl
  51. +123 −0 Ch_12and13/tuning_selection.erl
  52. +554 −0 Ch_14/LICENSE.txt
  53. +64 −0 Ch_14/actuator.erl
  54. +247 −0 Ch_14/benchmarker.erl
  55. +76 −0 Ch_14/benchmarks/graph_discrete_tmaze_report_Graphs
  56. +40 −0 Ch_14/benchmarks/graph_discrete_tmaze_test_Graphs
  57. +22 −0 Ch_14/benchmarks/graph_pole_balancing_report_Graphs
  58. +136 −0 Ch_14/benchmarks/graph_xor_mimic_report_Graphs
  59. +761 −0 Ch_14/benchmarks/report_Trace_Acc
  60. +826 −0 Ch_14/benchmarks/test_Trace_Acc
  61. +63 −0 Ch_14/cortex.erl
  62. +293 −0 Ch_14/exoself.erl
  63. +19 −0 Ch_14/fitness_postprocessor.erl
  64. +165 −0 Ch_14/functions.erl
  65. +851 −0 Ch_14/genome_mutator.erl
  66. +491 −0 Ch_14/genotype.erl
  67. +59 −0 Ch_14/morphology.erl
  68. +113 −0 Ch_14/neuron.erl
  69. +13 −0 Ch_14/plasticity.erl
  70. +154 −0 Ch_14/polis.erl
  71. +734 −0 Ch_14/population_monitor.erl
  72. +184 −0 Ch_14/records.hrl
  73. +302 −0 Ch_14/scape.erl
  74. +106 −0 Ch_14/selection_algorithm.erl
  75. +81 −0 Ch_14/sensor.erl
  76. +63 −0 Ch_14/signal_aggregator.erl
  77. +28 −0 Ch_14/tot_topological_mutations.erl
  78. +50 −0 Ch_14/tuning_duration.erl
  79. +123 −0 Ch_14/tuning_selection.erl
  80. +554 −0 Ch_15/LICENSE.txt
  81. +64 −0 Ch_15/actuator.erl
  82. +246 −0 Ch_15/benchmarker.erl
  83. +76 −0 Ch_15/benchmarks/graph_discrete_tmaze_hebbian_Graphs
  84. +76 −0 Ch_15/benchmarks/graph_discrete_tmaze_report_Graphs
  85. +76 −0 Ch_15/benchmarks/graph_discrete_tmaze_test_Graphs
  86. +22 −0 Ch_15/benchmarks/graph_pole_balancing_report_Graphs
  87. +136 −0 Ch_15/benchmarks/graph_xor_mimic_report_Graphs
  88. +799 −0 Ch_15/benchmarks/hebbian_Trace_Acc
  89. +804 −0 Ch_15/benchmarks/report_Trace_Acc
Sorry, we could not display the entire diff because it was too big.
554 Ch_11/LICENSE.txt
@@ -0,0 +1,554 @@
+Copyright (C) 2009 by Gene Sher, CorticalComputer@gmail.com
+All rights reserved.
+
+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
48 Ch_11/actuator.erl
@@ -0,0 +1,48 @@
+%% This source code and work is provided and developed by DXNN Research Group WWW.DXNNResearch.COM
+%%
+%Copyright (C) 2012 by Gene Sher, DXNN Research Group, CorticalComputer@gmail.com
+%All rights reserved.
+%
+%This code is licensed under the version 3 of the GNU General Public License. Please see the LICENSE file that accompanies this project for the terms of use.
+
+-module(actuator).
+-compile(export_all).
+-include("records.hrl").
+
+gen(ExoSelf_PId,Node)->
+ spawn(Node,?MODULE,prep,[ExoSelf_PId]).
+
+prep(ExoSelf_PId) ->
+ receive
+ {ExoSelf_PId,{Id,Cx_PId,Scape,ActuatorName,Fanin_PIds}} ->
+ loop(Id,ExoSelf_PId,Cx_PId,Scape,ActuatorName,{Fanin_PIds,Fanin_PIds},[])
+ end.
+%When gen/2 is executed it spawns the actuator element and immediately begins to wait for its initial state message.
+
+loop(Id,ExoSelf_PId,Cx_PId,Scape,AName,{[From_PId|Fanin_PIds],MFanin_PIds},Acc) ->
+ receive
+ {From_PId,forward,Input} ->
+ loop(Id,ExoSelf_PId,Cx_PId,Scape,AName,{Fanin_PIds,MFanin_PIds},lists:append(Input,Acc));
+ {ExoSelf_PId,terminate} ->
+ io:format("Actuator:~p is terminating.~n",[self()])
+ end;
+loop(Id,ExoSelf_PId,Cx_PId,Scape,AName,{[],MFanin_PIds},Acc)->
+ {Fitness,EndFlag} = actuator:AName(lists:reverse(Acc),Scape),
+ Cx_PId ! {self(),sync,Fitness,EndFlag},
+ loop(Id,ExoSelf_PId,Cx_PId,Scape,AName,{MFanin_PIds,MFanin_PIds},[]).
+%The actuator process gathers the control signals from the neurons, appending them to the accumulator. The order in which the signals are accumulated into a vector is in the same order as the neuron ids are stored within NIds. Once all the signals have been gathered, the actuator sends cortex the sync signal, executes its function, and then again begins to wait for the neural signals from the output layer by reseting the Fanin_PIds from the second copy of the list.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ACTUATORS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+pts(Result,_Scape)->
+ io:format("actuator:pts(Result): ~p~n",[Result]),
+ {1,0}.
+%The pts/2 actuation function simply prints to screen the vector passed to it.
+
+xor_SendOutput(Output,Scape)->
+ Scape ! {self(),action,Output},
+ receive
+ {Scape,Fitness,HaltFlag}->
+ {Fitness,HaltFlag}
+ end.
+%xor_sim/2 function simply forwards the Output vector to the XOR simulator, and waits for the resulting Fitness and EndFlag from the simulation process.
53 Ch_11/benchmarker.erl
@@ -0,0 +1,53 @@
+%% This source code and work is provided and developed by DXNN Research Group WWW.DXNNResearch.COM
+%%
+%Copyright (C) 2012 by Gene Sher, DXNN Research Group, CorticalComputer@gmail.com
+%All rights reserved.
+%
+%This code is licensed under the version 3 of the GNU General Public License. Please see the LICENSE file that accompanies this project for the terms of use.
+
+-module(benchmarker).
+-compile(export_all).
+-include("records.hrl").
+-define(MAX_ATTEMPTS,5).
+-define(EVAL_LIMIT,inf).
+-define(FITNESS_TARGET,inf).
+-define(TOT_RUNS,100).
+-define(MORPHOLOGY,xor_mimic).
+
+go(Morphology,HiddenLayerDensities)->
+ go(Morphology,HiddenLayerDensities,?TOT_RUNS).
+go(Morphology,HiddenLayerDensities,TotRuns)->
+ go(Morphology,HiddenLayerDensities,?MAX_ATTEMPTS,?EVAL_LIMIT,?FITNESS_TARGET,TotRuns).
+go(Morphology,HiddenLayerDensities,MaxAttempts,EvalLimit,FitnessTarget,TotRuns)->
+ PId = spawn(benchmarker,loop,[Morphology,HiddenLayerDensities,MaxAttempts,EvalLimit,FitnessTarget,TotRuns,[],[],[],[]]),
+ register(benchmarker,PId).
+%
+
+loop(Morphology,_HiddenLayerDensities,_MA,_EL,_FT,0,FitnessAcc,EvalsAcc,CyclesAcc,TimeAcc)->
+ io:format("Benchmark results for:~p~n",[Morphology]),
+ io:format("Fitness::~n Max:~p~n Min:~p~n Avg:~p~n Std:~p~n",[lists:max(FitnessAcc),lists:min(FitnessAcc),avg(FitnessAcc),std(FitnessAcc)]),
+ io:format("Evals::~n Max:~p~n Min:~p~n Avg:~p~n Std:~p~n",[lists:max(EvalsAcc),lists:min(EvalsAcc),avg(EvalsAcc),std(EvalsAcc)]),
+ io:format("Cycles::~n Max:~p~n Min:~p~n Avg:~p~n Std:~p~n",[lists:max(CyclesAcc),lists:min(CyclesAcc),avg(CyclesAcc),std(CyclesAcc)]),
+ io:format("Time::~n Max:~p~n Min:~p~n Avg:~p~n Std:~p~n",[lists:max(TimeAcc),lists:min(TimeAcc),avg(TimeAcc),std(TimeAcc)]);
+loop(Morphology,HiddenLayerDensities,MA,EL,FT,BenchmarkIndex,FitnessAcc,EvalsAcc,CyclesAcc,TimeAcc)->
+ Trainer_PId = trainer:go(Morphology,HiddenLayerDensities,MA,EL,FT),
+ receive
+ {Trainer_PId,Fitness,Evals,Cycles,Time}->
+ loop(Morphology,HiddenLayerDensities,MA,EL,FT,BenchmarkIndex-1,[Fitness|FitnessAcc],[Evals|EvalsAcc],[Cycles|CyclesAcc],[Time|TimeAcc]);
+ terminate ->
+ loop(Morphology,HiddenLayerDensities,MA,EL,FT,0,FitnessAcc,EvalsAcc,CyclesAcc,TimeAcc)
+ end.
+%
+
+avg(List)->
+ lists:sum(List)/length(List).
+std(List)->
+ Avg = avg(List),
+ std(List,Avg,[]).
+
+ std([Val|List],Avg,Acc)->
+ std(List,Avg,[math:pow(Avg-Val,2)|Acc]);
+ std([],_Avg,Acc)->
+ Variance = lists:sum(Acc)/length(Acc),
+ math:sqrt(Variance).
+%
57 Ch_11/cortex.erl
@@ -0,0 +1,57 @@
+%% This source code and work is provided and developed by DXNN Research Group WWW.DXNNResearch.COM
+%%
+%Copyright (C) 2012 by Gene Sher, DXNN Research Group, CorticalComputer@gmail.com
+%All rights reserved.
+%
+%This code is licensed under the version 3 of the GNU General Public License. Please see the LICENSE file that accompanies this project for the terms of use.
+
+-module(cortex).
+-compile(export_all).
+-include("records.hrl").
+-record(state,{id,exoself_pid,spids,npids,apids,cycle_acc=0,fitness_acc=0,endflag=0,status}).
+
+gen(ExoSelf_PId,Node)->
+ spawn(Node,?MODULE,prep,[ExoSelf_PId]).
+
+prep(ExoSelf_PId) ->
+ {V1,V2,V3} = now(),
+ random:seed(V1,V2,V3),
+ receive
+ {ExoSelf_PId,Id,SPIds,NPIds,APIds} ->
+ put(start_time,now()),
+ [SPId ! {self(),sync} || SPId <- SPIds],
+ loop(Id,ExoSelf_PId,SPIds,{APIds,APIds},NPIds,1,0,0,active)
+ end.
+%The gen/2 function spawns the cortex element, which immediately starts to wait for a the state message from the same process that spawned it, exoself. The initial state message contains the sensor, actuator, and neuron PId lists. The message also specifies how many total Sense-Think-Act cycles the Cortex should execute before terminating the NN system. Once we implement the learning algorithm, the termination criteria will depend on the fitness of the NN, or some other useful property
+
+loop(Id,ExoSelf_PId,SPIds,{[APId|APIds],MAPIds},NPIds,CycleAcc,FitnessAcc,EFAcc,active) ->
+ receive
+ {APId,sync,Fitness,EndFlag} ->
+ loop(Id,ExoSelf_PId,SPIds,{APIds,MAPIds},NPIds,CycleAcc,FitnessAcc+Fitness,EFAcc+EndFlag,active);
+ terminate ->
+ io:format("Cortex:~p is terminating.~n",[Id]),
+ [PId ! {self(),terminate} || PId <- SPIds],
+ [PId ! {self(),terminate} || PId <- MAPIds],
+ [PId ! {self(),termiante} || PId <- NPIds]
+ end;
+loop(Id,ExoSelf_PId,SPIds,{[],MAPIds},NPIds,CycleAcc,FitnessAcc,EFAcc,active)->
+ case EFAcc > 0 of
+ true ->%Organism finished evaluation
+ TimeDif=timer:now_diff(now(),get(start_time)),
+ ExoSelf_PId ! {self(),evaluation_completed,FitnessAcc,CycleAcc,TimeDif},
+ loop(Id,ExoSelf_PId,SPIds,{MAPIds,MAPIds},NPIds,CycleAcc,FitnessAcc,EFAcc,inactive);
+ false ->
+ [PId ! {self(),sync} || PId <- SPIds],
+ loop(Id,ExoSelf_PId,SPIds,{MAPIds,MAPIds},NPIds,CycleAcc+1,FitnessAcc,EFAcc,active)
+ end;
+loop(Id,ExoSelf_PId,SPIds,{MAPIds,MAPIds},NPIds,_CycleAcc,_FitnessAcc,_EFAcc,inactive)->
+ receive
+ {ExoSelf_PId,reactivate}->
+ put(start_time,now()),
+ [SPId ! {self(),sync} || SPId <- SPIds],
+ loop(Id,ExoSelf_PId,SPIds,{MAPIds,MAPIds},NPIds,1,0,0,active);
+ {ExoSelf_PId,terminate}->
+ io:format("Cortex:~p is terminating.~n",[Id]),
+ ok
+ end.
+%The cortex's goal is to synchronize the the NN system such that when the actuators have received all their control signals, the sensors are once again triggered to gather new sensory information. Thus the cortex waits for the sync messages from the actuator PIds in its system, and once it has received all the sync messages, it triggers the sensors and then drops back to waiting for a new set of sync messages. The cortex stores 2 copies of the actuator PIds: the APIds, and the MemoryAPIds (MAPIds). Once all the actuators have sent it the sync messages, it can restore the APIds list from the MAPIds. Finally, there is also the Step variable which decrements every time a full cycle of Sense-Think-Act completes, once this reaches 0, the NN system begins its termination and backup process.
282 Ch_11/exoself.erl
@@ -0,0 +1,282 @@
+%% This source code and work is provided and developed by DXNN Research Group WWW.DXNNResearch.COM
+%%
+%Copyright (C) 2012 by Gene Sher, DXNN Research Group, CorticalComputer@gmail.com
+%All rights reserved.
+%
+%This code is licensed under the version 3 of the GNU General Public License. Please see the LICENSE file that accompanies this project for the terms of use.
+
+-module(exoself).
+-compile(export_all).
+-include("records.hrl").
+-record(state,{
+ agent_id,
+ generation,
+ pm_pid,
+ idsNpids,
+ cx_pid,
+ spids=[],
+ npids=[],
+ nids=[],
+ apids=[],
+ scape_pids=[],
+ highest_fitness=0,
+ eval_acc=0,
+ cycle_acc=0,
+ time_acc=0,
+ max_attempts=10,
+ attempt=1,
+ tuning_duration_f,
+ tuning_selection_f,
+ annealing_parameter,
+ perturbation_range
+}).
+-define(MAX_ATTEMPTS,10).
+
+start(Agent_Id)->
+ case whereis(monitor) of
+ undefined ->
+ io:format("start(Agent_Id):: 'monitor' is not registered~n");
+ PId ->
+ start(Agent_Id,PId)
+ end.
+start(Agent_Id,PM_PId)->
+ spawn(exoself,prep,[Agent_Id,PM_PId]).
+%The start/2 function spawns a new Agent_Id exoself process, belonging to the population_monitor process with the pid PM_PId.
+
+prep(Agent_Id,PM_PId)->
+ random:seed(now()),
+ IdsNPIds = ets:new(idsNpids,[set,private]),
+ A = genotype:dirty_read({agent,Agent_Id}),
+ Cx = genotype:dirty_read({cortex,A#agent.cx_id}),
+ SIds = Cx#cortex.sensor_ids,
+ AIds = Cx#cortex.actuator_ids,
+ NIds = Cx#cortex.neuron_ids,
+ ScapePIds = spawn_Scapes(IdsNPIds,SIds,AIds),
+ spawn_CerebralUnits(IdsNPIds,cortex,[Cx#cortex.id]),
+ spawn_CerebralUnits(IdsNPIds,sensor,SIds),
+ spawn_CerebralUnits(IdsNPIds,actuator,AIds),
+ spawn_CerebralUnits(IdsNPIds,neuron,NIds),
+ link_Sensors(SIds,IdsNPIds),
+ link_Actuators(AIds,IdsNPIds),
+ link_Neurons(NIds,IdsNPIds),
+ {SPIds,NPIds,APIds}=link_Cortex(Cx,IdsNPIds),
+ Cx_PId = ets:lookup_element(IdsNPIds,Cx#cortex.id,2),
+ {TuningDurationFunction,Parameter} = A#agent.tuning_duration_f,
+ S = #state{
+ agent_id=Agent_Id,
+ generation=A#agent.generation,
+ pm_pid=PM_PId,
+ idsNpids=IdsNPIds,
+ cx_pid=Cx_PId,
+ spids=SPIds,
+ npids=NPIds,
+ nids=NIds,
+ apids=APIds,
+ scape_pids=ScapePIds,
+ max_attempts= tuning_duration:TuningDurationFunction(Parameter,NIds,A#agent.generation),
+ tuning_selection_f=A#agent.tuning_selection_f,
+ annealing_parameter=A#agent.annealing_parameter,
+ tuning_duration_f=A#agent.tuning_duration_f,
+ perturbation_range=A#agent.perturbation_range
+ },
+ loop(S).
+%The prep/2 function prepares and sets up the exoself's state before dropping into the main loop. The function first reads the agent and cortex records belonging to the Agent_Id NN based system. The function then reads the sensor, actuator, and neuron ids, then spawns the private scapes using the spawn_Scapes/3 function, spawns the cortex, sensor, actuator, and neuron processes, and then finally links up all these processes together using the link_.../2 processes. Once the phenotype has been generated from the genotype, the exoself drops into its main loop.
+
+loop(S)->
+ receive
+ {Cx_PId,evaluation_completed,Fitness,Cycles,Time}->
+ IdsNPIds = S#state.idsNpids,
+ {U_HighestFitness,U_Attempt}=case Fitness > S#state.highest_fitness of
+ true ->
+ [NPId ! {self(),weight_backup} || NPId <- S#state.npids],
+ {Fitness,0};
+ false ->
+ Perturbed_NIdPs=get(perturbed),
+ [ets:lookup_element(IdsNPIds,NId,2) ! {self(),weight_restore} || {NId,_Spread} <- Perturbed_NIdPs],
+ {S#state.highest_fitness,S#state.attempt+1}
+ end,
+ [PId ! {self(), reset_prep} || PId <- S#state.npids],
+ gather_acks(length(S#state.npids)),
+ [PId ! {self(), reset} || PId <- S#state.npids],
+ %io:format("HighestFitness:~p U_Attempt:~p~n",[HighestFitness,U_Attempt]),
+ U_CycleAcc = S#state.cycle_acc+Cycles,
+ U_TimeAcc = S#state.time_acc+Time,
+ U_EvalAcc = S#state.eval_acc+1,
+ case U_Attempt >= S#state.max_attempts of
+ true -> %End training
+ A=genotype:dirty_read({agent,S#state.agent_id}),
+ genotype:write(A#agent{fitness=U_HighestFitness}),
+ backup_genotype(S#state.idsNpids,S#state.npids),
+ terminate_phenotype(S#state.cx_pid,S#state.spids,S#state.npids,S#state.apids,S#state.scape_pids),
+ io:format("Agent:~p terminating. Genotype has been backed up.~n Fitness:~p~n TotEvaluations:~p~n TotCycles:~p~n TimeAcc:~p~n",
+ [self(),U_HighestFitness,U_EvalAcc,U_CycleAcc,U_TimeAcc]),
+ gen_server:cast(S#state.pm_pid,{S#state.agent_id,terminated,U_HighestFitness,U_EvalAcc,U_CycleAcc,U_TimeAcc});
+ false -> %Continue training
+ %io:format("exoself state:~p~n",[S]),
+ TuningSelectionFunction=S#state.tuning_selection_f,
+ PerturbationRange = S#state.perturbation_range,
+ AnnealingParameter = S#state.annealing_parameter,
+ ChosenNIdPs=tuning_selection:TuningSelectionFunction(S#state.nids,S#state.generation,PerturbationRange,AnnealingParameter),
+ [ets:lookup_element(IdsNPIds,NId,2) ! {self(),weight_perturb,Spread} || {NId,Spread} <- ChosenNIdPs],
+ %io:format("ChosenNPIds:~p~n",[ChosenNIdPs]),
+ put(perturbed,ChosenNIdPs),
+ Cx_PId ! {self(),reactivate},
+ U_S =S#state{
+ cycle_acc=U_CycleAcc,
+ time_acc=U_TimeAcc,
+ eval_acc=U_EvalAcc,
+ attempt=U_Attempt,
+ highest_fitness=U_HighestFitness
+ },
+ loop(U_S)
+ end
+ after 10000 ->
+ io:format("exoself:~p stuck.~n",[S#state.agent_id])
+ end.
+%The exoself process' main loop awaits from its cortex proccess the evoluation_completed message. Once the message is received, based on the fitness achieved, exoself decides whether to continue tunning the weights or terminate the system. Exoself tries to improve the fitness by perturbing/tuning the weights of its neurons, after each tuning session, the Neural Network based system performs another evaluation by interacting with the scape until completion (the NN solves a problem, or dies within the scape or...). The order of events is important: When evaluation_completed message is received, the function first checks whether the newly achieved fitness is higher than the highest fitness achieved so far. If it is not, the function sends the neurons a message to restore their weights to previous state, during which it last acehived the highest fitness instead of their current state which yielded the current lower fitness score. If on the other hand the new fitness is higher than the previously highest achieved fitness, then the function tells the neurons to backup their current weights, as these weights represent the NN's best, most fit form yet. Exoself then tells all the neurons to prepare for a reset by sending each neuron the {self(),reset_prep} message. Since the NN can have recursive connections, and the manner in which initial recursive messages are sent, it is important for each neuron to flush their buffers to be reset into an initial fresh state, which is achieved after the neurons receive the reset_prep message. The function then sends the reset message to the neurons, which returns them into their main loop. Finally, the function checks whether exoself has already tried to improve the NN's fitness a maximum S#state.max_attempts number of times. If that is the case, the exoself process backs up the updated NN (the updated, tuned weights) to database using the backup_genotype/2 function, prints to screen that it is terminating, and sends to the population_monitor the acumulated statistics (highest fitness, evaluation count, cycle count...). On the other hand, if the exoself is not yet done tuning the neural weights, it has not yet reached its ending condition, it uses a tuning_selection_function to compose a list of tuples: [{NId,Spread}...] of neuron ids and the perturbation spread values, where the spread is the range from which the perturbation is randomly chosen. The spread itself is based on the age of the slected neuron, using the annealing_factor value, which when set to 1 implies that there is no annealing, and when set to a value less than 1, decreases the Spread. Once this list of elements is composed, the exoself sends each of the neurons a message to perturb their synaptic weights using the Spread value. The exoself then reactivates the cortex, and drops back into its main loop.
+
+ spawn_CerebralUnits(IdsNPIds,CerebralUnitType,[Id|Ids])->
+ PId = CerebralUnitType:gen(self(),node()),
+ ets:insert(IdsNPIds,{Id,PId}),
+ ets:insert(IdsNPIds,{PId,Id}),
+ spawn_CerebralUnits(IdsNPIds,CerebralUnitType,Ids);
+ spawn_CerebralUnits(IdsNPIds,_CerebralUnitType,[])->
+ ets:insert(IdsNPIds,{bias,bias}).
+%We spawn the process for each element based on its type: CerebralUnitType, and the gen function that belongs to the CerebralUnitType module. We then enter the {Id,PId} tuple into our ETS table for later use.
+
+ spawn_Scapes(IdsNPIds,Sensor_Ids,Actuator_Ids)->
+ Sensor_Scapes = [(genotype:dirty_read({sensor,Id}))#sensor.scape || Id<-Sensor_Ids],
+ Actuator_Scapes = [(genotype:dirty_read({actuator,Id}))#actuator.scape || Id<-Actuator_Ids],
+ Unique_Scapes = Sensor_Scapes++(Actuator_Scapes--Sensor_Scapes),
+ SN_Tuples=[{scape:gen(self(),node()),ScapeName} || {private,ScapeName}<-Unique_Scapes],
+ [ets:insert(IdsNPIds,{ScapeName,PId}) || {PId,ScapeName} <- SN_Tuples],
+ [ets:insert(IdsNPIds,{PId,ScapeName}) || {PId,ScapeName} <-SN_Tuples],
+ [PId ! {self(),ScapeName} || {PId,ScapeName} <- SN_Tuples],
+ [PId || {PId,_ScapeName} <-SN_Tuples].
+%The spawn_Scapes/3 function first extracts all the scapes that the sensors and actuators interface with, it then creates a filtered scape list which only holds unique scape records, after which it further only selects those scapes that are private, and spawns them.
+
+ link_Sensors([SId|Sensor_Ids],IdsNPIds) ->
+ S=genotype:dirty_read({sensor,SId}),
+ SPId = ets:lookup_element(IdsNPIds,SId,2),
+ Cx_PId = ets:lookup_element(IdsNPIds,S#sensor.cx_id,2),
+ SName = S#sensor.name,
+ Fanout_Ids = S#sensor.fanout_ids,
+ Fanout_PIds = [ets:lookup_element(IdsNPIds,Id,2) || Id <- Fanout_Ids],
+ Scape=case S#sensor.scape of
+ {private,ScapeName}->
+ ets:lookup_element(IdsNPIds,ScapeName,2)
+ end,
+ SPId ! {self(),{SId,Cx_PId,Scape,SName,S#sensor.vl,Fanout_PIds}},
+ link_Sensors(Sensor_Ids,IdsNPIds);
+ link_Sensors([],_IdsNPIds)->
+ ok.
+%The link_Sensors/2 function sends to the already spawned and waiting sensors their states, composed of the PId lists and other information which are needed by the sensors to link up and interface with other elements in the distributed phenotype.
+
+ link_Actuators([AId|Actuator_Ids],IdsNPIds) ->
+ A=genotype:dirty_read({actuator,AId}),
+ APId = ets:lookup_element(IdsNPIds,AId,2),
+ Cx_PId = ets:lookup_element(IdsNPIds,A#actuator.cx_id,2),
+ AName = A#actuator.name,
+ Fanin_Ids = A#actuator.fanin_ids,
+ Fanin_PIds = [ets:lookup_element(IdsNPIds,Id,2) || Id <- Fanin_Ids],
+ Scape=case A#actuator.scape of
+ {private,ScapeName}->
+ ets:lookup_element(IdsNPIds,ScapeName,2)
+ end,
+ APId ! {self(),{AId,Cx_PId,Scape,AName,Fanin_PIds}},
+ link_Actuators(Actuator_Ids,IdsNPIds);
+ link_Actuators([],_IdsNPIds)->
+ ok.
+%The link_Actuators2 function sends to the already spawned and waiting actuators their states, composed of the PId lists and other information which are needed by the actuators to link up and interface with other elements in the distributed phenotype.
+
+ link_Neurons([NId|Neuron_Ids],IdsNPIds) ->
+ N=genotype:dirty_read({neuron,NId}),
+ NPId = ets:lookup_element(IdsNPIds,NId,2),
+ Cx_PId = ets:lookup_element(IdsNPIds,N#neuron.cx_id,2),
+ AFName = N#neuron.af,
+ PFName = N#neuron.pf,
+ AggrFName = N#neuron.aggr_f,
+ Input_IdPs = N#neuron.input_idps,
+ Output_Ids = N#neuron.output_ids,
+ RO_Ids = N#neuron.ro_ids,
+ Input_PIdPs = convert_IdPs2PIdPs(IdsNPIds,Input_IdPs,[]),
+ Output_PIds = [ets:lookup_element(IdsNPIds,Id,2) || Id <- Output_Ids],
+ RO_PIds = [ets:lookup_element(IdsNPIds,Id,2) || Id <- RO_Ids],
+ NPId ! {self(),{NId,Cx_PId,AFName,PFName,AggrFName,Input_PIdPs,Output_PIds,RO_PIds}},
+ link_Neurons(Neuron_Ids,IdsNPIds);
+ link_Neurons([],_IdsNPIds)->
+ ok.
+%The link_Neurons/2 function sends to the already spawned and waiting neurons their states, composed of the PId lists and other information needed by the neurons to link up and interface with other elements in the distributed phenotype.
+
+ convert_IdPs2PIdPs(IdsNPIds,[{Id,Weights}|Fanin_IdPs],Acc)->
+ convert_IdPs2PIdPs(IdsNPIds,Fanin_IdPs,[{ets:lookup_element(IdsNPIds,Id,2),Weights}|Acc]);
+ convert_IdPs2PIdPs(_IdsNPIds,[],Acc)->
+ lists:reverse(Acc).
+%The convert_IdPs2PIdPs/3 converts the IdPs tuples into tuples that use PIds instead of Ids, such that the Neuron will know which weights are to be associated with which incoming vector signals. The last element is the bias, which is added to the list in a non tuple form. Afterwards, the list is reversed to take its proper order.
+
+ link_Cortex(Cx,IdsNPIds) ->
+ Cx_Id = Cx#cortex.id,
+ Cx_PId = ets:lookup_element(IdsNPIds,Cx_Id,2),
+ SIds = Cx#cortex.sensor_ids,
+ AIds = Cx#cortex.actuator_ids,
+ NIds = Cx#cortex.neuron_ids,
+ SPIds = [ets:lookup_element(IdsNPIds,SId,2) || SId <- SIds],
+ NPIds = [ets:lookup_element(IdsNPIds,NId,2) || NId <- NIds],
+ APIds = [ets:lookup_element(IdsNPIds,AId,2) || AId <- AIds],
+ Cx_PId ! {self(),Cx_Id,SPIds,NPIds,APIds},
+ {SPIds,NPIds,APIds}.
+%The link_Cortex/2 function sends to the already spawned and waiting cortex its state, composed of the PId lists and other information which is needed by the cortex to link up and interface with other elements in the distributed phenotype.
+
+backup_genotype(IdsNPIds,NPIds)->
+ Neuron_IdsNWeights = get_backup(NPIds,[]),
+ update_genotype(IdsNPIds,Neuron_IdsNWeights),
+ io:format("Finished updating genotype~n").
+
+ get_backup([NPId|NPIds],Acc)->
+ NPId ! {self(),get_backup},
+ receive
+ {NPId,NId,WeightTuples}->
+ get_backup(NPIds,[{NId,WeightTuples}|Acc])
+ end;
+ get_backup([],Acc)->
+ Acc.
+%The backup_genotype/2 uses get_backup/2 to contact all the neurons in its NN and request for the neuron's Ids and their Input_IdPs. Once the updated Input_IdPs from all the neurons have been accumulated, they are passed through the update_genotype/2 function to produce updated neurons, and write them to database.
+
+ update_genotype(IdsNPIds,[{N_Id,PIdPs}|WeightPs])->
+ N = genotype:dirty_read({neuron,N_Id}),
+ Updated_InputIdPs = convert_PIdPs2IdPs(IdsNPIds,PIdPs,[]),
+ U_N = N#neuron{input_idps = Updated_InputIdPs},
+ genotype:write(U_N),
+ %io:format("N:~p~n U_N:~p~n Genotype:~p~n U_Genotype:~p~n",[N,U_N,Genotype,U_Genotype]),
+ update_genotype(IdsNPIds,WeightPs);
+ update_genotype(_IdsNPIds,[])->
+ ok.
+%For every {N_Id,PIdPs} tuple the update_genotype/3 function extracts the neuron with the id: N_Id, updates the neuron's input_IdPs, and writes the updated neuron to database.
+
+ convert_PIdPs2IdPs(IdsNPIds,[{PId,Weights}|Input_PIdPs],Acc)->
+ convert_PIdPs2IdPs(IdsNPIds,Input_PIdPs,[{ets:lookup_element(IdsNPIds,PId,2),Weights}|Acc]);
+ convert_PIdPs2IdPs(_IdsNPIds,[Bias],Acc)->
+ lists:reverse([{bias,[Bias]}|Acc]);
+ convert_PIdPs2IdPs(_IdsNPIds,[],Acc)->
+ lists:reverse(Acc).
+%The convert_PIdPs2IdPs/3 performs the conversion from PIds to Ids of every {PId,Weights} tuple in the Input_PIdPs list. The updated Input_IdPs are then returned to the caller.
+
+terminate_phenotype(Cx_PId,SPIds,NPIds,APIds,ScapePIds)->
+ io:format("Terminating the phenotype:~nCx_PId:~p~nSPIds:~p~nNPIds:~p~nAPIds:~p~nScapePids:~p~n",[Cx_PId,SPIds,NPIds,APIds,ScapePIds]),
+ [PId ! {self(),terminate} || PId <- SPIds],
+ [PId ! {self(),terminate} || PId <- APIds],
+ [PId ! {self(),terminate} || PId <- NPIds],
+ [PId ! {self(),terminate} || PId <- ScapePIds],
+ Cx_PId ! {self(),terminate}.
+%The terminate_phenotype/5 function termiantes sensors, actuators, neurons, all private scapes, and the cortex which composes the NN based system.
+
+gather_acks(0)->
+ done;
+gather_acks(PId_Index)->
+ receive
+ {_From,ready}->
+ gather_acks(PId_Index-1)
+ after 100000 ->
+ io:format("******** Not all acks received:~p~n",[PId_Index])
+ end.
+%gather_acks/1 ensures that the X number of {From,ready} messages are sent to it, before it returns with done. X is set by the caller of the function.
19 Ch_11/fitness_postprocessor.erl
@@ -0,0 +1,19 @@
+%% This source code and work is provided and developed by DXNN Research Group WWW.DXNNResearch.COM
+%%
+%Copyright (C) 2012 by Gene Sher, DXNN Research Group, CorticalComputer@gmail.com
+%All rights reserved.
+%
+%This code is licensed under the version 3 of the GNU General Public License. Please see the LICENSE file that accompanies this project for the terms of use.
+
+-module(fitness_postprocessor).
+-compile(export_all).
+-include("records.hrl").
+-define(EFF,0.1). %Efficiency.
+
+none(Agent_Summaries)->
+ lists:reverse(lists:sort(Agent_Summaries)).
+
+size_proportional(Agent_Summaries)->
+ SDX=lists:reverse(lists:sort([{Fitness/math:pow(TotN,?EFF),{Fitness,TotN,Agent_Id}}||{Fitness,TotN,Agent_Id}<-Agent_Summaries])),
+ ProperlySorted_AgentSummaries = [Val || {_,Val}<-SDX],
+ ProperlySorted_AgentSummaries.
165 Ch_11/functions.erl
@@ -0,0 +1,165 @@
+%% This source code and work is provided and developed by DXNN Research Group WWW.DXNNResearch.COM
+%%
+%Copyright (C) 2012 by Gene Sher, DXNN Research Group, CorticalComputer@gmail.com
+%All rights reserved.
+%
+%This code is licensed under the version 3 of the GNU General Public License. Please see the LICENSE file that accompanies this project for the terms of use.
+
+-module(functions).
+-compile(export_all).
+
+saturation(Val)->
+ case Val > 1000 of
+ true ->
+ 1000;
+ false ->
+ case Val < -1000 of
+ true ->
+ -1000;
+ false ->
+ Val
+ end
+ end.
+
+saturation(Val,Spread)->
+ case Val > Spread of
+ true ->
+ Spread;
+ false ->
+ case Val < -Spread of
+ true ->
+ -Spread;
+ false ->
+ Val
+ end
+ end.
+scale([H|T],Max,Min)->
+ [scale(Val,Max,Min)||Val<-[H|T]];
+scale(Val,Max,Min)-> %Nm = (Y*2 - (Max + Min))/(Max-Min)
+ case Max == Min of
+ true ->
+ 0;
+ false ->
+ (Val*2 - (Max+Min))/(Max-Min)
+ end.
+
+sat(Val,Max,Min)->
+ case Val > Max of
+ true ->
+ Max;
+ false ->
+ case Val < Min of
+ true ->
+ Min;
+ false ->
+ Val
+ end
+ end.
+
+sat_dzone(Val,Max,Min,DZMax,DZMin)->
+ case (Val < DZMax) and (Val > DZMin) of
+ true ->
+ 0;
+ false ->
+ sat(Val,Max,Min)
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Activation Functions %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+tanh(Val)->
+ math:tanh(Val).
+
+cos(Val)->
+ math:cos(Val).
+
+sin(Val)->
+ math:sin(Val).
+
+sgn(0)->
+ 0;
+sgn(Val)->
+ case Val > 0 of
+ true -> 1;
+ false -> -1
+ end.
+
+bin(Val)->
+ case Val > 0 of
+ true -> 1;
+ false -> 0
+ end.
+
+trinary(Val)->
+ if
+ (Val < 0.33) and (Val > -0.33) -> 0;
+ Val >= 0.33 -> 1;
+ Val =< -0.33 -> -1
+ end.
+
+multiquadric(Val)->
+ math:pow(Val*Val + 0.01,0.5).
+
+absolute(Val)->
+ abs(Val).
+
+linear(Val)->
+ Val.
+
+quadratic(Val)->
+ sgn(Val)*Val*Val.
+
+gaussian(Val)->
+ gaussian(2.71828183,Val).
+
+gaussian(Const,Val)->
+ V = case Val > 10 of
+ true ->
+ 10;
+ false ->
+ case Val < -10 of
+ true ->
+ -10;
+ false ->
+ Val
+ end
+ end,
+ math:pow(Const,-V*V).
+
+sqrt(Val)->
+ sgn(Val)*math:sqrt(abs(Val)).
+
+log(Val)->
+ case Val == 0 of
+ true ->
+ 0;
+ false ->
+ sgn(Val)*math:log(abs(Val))
+ end.
+
+sigmoid(Val)-> %(-1 : 1)--Der:Y*(1-Y)
+ V = case Val > 10 of
+ true ->
+ 10;
+ false ->
+ case Val < -10 of
+ true ->
+ -10;
+ false ->
+ Val
+ end
+ end,
+ 2/(1+math:pow(2.71828183,-V)) - 1.
+
+sigmoid1(Val)-> %(-1 : 1) -- Der:1/((1+abs(val))*(1+abs(val)))
+ Val/(1+abs(Val)).
+
+avg(List)->
+ lists:sum(List)/length(List).
+std(List)->
+ Avg = avg(List),
+ std(List,Avg,[]).
+
+ std([Val|List],Avg,Acc)->
+ std(List,Avg,[math:pow(Avg-Val,2)|Acc]);
+ std([],_Avg,Acc)->
+ Variance = lists:sum(Acc)/length(Acc),
+ math:sqrt(Variance).
854 Ch_11/genome_mutator.erl
@@ -0,0 +1,854 @@
+%% This source code and work is provided and developed by DXNN Research Group WWW.DXNNResearch.COM
+%%
+%Copyright (C) 2012 by Gene Sher, DXNN Research Group, CorticalComputer@gmail.com
+%All rights reserved.
+%
+%This code is licensed under the version 3 of the GNU General Public License. Please see the LICENSE file that accompanies this project for the terms of use.
+
+-module(genome_mutator).
+-compile(export_all).
+-include("records.hrl").
+-define(DELTA_MULTIPLIER,math:pi()*2).
+-define(SAT_LIMIT,math:pi()*2).
+-define(SEARCH_PARAMTERS_MUTATION_PROBABILITY,0.1).
+-define(MUTATORS,[
+ mutate_weights,
+ add_bias,
+% remove_bias,
+ mutate_af,
+ add_outlink,
+% remove_outlink,
+ add_inlink,
+% remove_inlink,
+% add_sensorlink,
+% add_actuatorlink,
+ add_neuron,
+% remove_neuron,
+ outsplice,
+% insplice,
+ add_sensor,
+% remove_sensor,
+ add_actuator
+% remove_actuator
+ ]).
+-define(ES_MUTATORS,[
+ mutate_tuning_selection,
+ mutate_tuning_duration,
+ mutate_tuning_annealing,
+ mutate_tot_topological_mutations
+]).
+-define(ACTUATORS,morphology:get_Actuators(A#agent.morphology)).
+-define(SENSORS,morphology:get_Sensors(A#agent.morphology)).
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+long_test(TotMutations) when TotMutations > 0->
+ genotype:create_test(),
+ short_test(TotMutations).
+
+ short_test(0)->
+ exoself:start(test,void);
+ short_test(Index)->
+ test(),
+ short_test(Index-1).
+%This is a simple function that executes the test() function the number of times with which the long_test/1 function was initially called. The test/0 function executes mutate(test), which applies a random number of mutation operators to the genotype, where that number ranges from 1 to sqrt(Tot_neurons). After all the mutation operators have been applied succesfully, the function executes exoself:start(test,void), mapping the genotype to phenotype, to test whether the resulting NN system is functional.
+
+test()->
+ Result = mutate(test),
+ case Result of
+ {atomic,_} ->
+ io:format("******** Mutation Succesful.~n");
+ _->
+ io:format("******** Mutation Failure:~p~n",[Result])
+ end.
+%The test/1 function simply tests the mutate/1 function with an agent whose id is 'test'.
+
+test(Agent_Id,Mutator)->
+ F = fun()->
+ genome_mutator:Mutator(Agent_Id)
+ end,
+ mnesia:transaction(F).
+%test/2 function tests the mutation operator "Mutator" on the agent with an id Agent_Id.
+
+mutate(Agent_Id)->
+ random:seed(now()),
+ F = fun()->
+ mutate_SearchParameters(Agent_Id),
+ A = genotype:read({agent,Agent_Id}),
+ {TTM_Name,Parameter} = A#agent.tot_topological_mutations_f,
+ TotMutations = tot_topological_mutations:TTM_Name(Parameter,Agent_Id),
+ OldGeneration = A#agent.generation,
+ NewGeneration = OldGeneration+1,
+ genotype:write(A#agent{generation = NewGeneration}),
+ apply_Mutators(Agent_Id,TotMutations),
+ genotype:update_fingerprint(Agent_Id)
+ end,
+ mnesia:transaction(F).
+%The function mutate/1 first updates the generation of the agent to be mutated, then calculates the number of mutation operators to be applied to it by executing the tot_topological_mutations:TTM_Name/2 function, and then finally runs the apply_Mutators/2 function, which mutates the agent. Once the agent is mutated, the function updates its fingerprint by executing genotype:update_finrgerprint/1.
+
+ mutate_SearchParameters(Agent_Id)->
+ case random:uniform() < ?SEARCH_PARAMTERS_MUTATION_PROBABILITY of
+ true ->
+ TotMutations = random:uniform(length(?ES_MUTATORS)),
+ apply_ESMutators(Agent_Id,TotMutations);
+ false ->
+ ok
+ end.
+%mutate_SearchParamters/1 with a probability of ?SEARCH_PARAMETERS_MUTATION_PROBABILITY applies a random number between 1 and length(?ES_MUTATORS) of evolutioanry strategy mutation operators from the ?ES_MUTATORS list.
+
+ apply_ESMutators(_Agent_Id,0)->
+ done;
+ apply_ESMutators(Agent_Id,MutationIndex)->
+ ES_Mutators = ?ES_MUTATORS,
+ ES_Mutator = lists:nth(random:uniform(length(ES_Mutators)),ES_Mutators),
+ io:format("Evolutionary Strategy Mutation Operator:~p~n",[ES_Mutator]),
+ Result = genome_mutator:ES_Mutator(Agent_Id),
+ case Result of
+ {atomic,_} ->
+ apply_ESMutators(Agent_Id,MutationIndex-1);
+ Error ->
+ io:format("******** Error:~p~nRetrying with new Mutation...~n",[Error]),
+ apply_ESMutators(Agent_Id,MutationIndex-1)
+ end.
+%apply_ESMutators/2 with uniform distribution chooses a random evolutionary strategy mutation operator from the ?ES_MUTATORS list of such functions, and applies it to the agent. Whether the mutation succcessful or not, the function counts down the total number of mutation operators left to apply. This is to ensure that if the researcher set for each such evolutionary strategy to be static, having only one available mutatable parameter for every agent, the system will eventually try to mutate the strategy TotMutations number of times, and then return to the caller.
+
+ apply_Mutators(_Agent_Id,0)->
+ done;
+ apply_Mutators(Agent_Id,MutationIndex)->
+ Result = apply_NeuralMutator(Agent_Id),
+ case Result of
+ {atomic,_} ->
+ apply_Mutators(Agent_Id,MutationIndex-1);
+ Error ->
+ io:format("******** Error:~p~nRetrying with new Mutation...~n",[Error]),
+ apply_Mutators(Agent_Id,MutationIndex)
+ end.
+%apply_Mutators/2 applies the set number of successfull mutation operators to the Agent. If a mutaiton operator exits with an error, the function tries another mutaiton operator. It is only after a sucessfull mutation operator is applied that the MutationIndex is decremented.
+
+ apply_NeuralMutator(Agent_Id)->
+ F = fun()->
+ A = genotype:read({agent,Agent_Id}),
+ MutatorsP = A#agent.mutation_operators,
+ Mutator = select_random_MO(MutatorsP),
+ io:format("Mutation Operator:~p~n",[Mutator]),
+ genome_mutator:Mutator(Agent_Id)
+ end,
+ mnesia:transaction(F).
+%apply_NeuralMutator/1 applies the available mutation operators to the NN. Because the genotype is stored in mnesia, if the mutation operator function exits with an error, the database made changes are retracted, and a new mutation operator can then be applied to the agent, as if the previous unsuccessful mutation operator was never applied. The mutation operator to be applied to the agent is chosen randomly from the agent's mutation_operators list.
+
+ select_random_MO(MutatorsP)->
+ TotSize = lists:sum([SliceSize || {_MO,SliceSize} <- MutatorsP]),
+ Choice=random:uniform(TotSize),
+ select_random_MO(MutatorsP,Choice,0).
+
+ select_random_MO([{MO,SliceSize}|MOs],Choice,Range_From)->
+ Range_To = Range_From+SliceSize,
+ case (Choice >= Range_From) and (Choice =< Range_To) of
+ true ->
+ MO;
+ false ->
+ select_random_MO(MOs,Choice,Range_To)
+ end;
+ select_random_MO([],_Choice,_Range_From)->
+ exit("********ERROR:select_random_MO:: reached [] without selecting a mutation operator.").
+%select_random_MO/1, using the analogy of a roulette wheel, first calculates the entire are of the wheel by summing together all the slice sizes of the parts. The function than chooses randomly a spot on the wheel, and through select_random_MO/3 calculates where that spot is located, with regards to the mutation operator that it falls on. Since some slices are larger then others, they will have uniformly larger probabilities of being selected.
+
+mutate_tuning_selection(Agent_Id)->
+ A = genotype:read({agent,Agent_Id}),
+ case (A#agent.constraint)#constraint.tuning_selection_fs -- [A#agent.tuning_selection_f] of
+ [] ->
+ exit("********ERROR:mutate_tuning_selection/1:: Nothing to mutate, only a single function available.");
+ Tuning_Selection_Functions->
+ New_TSF = lists:nth(random:uniform(length(Tuning_Selection_Functions)),Tuning_Selection_Functions),
+ U_A = A#agent{tuning_selection_f = New_TSF},
+ genotype:write(U_A)
+ end.
+%mutate_tuning_selection/1 function checks if there are any other than the currently used tuning selection functions available in the agent's constraint. If there is, then it chooses a random one from this list, and sets the agent's tuning_selection_f to it. If there are no other tuning selection functions, then it exits with an error.
+
+mutate_tuning_annealing(Agent_Id)->
+ A = genotype:read({agent,Agent_Id}),
+ case (A#agent.constraint)#constraint.annealing_parameters -- [A#agent.annealing_parameter] of
+ [] ->
+ exit("********ERROR:mutate_tuning_annealing/1:: Nothing to mutate, only a single function available.");
+ Tuning_Annealing_Parameters->
+ New_TAP = lists:nth(random:uniform(length(Tuning_Annealing_Parameters)),Tuning_Annealing_Parameters),
+ U_A = A#agent{annealing_parameter = New_TAP},
+ genotype:write(U_A)
+ end.
+%mutate_annealing_parameter/1 function checks if there are any other than the currently used tuning annealing parameters available in the agent's constraint. If there is, then it chooses a random one from this list, and sets the agent's annealing_parameter to it. If there are no other tuning annealing parameters, then it exits with an error.
+
+mutate_tot_topological_mutations(Agent_Id)->
+ A = genotype:read({agent,Agent_Id}),
+ case (A#agent.constraint)#constraint.tuning_selection_fs -- [A#agent.tuning_selection_f] of
+ [] ->
+ exit("********ERROR:mutate_tuning_selection/1:: Nothing to mutate, only a single function available.");
+ Tuning_Selection_Functions->
+ New_TSF = lists:nth(random:uniform(length(Tuning_Selection_Functions)),Tuning_Selection_Functions),
+ U_A = A#agent{tuning_selection_f = New_TSF},
+ genotype:write(U_A)
+ end.
+%mutate_tot_topological_mutations/1 function checks if there are any other than the currently used tuning tot topological mutation functions available in the agent's constraint. If there is, then it chooses a random one from this list, and sets the agent's tot_topological_mutations_f to it. If there are no other functions that can calculate tot topological mutations, then it exits with an error.
+
+mutate_weights(Agent_Id)->
+ A = genotype:read({agent,Agent_Id}),
+ Cx_Id = A#agent.cx_id,
+ Cx = genotype:read({cortex,Cx_Id}),
+ N_Ids = Cx#cortex.neuron_ids,
+
+ N_Id = lists:nth(random:uniform(length(N_Ids)),N_Ids),
+ N = genotype:read({neuron,N_Id}),
+ Input_IdPs = N#neuron.input_idps,
+ U_Input_IdPs = perturb_IdPs(Input_IdPs),
+ U_N = N#neuron{input_idps = U_Input_IdPs},
+ EvoHist = A#agent.evo_hist,
+ U_EvoHist = [{mutate_weights,N_Id}|EvoHist],
+ U_A = A#agent{evo_hist = U_EvoHist},
+ genotype:write(U_N),
+ genotype:write(U_A).
+%The mutate_weights/1 function accepts the Agent_Id parameter, extracts the NN's cortex, and then chooses a random neuron belonging to the NN with a uniform distribution probability. Then the neuron's input_idps list is extracted, and the function perturb_IdPs/1 is used to perturb/mutate the weights. Once the Input_IdPs have been perturbed, the agent's evolutionary history, EvoHist is updated to include the successfully applied mutate_weights mutation operator. Then the updated Agent and the updated neuron are written to the database.
+
+ perturb_IdPs(Input_IdPs)->
+ Tot_Weights=lists:sum([length(Weights) || {_Input_Id,Weights}<-Input_IdPs]),
+ MP = 1/math:sqrt(Tot_Weights),
+ perturb_IdPs(MP,Input_IdPs,[]).
+ perturb_IdPs(MP,[{Input_Id,Weights}|Input_IdPs],Acc)->
+ U_Weights = perturb_weights(MP,Weights,[]),
+ perturb_IdPs(MP,Input_IdPs,[{Input_Id,U_Weights}|Acc]);
+ perturb_IdPs(_MP,[],Acc)->
+ lists:reverse(Acc).
+%perturb_IdPs/1 accepts the Input_IdPs list of the format:[{Id,Weights}...], calculates the total number of weights in the Input_IdPs, and then calculates the mutation probability MP, which is 1/sqrt(Tot_Weights). Once the mutation probability is calculated, each weight in the Input_IdPs list has a chance of MP to be perturbed/mutated. Once all the weights in the Input_IdPs list had a chance of being mutated, the updated Input_IdPs is returned to the caller.
+
+ perturb_weights(MP,[W|Weights],Acc)->
+ U_W = case random:uniform() < MP of
+ true->
+ sat((random:uniform()-0.5)*?DELTA_MULTIPLIER+W,-?SAT_LIMIT,?SAT_LIMIT);
+ false ->
+ W
+ end,
+ perturb_weights(MP,Weights,[U_W|Acc]);
+ perturb_weights(_MP,[],Acc)->
+ lists:reverse(Acc).
+%perturb_weights/3 is called with the mutation probability MP, a weights list, and an empty list, [], to be used as an accumulator. The function goes through every weight, where every weight has a chance of MP to be mutated/perturbed. The perturbations have a random intensity between -Pi/2 and Pi/2. Once all the weights in the weights list had a chance of being perturbed, the updated weights list is reversed back to its original order, and returned back to the caller.
+ sat(Val,Min,Max)->
+ if
+ Val < Min -> Min;
+ Val > Max -> Max;
+ true -> Val
+ end.
+%sat/3 calculates whether Val is between Min and Max values, if it is, then val is returned as is. If Val is less than Min, then Min is returned, if Val is greater than Max, then Max is returned.
+
+add_bias(Agent_Id)->
+ A = genotype:read({agent,Agent_Id}),
+ Cx_Id = A#agent.cx_id,
+ Cx = genotype:read({cortex,Cx_Id}),
+ N_Ids = Cx#cortex.neuron_ids,
+ N_Id = lists:nth(random:uniform(length(N_Ids)),N_Ids),
+ Generation = A#agent.generation,
+
+ N = genotype:read({neuron,N_Id}),
+ Input_IdPs = N#neuron.input_idps,
+ case lists:keymember(bias, 1, Input_IdPs) of
+ true ->
+ exit("********ERROR:add_bias:: This Neuron already has a bias part.");
+ false ->
+ U_Input_IdPs = lists:append(Input_IdPs,[{bias,[random:uniform()-0.5]}]),
+ U_N = N#neuron{
+ input_idps = U_Input_IdPs,
+ generation = Generation},
+ EvoHist = A#agent.evo_hist,
+ U_EvoHist = [{add_bias,N_Id}|EvoHist],
+ U_A = A#agent{evo_hist=U_EvoHist},
+ genotype:write(U_N),
+ genotype:write(U_A)
+ end.
+%The add_bias/1 function is called with the Agent_Id parameter. The function first extracts the neuron_ids list from the cortex element and chooses a random neuron from the id list. The neuron is the read from the database and its input_idps list checked for bias element. If the neuron's input_idps list already has a bias tuple, then the function is exited. If the input_idps list does not have the bias tuple, then the bias is added, the agent's evolutionary history list is updated with the tuple {add_bias,N_Id}, and the updated neuron and agent records are written to the database.
+
+remove_bias(Agent_Id)->
+ A = genotype:read({agent,Agent_Id}),
+ Cx_Id = A#agent.cx_id,
+ Cx = genotype:read({cortex,Cx_Id}),
+ N_Ids = Cx#cortex.neuron_ids,
+ N_Id = lists:nth(random:uniform(length(N_Ids)),N_Ids),
+ Generation = A#agent.generation,
+
+ N = genotype:read({neuron,N_Id}),
+ Input_IdPs = N#neuron.input_idps,
+ case lists:keymember(bias, 1, Input_IdPs) of
+ false ->
+ exit("********ERROR:remove_bias:: This Neuron does not have a bias part.");
+ true ->
+ U_Input_IdPs = lists:keydelete(bias,1,Input_IdPs),
+ U_N = N#neuron{
+ input_idps = U_Input_IdPs,
+ generation = Generation},
+ EvoHist = A#agent.evo_hist,
+ U_EvoHist = [{remove_bias,N_Id}|EvoHist],
+ U_A = A#agent{evo_hist=U_EvoHist},
+ genotype:write(U_N),
+ genotype:write(U_A)
+ end.
+%The remove_bias/1 function is called with the Agent_Id parameter. The function first extracts the neuron_ids list from the cortex element and chooses a random neuron from the id list. The neuron is the read from the database and its input_idps list checked for bias element. If the neuron's input_idps list has a bias tuple, it is removed ,the agent's evolutionary history list is updated with the tuple {add_bias,N_Id}, and the updated neuron and agent records are written to the database. If the input_idps list does not have the bias tuple, the function exits with an error stating so.
+
+mutate_af(Agent_Id)->
+ A = genotype:read({agent,Agent_Id}),
+ Cx_Id = A#agent.cx_id,
+ Cx = genotype:read({cortex,Cx_Id}),
+ N_Ids = Cx#cortex.neuron_ids,
+ N_Id = lists:nth(random:uniform(length(N_Ids)),N_Ids),
+ Generation = A#agent.generation,
+
+ N = genotype:read({neuron,N_Id}),
+ AF = N#neuron.af,
+ case (A#agent.constraint)#constraint.neural_afs -- [AF] of
+ [] ->
+ exit("********ERROR:mutate_af:: There are no other activation functions to use.");
+ Activation_Functions ->
+ NewAF = lists:nth(random:uniform(length(Activation_Functions)),Activation_Functions),
+ U_N = N#neuron{af=NewAF,generation=Generation},
+ EvoHist = A#agent.evo_hist,
+ U_EvoHist = [{mutate_af,N_Id}|EvoHist],
+ U_A = A#agent{evo_hist=U_EvoHist},
+ genotype:write(U_N),
+ genotype:write(U_A)
+ end.
+%The mutate_af/1 function chooses a random neuron, and then changes its currently used activation function into another one available from the neural_afs list of the agent's constraint record.
+
+mutate_pf(Agent_Id)->
+ A = genotype:read({agent,Agent_Id}),
+ Cx_Id = A#agent.cx_id,
+ Cx = genotype:read({cortex,Cx_Id}),
+ N_Ids = Cx#cortex.neuron_ids,
+ N_Id = lists:nth(random:uniform(length(N_Ids)),N_Ids),
+ Generation = A#agent.generation,
+
+ N = genotype:read({neuron,N_Id}),
+ PF = N#neuron.pf,
+ case (A#agent.constraint)#constraint.neural_pfs -- [PF] of
+ [] ->
+ exit("********ERROR:mutate_pf:: There are no other plasticity functions to use.");
+ Plasticity_Functions ->
+ NewPF = lists:nth(random:uniform(length(Plasticity_Functions)),Plasticity_Functions),
+ U_N = N#neuron{pf=NewPF,generation=Generation},
+ EvoHist = A#agent.evo_hist,
+ U_EvoHist = [{mutate_pf,N_Id}|EvoHist],
+ U_A = A#agent{evo_hist=U_EvoHist},
+ genotype:write(U_N),
+ genotype:write(U_A)
+ end.
+%The mutate_pf/1 function chooses a random neuron, and then changes its currently used plasticity function into another one available from the neural_pfs list of the agent's constraint record.
+
+mutate_aggrf(Agent_Id)->
+ A = genotype:read({agent,Agent_Id}),
+ Cx_Id = A#agent.cx_id,
+ Cx = genotype:read({cortex,Cx_Id}),
+ N_Ids = Cx#cortex.neuron_ids,
+ N_Id = lists:nth(random:uniform(length(N_Ids)),N_Ids),
+ Generation = A#agent.generation,
+
+ N = genotype:read({neuron,N_Id}),
+ AggrF = N#neuron.aggr_f,
+ case (A#agent.constraint)#constraint.neural_aggr_fs -- [AggrF] of
+ [] ->
+ exit("********ERROR:mutate_aggrf:: There are no other aggregation functions to use.");
+ Aggregation_Functions ->
+ NewAggrF = lists:nth(random:uniform(length(Aggregation_Functions)),Aggregation_Functions),
+ U_N = N#neuron{aggr_f=NewAggrF,generation=Generation},
+ EvoHist = A#agent.evo_hist,
+ U_EvoHist = [{mutate_aggrf,N_Id}|EvoHist],
+ U_A = A#agent{evo_hist=U_EvoHist},
+ genotype:write(U_N),
+ genotype:write(U_A)
+ end.
+%The mutate_aggrf/1 function chooses a random neuron, and then changes its currently used aggregation function into another one available from the neural_aggr_fs list of the agent's constraint record.
+
+link_FromElementToElement(Agent_Id,From_ElementId,To_ElementId)->
+ case {From_ElementId,To_ElementId} of
+ {{_FromSId,neuron},{_ToSId,neuron}} ->
+ link_FromNeuronToNeuron(Agent_Id,From_ElementId,To_ElementId);
+ {{_FromSId,sensor},{_ToSId,neuron}} ->
+ link_FromSensorToNeuron(Agent_Id,From_ElementId,To_ElementId);
+ {{_FromNId,neuron},{_ToAId,actuator}} ->
+ link_FromNeuronToActuator(Agent_Id,From_ElementId,To_ElementId)
+ end.
+%The function link_FromElementToElement/3 first calculates what type of link is going to be established (neuron to neuron, sensor to neuron, or neuron to actuator), and then calls the specific linking function based on that.
+
+link_FromNeuronToNeuron(Agent_Id,From_NeuronId,To_NeuronId)->
+ A = genotype:read({agent,Agent_Id}),
+ Generation = A#agent.generation,
+%From Partf
+ FromN = genotype:read({neuron,From_NeuronId}),
+ U_FromN = link_FromNeuron(FromN,To_NeuronId,Generation),
+ genotype:write(U_FromN),
+%To Part
+ ToN = genotype:read({neuron,To_NeuronId}),%We read it afterwards, in the case that it's the same Element. Thus we do not overwrite the earlier changes.
+ FromOVL = 1,
+ U_ToN = link_ToNeuron(From_NeuronId,FromOVL,ToN,Generation),
+ genotype:write(U_ToN).
+%link_FromNeuronToNeuron/3 establishes a link from neuron with id From_NeuronId, to a neuron with id To_NeuronId. The function then calls link_FromNeuron/4, which establishes the link on the From_NeuronId's side. The updated neuron associated with the From_NeuronId is then written to database. To decide how long the weight list that is going to be added to the To_NeuronId's input_idps, the function calculates From_NeuronId's output vector length. Since the connection is from a neuron, FromOVL is set to 1. link_ToNeuron/4 is then called, and the link is established on the To_NeuronId's side. Finally, the updated neuron associated with the id To_NeuronId is written to database. The order of reading the FromN and ToN neuron records from the database is important. It is essential that ToN is read after the U_FromN is written to database, in the case that From_NeuronId and To_NeuronId refer to the same neuron (a recurrent connection from the neuron to itself). If both neurons are read at the same time for example before the links are established, then the link established in the U_FromN will be overwritten when the U_ToN is written to file. Thus order is important in this function.
+
+ link_FromNeuron(FromN,ToId,Generation)->
+ {{FromLI,_},_} = FromN#neuron.id,
+ {{ToLI,_},_} = ToId,
+ FromOutput_Ids = FromN#neuron.output_ids,
+ FromRO_Ids = FromN#neuron.ro_ids,
+ case lists:member(ToId, FromOutput_Ids) of
+ true ->
+ exit("******** ERROR:add_NeuronO[can not add O_Id to Neuron]: ~p already a member of ~p~n",[ToId,FromN#neuron.id]);
+ false ->
+ {U_FromOutput_Ids,U_FromRO_Ids} = case FromLI >= ToLI of
+ true ->
+ {[ToId|FromOutput_Ids],[ToId|FromRO_Ids]};
+ false ->
+ {[ToId|FromOutput_Ids],FromRO_Ids}
+ end,
+ FromN#neuron{
+ output_ids = U_FromOutput_Ids,
+ ro_ids = U_FromRO_Ids,
+ generation = Generation
+ }
+ end.
+%link_FromNeuron/4 updates the record of the neuron from whom the link is being created. FromN is the record of the neuron from whom the link/connection eminates, and ToId is the id of the element to whom the link is headed towards. The function extracts the layer index of the neuron FromN, and the layer index of the element with the id ToId. Then the two layer indecies are compared, and the ToId is either added only to the FromN's output_ids list, or if the connection is recursive, ToLayerIndex =< FromLayerIndex, to output_ids and ro_ids lists. The FromN's generation is updated to the value Generation, which is the current, most recent generation, since this neuron has just been modified. Finally, the updated neuron record is then returned to the caller. On the other hand, if ToId, the id of the element to which the connection is being established, is already a member of the FromN's output_ids list, then the function exits with error.
+
+ link_ToNeuron(FromId,FromOVL,ToN,Generation)->
+ ToInput_IdPs = ToN#neuron.input_idps,
+ case lists:keymember(FromId, 1, ToInput_IdPs) of
+ true ->
+ exit("ERROR:add_NeuronI::[can not add I_Id]: ~p already a member of ~p~n",[FromId,ToN#neuron.id]);
+ false ->
+ U_ToInput_IdPs = [{FromId, genotype:create_NeuralWeights(FromOVL,[])}|ToInput_IdPs],
+ ToN#neuron{
+ input_idps = U_ToInput_IdPs,
+ generation = Generation
+ }
+ end.
+%link_ToNeuron/4 updates the record of ToN, so that its updated to receive a connection from the element FromId. The link eminates from element with the id FromId, whose output vector length is FromOVL, and the connection is made to the neuron ToN, the record whose is updated in this function. The ToN's input_idps is updated with the tuple {FromId,[W_1...W_FromOVL]}, then the neuron's generation is updated to Generation (the current, most recent generation), and the updated ToN's record is returned to the caller. On the other hand, if the FromId is already part of the ToN's input_idps list, which means that the link already exists between the neuron ToN, and element FromId, then the function exits with an error.
+
+link_FromSensorToNeuron(Agent_Id,From_SensorId,To_NeuronId)->
+ A = genotype:read({agent,Agent_Id}),
+ Generation = A#agent.generation,
+%From Part
+ FromS = genotype:read({sensor,From_SensorId}),
+ U_FromS = link_FromSensor(FromS,To_NeuronId,Generation),
+ genotype:write(U_FromS),
+%To Part
+ ToN = genotype:read({neuron,To_NeuronId}),
+ FromOVL = FromS#sensor.vl,
+ U_ToN = link_ToNeuron(From_SensorId,FromOVL,ToN,Generation),
+ genotype:write(U_ToN).
+%The function link_FromSensorToNeuron/3 establishes a connection from the sensor with id From_SensorId, and the neuron with id To_NeuronId. First the sensor record is updated with the connection details using the function link_FromSensor, and the updated sensor record is written to database. Then the record of the neuron to whom the link is being established is updated using the function link_ToNeuron/4, after which the updated neuron is written to database.
+
+ link_FromSensor(FromS,ToId,Generation)->
+ FromFanout_Ids = FromS#sensor.fanout_ids,
+ case lists:member(ToId, FromFanout_Ids) of
+ true ->
+ exit("******** ERROR:link_FromSensor[can not add ToId to Sensor]: ~p already a member of ~p~n",[ToId,FromS#sensor.id]);
+ false ->
+ FromS#sensor{
+ fanout_ids = [ToId|FromFanout_Ids],
+ generation=Generation
+ }
+ end.
+%The function link_FromSensor/2 updates the record of the sensor FromS, from whom the link eminates towards the element with id ToId. First the function ensures that there is no connection yet established between FromS and ToId, if a connection between these two elements already exists, then the function exits with error. If there is no connection between the two elements, then ToId is added to the sensor's fanout_ids list, and the updated record of the sensor is returned to the caller.
+
+link_FromNeuronToActuator(Agent_Id,From_NeuronId,To_ActuatorId)->
+ A = genotype:read({agent,Agent_Id}),
+ Generation = A#agent.generation,
+%From Part
+ FromN = genotype:read({neuron,From_NeuronId}),
+ U_FromN = link_FromNeuron(FromN,To_ActuatorId,Generation),
+ genotype:write(U_FromN),
+%To Part
+ ToA = genotype:read({actuator,To_ActuatorId}),
+ Fanin_Ids = ToA#actuator.fanin_ids,
+ case length(Fanin_Ids) >= ToA#actuator.vl of
+ true ->
+ exit("******** ERROR:link_FromNeuronToActuator:: Actuator already fully connected");
+ false ->
+ U_Fanin_Ids = [From_NeuronId|Fanin_Ids],
+ genotype:write(ToA#actuator{
+ fanin_ids = U_Fanin_Ids,
+ generation=Generation})
+ end.
+%The function Link_FromNeuronToActuator/4 establishes a link eminating from the neuron with an id From_NeuronId, to an actuator with the id To_ActuatorId. First the From_NeuronId's record is updated using the function link_FromNeuron/3, after which the updated neuron record is written to database. Then the function checks whether the actuator to which the neuron is establishing the link, still has space for that link (length(Fanin_Ids) is less than the actuator's vector length, vl). If there is no more room, then the function exits with error, if there is room, then the actuator's fanin_ids list is updated by appending to it the id of the neuron's id. The updated actuator is then written to the database.
+
+cutlink_FromElementToElement(Agent_Id,From_ElementId,To_ElementId)->
+ case {From_ElementId,To_ElementId} of
+ {{_FromId,neuron},{_ToId,neuron}} ->
+ cutlink_FromNeuronToNeuron(Agent_Id,From_ElementId,To_ElementId);
+ {{_FromId,sensor},{_ToId,neuron}} ->
+ cutlink_FromSensorToNeuron(Agent_Id,From_ElementId,To_ElementId);
+ {{_FromId,neuron},{_ToId,actuator}} ->
+ cutlink_FromNeuronToActuator(Agent_Id,From_ElementId,To_ElementId)
+ end.
+%cutlink_FromElementToElement/3 first checks which of the three types of connections is between the From_ElementId and To_ElementId (neuron to neuron, sensor to neuron, or neuron to actuator), and then disconnects the two elements using one of the three specialised cutlink_... functions.
+
+cutlink_FromNeuronToNeuron(Agent_Id,From_NeuronId,To_NeuronId)->
+ A = genotype:read({agent,Agent_Id}),
+ Generation = A#agent.generation,
+%From Part
+ FromN = genotype:read({neuron,From_NeuronId}),
+ U_FromN = cutlink_FromNeuron(FromN,To_NeuronId,Generation),
+ genotype:write(U_FromN),
+%To Part
+ ToN = genotype:read({neuron,To_NeuronId}),
+ U_ToN = cutlink_ToNeuron(From_NeuronId,ToN,Generation),
+ genotype:write(U_ToN).
+%The cutlink_FromNeuronToNeuron/3 function disconnections the connection from the From_NeuronId to the To_NeuronId. The function first disconnects the neuron associated with From_NeuronId by calling the cutlink_FromNeuron/3, and then writing to database the updated neuron. The function then disconnects the neuron associated with the To_NeuronId from the connection using the cutlink_ToNeuron/3, and writes to database the updated ToN record. If the From_NeuronId and the To_NeuronId are ids of the same neuron, then it is important to first write U_FromN to database, before reading the ToN neuron from the database, so as not to lose the update made by the cutlink_FromNeuron/3, before reading the updated neuron from the database and calling the cutlink_ToNeuron. Thus this order of reading and writing the neurons from the database is essential to cover the corner cases.
+
+ cutlink_FromNeuron(FromN,ToId,Generation)->
+ FromOutput_Ids = FromN#neuron.output_ids,
+ FromRO_Ids = FromN#neuron.ro_ids,
+ case lists:member(ToId, FromOutput_Ids) of
+ true ->
+ U_FromOutput_Ids = FromOutput_Ids--[ToId],
+ U_FromRO_Ids = FromRO_Ids--[ToId],%Not necessary if not recursive...
+ FromN#neuron{
+ output_ids = U_FromOutput_Ids,
+ ro_ids = U_FromRO_Ids,
+ generation = Generation};
+ false ->
+ exit("ERROR:: cutlink_FromNeuron [can not remove O_Id]: ~p not a member of ~p~n",[ToId,FromN#neuron.id])
+ end.
+%cutlink_FromNeuron/3 cuts the connection on the FromNeuron (FromN) side. The function first checks if the ToId is a member of the output_ids list, if its not then the function exits with an error. If the ToId is a member of the output_ids list, then the function removes the ToId from the FromOutput_Ids and from the FromRO_Ids. Even if the ToId is a recursive connection, then removing it from ro_ids updates the FromRO_Ids list, if its not, then no change is made to the ro_ids list. Once the lists are updated, the updated neuron record of FromN is returned to the caller.
+
+ cutlink_ToNeuron(FromId,ToN,Generation)->
+ ToInput_IdPs = ToN#neuron.input_idps,
+ case lists:keymember(FromId, 1, ToInput_IdPs) of
+ true ->
+ U_ToInput_IdPs = lists:keydelete(FromId,1,ToInput_IdPs),
+ ToN#neuron{
+ input_idps = U_ToInput_IdPs,
+ generation = Generation};
+ false ->
+ exit("ERROR[can not remove I_Id]: ~p not a member of ~p~n",[FromId,ToN#neuron.id])
+ end.
+%cutlink_ToNeuron/3 cuts the connection on the ToNeuron (ToN) side. The function first checks if the FromId is a member of the ToN's input_idps list, if its not, then the function exits with error. If FromId is a member, then that tuple is removed from the ToInput_IdPs list, and the updated ToN record is returned to the caller.
+
+cutlink_FromSensorToNeuron(Agent_Id,From_SensorId,To_NeuronId)->
+ A = genotype:read({agent,Agent_Id}),
+ Generation = A#agent.generation,
+%From Part
+ FromS = genotype:read({sensor,From_SensorId}),
+ U_FromS = cutlink_FromSensor(FromS,To_NeuronId,Generation),
+ genotype:write(U_FromS),
+%To Part
+ ToN = genotype:read({neuron,To_NeuronId}),
+ U_ToN = cutlink_ToNeuron(From_SensorId,ToN,Generation),
+ genotype:write(U_ToN).
+%The cutlink_FromSensorToNeuron/3 cuts the connection from the From_SensorId to To_NeuronId. The function first cuts the cunnection on the From_SensorId side using the cutlink_FromSensor/3 side, and writes the updated sensor to database. The function then cuts the connection on the To_NeuronId side using the cutlink_ToNeuron/3 function, and writes the updated neuron record to database.
+
+ cutlink_FromSensor(FromS,ToId,Generation)->
+ FromFanout_Ids = FromS#sensor.fanout_ids,
+ case lists:member(ToId, FromFanout_Ids) of
+ true ->
+ U_FromFanout_Ids = FromFanout_Ids--[ToId],
+ FromS#sensor{
+ fanout_ids = U_FromFanout_Ids,
+ generation=Generation};
+ false ->
+ exit("ERROR:: cutlink_FromSensor [can not remove ToId]: ~p not a member of ~p~n",[ToId,FromS#sensor.id])
+ end.
+%The cutlink_FromSensor/3 function first checks whether ToId is a member of the sensor's FromS fanout_ids list. If its not, then the function exits with an error. If ToId is a member of FromS's fanout_ids list, then it is removed from that list, and the updated sensor record of FromS is returned to the caller.
+
+cutlink_FromNeuronToActuator(Agent_Id,From_NeuronId,To_ActuatorId)->
+ A = genotype:read({agent,Agent_Id}),
+ Generation = A#agent.generation,
+%From Part
+ FromN = genotype:read({neuron,From_NeuronId}),
+ U_FromN = cutlink_FromNeuron(FromN,To_ActuatorId,Generation),
+ genotype:write(U_FromN),
+%To Part
+ ToA = genotype:read({actuator,To_ActuatorId}),
+ U_ToA = cutlink_ToActuator(From_NeuronId,ToA,Generation),
+ genotype:write(U_ToA).
+%cutlink_FromNeuronToActuator/3 cuts the connection from the From_NeuronId to To_ActuatorId. The function first cuts the connection on the From_NeuronId side using the cutlink_FromNeuron/3 function, and writes the updated U_FromN to database. Then the connection on the To_ActuatorId is cut using the cutlink_ToActuator/3 function, after which the updated actuator record is written to the database.
+
+ cutlink_ToActuator(FromId,ToA,Generation)->
+ ToFanin_Ids = ToA#actuator.fanin_ids,
+ case lists:member(FromId, ToFanin_Ids) of
+ true ->
+ U_ToFanin_Ids = ToFanin_Ids--[FromId],
+ ToA#actuator{
+ fanin_ids = U_ToFanin_Ids,
+ generation=Generation};
+ false ->
+ exit("ERROR:: cutlink_ToActuator [can not remove FromId]: ~p not a member of ~p~n",[FromId,ToA])
+ end.
+%The cutlink_ToActuator/3 function cuts the connection on the ToActuator's side. The function first checks if the FromId is a member of the actuator ToA's fanin_ids list. If its not, the function exits with an error. If FromId is a member of the actuator's fanin_ids list, then the id is removed from the list, and the updated actuator record is returned to the caller.
+
+add_outlink(Agent_Id)->
+ A = genotype:read({agent,Agent_Id}),
+ Cx_Id = A#agent.cx_id,
+ Cx = genotype:read({cortex,Cx_Id}),
+ N_Ids = Cx#cortex.neuron_ids,
+ A_Ids = Cx#cortex.actuator_ids,
+ N_Id = lists:nth(random:uniform(length(N_Ids)),N_Ids),
+ N = genotype:read({neuron,N_Id}),
+ Output_Ids = N#neuron.output_ids,
+ Outlink_NIdPool = filter_OutlinkIdPool(A#agent.constraint,N_Id,N_Ids),
+ case lists:append(A_Ids,Outlink_NIdPool) -- Output_Ids of
+ [] ->
+ exit("********ERROR:add_outlink:: Neuron already connected to all ids");
+ Available_Ids ->
+ To_Id = lists:nth(random:uniform(length(Available_Ids)),Available_Ids),
+ link_FromElementToElement(Agent_Id,N_Id,To_Id),
+ EvoHist = A#agent.evo_hist,
+ U_EvoHist = [{add_outlink,N_Id,To_Id}|EvoHist],
+ U_A = A#agent{evo_hist=U_EvoHist},
+ genotype:write(U_A)
+ end.
+%The add_outlink/1 function reads the cortex record from the database based on the cortex id extracted from the agent record. The function then selects a random neuron from the neuron_ids stored in the cortex record. The function then subtracts the neuron's output_ids from the combined list of the actuator and neuron ids belonging to the neural network to get a list of ids belonging to the elements to which this neuron is not yet connected. If this list is empty, the function exits with error. If the list is not empty, it is given the name Available_Ids, from which a random id is chosen, and the neuron is then connected to the element to whom the id belongs. Finally, the agent's evo_hist list is updated, and the updated agent record is written to the database.
+
+ filter_OutlinkIdPool(C,N_Id,N_Ids)->
+ case C#constraint.connection_architecture of
+ recurrent ->
+ N_Ids;
+ feedforward ->
+ {{LI,_},neuron} = N_Id,
+ [{{Outlink_LI,Outlink_UniqueId},neuron} || {{Outlink_LI,Outlink_UniqueId},neuron} <- N_Ids, Outlink_LI > LI]
+ end.
+%The function filter_OutlinkIdPool/3 uses the connection_architecture specification in the constraint record of the agent to return a filtered neuron id pool. For the feedforward connection_architecture, the function ensures that only the neurons in the forward facing layers are allowed in the id pool.
+
+add_inlink(Agent_Id)->
+ A = genotype:read({agent,Agent_Id}),
+ Cx_Id = A#agent.cx_id,
+ Cx = genotype:read({cortex,Cx_Id}),
+ N_Ids = Cx#cortex.neuron_ids,
+ S_Ids = Cx#cortex.sensor_ids,
+ N_Id = lists:nth(random:uniform(length(N_Ids)),N_Ids),
+ N = genotype:read({neuron,N_Id}),
+ {I_Ids,_WeightLists} = lists:unzip(N#neuron.input_idps),
+ Inlink_NIdPool = filter_InlinkIdPool(A#agent.constraint,N_Id,N_Ids),
+ case lists:append(S_Ids,Inlink_NIdPool) -- I_Ids of
+ [] ->
+ exit("********ERROR:add_INLink:: Neuron already connected from all ids");
+ Available_Ids ->
+ From_Id = lists:nth(random:uniform(length(Available_Ids)),Available_Ids),
+ link_FromElementToElement(Agent_Id,From_Id,N_Id),
+ EvoHist = A#agent.evo_hist,
+ U_EvoHist = [{add_inlink,From_Id,N_Id}|EvoHist],
+ genotype:write(A#agent{evo_hist=U_EvoHist})
+ end.
+%The add_inlink/1 function extracts the list of neuron ids within the NN, and chooses a random id from this list. The input ids belonging to the neuron's input_Idps list are then subtracted from the combined neuron and sensor ids belonging to the NN. The result is a list of element ids from which the neuron is not yet connected. If this list is empty, the function exits with an error, otherwise the function chooses a random id from this list and establishes a connection between the neuron and the element correlated with randomly chosen id. Finally, the agent's evo_hist list is updated, and the updated agent is written to database.
+
+ filter_InlinkIdPool(C,N_Id,N_Ids)->
+ case C#constraint.connection_architecture of
+ recurrent ->
+ N_Ids;
+ feedforward ->
+ {{LI,_},neuron} = N_Id,
+ [{{Inlink_LI,Inlink_UniqueId},neuron} || {{Inlink_LI,Inlink_UniqueId},neuron} <- N_Ids, Inlink_LI < LI]
+ end.
+%The function filter_InlinkIdPool/3 uses the connection_architecture specification in the constraint record of the agent to return a filtered neuron id pool. For the feedforward connection_architecture, the function ensures that only the neurons in the previous layers are allowed in the filtered neuron id pool.
+
+add_neuron(Agent_Id)->%Adds neuron and connects it to other neurons, not sensors or actuators.
+ A = genotype:read({agent,Agent_Id}),
+ Generation = A#agent.generation,
+ Pattern = A#agent.pattern,
+ Cx_Id = A#agent.cx_id,
+ Cx = genotype:read({cortex,Cx_Id}),
+ N_Ids = Cx#cortex.neuron_ids,
+ S_Ids = Cx#cortex.sensor_ids,
+ A_Ids = Cx#cortex.actuator_ids,
+ {TargetLayer,TargetNeuron_Ids} = lists:nth(random:uniform(length(Pattern)),Pattern),
+ NewN_Id = {{TargetLayer,genotype:generate_UniqueId()},neuron},
+ U_N_Ids = [NewN_Id|N_Ids],
+ U_Pattern = lists:keyreplace(TargetLayer, 1, Pattern, {TargetLayer,[NewN_Id|TargetNeuron_Ids]}),
+ SpecCon = A#agent.constraint,
+ genotype:construct_Neuron(Cx_Id,Generation,SpecCon,NewN_Id,[],[]),
+ Inlink_NIdPool = filter_InlinkIdPool(A#agent.constraint,NewN_Id,N_Ids),
+ Outlink_NIdPool = filter_OutlinkIdPool(A#agent.constraint,NewN_Id,N_Ids),
+ FromElementId_Pool = Inlink_NIdPool++S_Ids,
+ ToElementId_Pool = Outlink_NIdPool++A_Ids,
+ case (Inlink_NIdPool == []) or (Outlink_NIdPool == []) of
+ true ->
+ exit("********ERROR::add_neuron(Agent_Id)::Can't add new neuron here, Inlink_NIdPool or Outlink_NIdPool is empty.");
+ false ->
+ From_ElementId = lists:nth(random:uniform(length(FromElementId_Pool)),FromElementId_Pool),
+ To_ElementId = lists:nth(random:uniform(length(ToElementId_Pool)),ToElementId_Pool),
+ link_FromElementToElement(Agent_Id,From_ElementId,NewN_Id),
+ link_FromElementToElement(Agent_Id,NewN_Id,To_ElementId),
+ U_EvoHist = [{add_neuron,From_ElementId,NewN_Id,To_ElementId}|A#agent.evo_hist],
+ genotype:write(Cx#cortex{neuron_ids = U_N_Ids}),
+ genotype:write(A#agent{pattern=U_Pattern,evo_hist=U_EvoHist})
+ end.
+%The function add_neuron/1 creats a new neuron, and connects it to a randomly selected element in the NN, and form a randomly selected element in the NN. The function first reads the agent's pattern list, selects a random layer from the pattern, and then creates a new neuron id for that layer. Then, a new, unconnected neuron, is created with that neuron id. From the cortex's neuron_ids list, two random neuron ids are chosen: From_ElementId, and To_ElementId, (they can be the same ids). The function then establishes a connection from the neuron to To_ElemenId, and to the neuron from From_ElementId. Finally, the cortex's neuron_ids list is updated by appending to it the id of the newly created neuron, the agent's evo_hist is updated, and the updated cortex and agent records are written to database.
+
+outsplice(Agent_Id)->
+ A = genotype:read({agent,Agent_Id}),
+ Generation = A#agent.generation,
+ Pattern = A#agent.pattern,
+ Cx_Id = A#agent.cx_id,
+ Cx = genotype:read({cortex,Cx_Id}),
+ N_Ids = Cx#cortex.neuron_ids,
+ N_Id = lists:nth(random:uniform(length(N_Ids)),N_Ids),
+ N = genotype:read({neuron,N_Id}),
+ {{LayerIndex,_UId},neuron} = N_Id,
+%Compose a feedforward Id pool, to create the splice from.
+ O_IdPool = case [{{TargetLayerIndex,TargetUId},TargetType} || {{TargetLayerIndex,TargetUId},TargetType} <- N#neuron.output_ids, TargetLayerIndex > LayerIndex] of
+ [] ->
+ exit("********ERROR:outsplice:: NeuroLink_OutputSplice O_IdPool == []");
+ Ids ->
+ Ids
+ end,
+%Choose a random neuron in the output_ids for splicing.
+ O_Id = lists:nth(random:uniform(length(O_IdPool)),O_IdPool),
+ {{OutputLayerIndex,_Output_UId},_OutputType} = O_Id,
+%Create a new Layer, or select an existing one between N_Id and the O_Id, and create the new unlinked neuron.
+ NewLI = get_NewLI(LayerIndex,OutputLayerIndex,next,Pattern),
+ NewN_Id={{NewLI,genotype:generate_UniqueId()},neuron},
+ SpecCon = A#agent.constraint,
+ genotype:construct_Neuron(Cx_Id,Generation,SpecCon,NewN_Id,[],[]),
+%Update pattern.
+ U_Pattern=case lists:keymember(NewLI,1,Pattern) of
+ true->
+ {NewLI,InLayerIds}=lists:keyfind(NewLI, 1, Pattern),
+ lists:keyreplace(NewLI, 1, Pattern, {NewLI,[NewN_Id|InLayerIds]});
+ false ->
+ lists:sort([{NewLI,[NewN_Id]}|Pattern])
+ end,
+%Disconnect the N_Id from the O_Id, and reconnect through NewN_Id
+ cutlink_FromElementToElement(Agent_Id,N_Id,O_Id),
+ link_FromElementToElement(Agent_Id,N_Id,NewN_Id),
+ link_FromElementToElement(Agent_Id,NewN_Id,O_Id),
+%Updated agent
+ EvoHist = A#agent.evo_hist,
+ U_EvoHist = [{outsplice,N_Id,NewN_Id,O_Id}|EvoHist],
+ U_Cx = Cx#cortex{neuron_ids = [NewN_Id|Cx#cortex.neuron_ids]},
+ genotype:write(U_Cx),
+ genotype:write(A#agent{pattern=U_Pattern,evo_hist=U_EvoHist}).
+%The function outsplice/1 chooses a random neuron id from the cortex's neuron_ids list, disconnects it from a randomly chosen id in its output_ids list, and then reconnects it to the same element through a newly created neuron. The function first chooses a random neuron N with the neuron id N_Id from the cortex's neuron_ids list. Then the neuron N's output_ids list is extracted, and a new id list O_IdPool is created from the ids in the output_ids list that are located in the layer after the N_Id's layer (the ids of elements to whom the N_Id forms a feed forward connection). From that sublist of N's output_ids list, a random O_Id is chosen, and if the sublist is empty, then the function exits with an error. Then N_Id is disconnected from the O_Id. The function then creates or extracts a new layer index, NewLI, located between N_Id and O_Id. If there exists a layer between N_Id and O_Id, NewLI is simply that layer, if on the other hand O_Id's layer comes imedietly after N_Id's then a new layer is created between O_Id and N_Id, whose layer index is in the middle of the two elements. A new unconnected neuron is then created in that layer, with a neuron id NewN_Id, and connected to the O_Id, and from the N_Id, thus establishing a path from N_Id to O_Id through the NewN_Id. The cortex's neuron_ids is updated with the NewN_Id, and the agent's evo_hist list is updated with the new mutation operator tuple {outsplice,N_Id,Newn_Id,O_Id}. Finally, the updated cortex and agent are written to database.
+
+get_NewLI(LI,LI,_Direction,_Pattern)->
+ exit("******** ERROR: get_NewLI FromLI == ToLI");
+get_NewLI(FromLI,ToLI,Direction,Pattern)->
+ NewLI = case Direction of
+ next ->
+ get_NextLI(Pattern,FromLI,ToLI);
+ prev ->
+ get_PrevLI(lists:reverse(Pattern),FromLI,ToLI)
+ end,
+ NewLI.
+%get_NewLI/4 calculates or creats a new layer index located between FromLI and ToLI. The function calls get_NextLI/3 or get_PrevLI/3, depending on whether the direction of the connection is forward, from sensors towards actuators (Direction = next) or from actuators towards sensors (Direction = prev), which is the case when executing an insplice/1 function, which calculates or creates a new layer between the N_Id and one of the ids in its input_idps list. If the FromLI == ToLI, the function exits with an error.
+
+ get_NextLI([{FromLI,_LastLayerNIds}],FromLI,ToLI)->
+ (FromLI+ToLI)/2;
+ get_NextLI([{LI,_LayerNIds}|Pattern],FromLI,ToLI)->
+ case LI == FromLI of
+ true ->
+ [{NextLI,_NextLayerNIds}|_] = Pattern,
+ case NextLI == ToLI of
+ true ->
+ (FromLI + ToLI)/2;
+ false ->
+ NextLI
+ end;
+ false ->
+ get_NextLI(Pattern,FromLI,ToLI)
+ end.
+%get_NextLI checks whether the ToLI comes directly after FromLI, or whether there is another layer between them. If there is another layer between them, then that layer is returned, and the splice neuron should then be put into that layer. If there is no layer between FromLI and ToLI, then a new layer is created in the middle, the new layer index has the value of (FromLI+ToLI)/2.
+
+ get_PrevLI([{FromLI,_FirstLayerNIds}],FromLI,ToLI)->
+ (FromLI+ToLI)/2;
+ get_PrevLI([{LI,_LayerNIds}|Pattern],FromLI,ToLI)->
+ case LI == FromLI of
+ true ->
+ [{PrevLI,_PrevLayerNIds}|_] = Pattern,
+ case PrevLI == ToLI of
+ true ->
+ (FromLI + ToLI)/2;
+ false ->
+ PrevLI
+ end;
+ false ->
+ get_PrevLI(Pattern,FromLI,ToLI)
+ end.
+%get_PrevLI checks whether the The ToLI comes directly before FromLI, or whetehr there is another layer in between them. If there is another layer, then the function returns that layer, if no such layer is found, the the function creates a new layer indeex, (FromLI+ToLI)/2.
+
+add_sensorlink(Agent_Id)->
+ A = genotype:read({agent,Agent_Id}),
+ Cx_Id = A#agent.cx_id,
+ Cx = genotype:read({cortex,Cx_Id}),
+ N_Ids = Cx#cortex.neuron_ids,
+ S_Ids = Cx#cortex.sensor_ids,
+ S_Id = lists:nth(random:uniform(length(S_Ids)),S_Ids),
+ S = genotype:read({sensor,S_Id}),
+ case N_Ids -- S#sensor.fanout_ids of
+ [] ->
+ exit("********ERROR:add_sensorlink:: Sensor already connected to all N_Ids");
+ Available_Ids ->
+ N_Id = lists:nth(random:uniform(length(Available_Ids)),Available_Ids),
+ link_FromElementToElement(Agent_Id,S_Id,N_Id),
+ EvoHist = A#agent.evo_hist,
+ U_EvoHist = [{add_sensorlink,S_Id,N_Id}|EvoHist],
+ genotype:write(A#agent{evo_hist=U_EvoHist})
+ end.
+%The function add_sensorlink/1, randomly selects a S_Id from the cortex's sensor_ids list, and then establishes from that sensor a connection to a still unlinked to this sensor, randomly selected neuron from the cortex's neuron_ids list. The function first selects a random sensor id S_Id from the cortex's sensor_ids list. Then a list of N_Ids to which S_Id is not yet connected is calculated by subtracting from the N_Ids the S_Ids fanout_ids list. If the resulting list is empty, then the function exits with an error since there is no other neurons to which the sensor can establish a new connection to. If the list is not empty, then a random neuron id, N_Id, is selected from this list, and a connection is established from S_Id to N_Id. Finally, the agent's evo_hist is then updated, and it is written to database.
+
+add_actuatorlink(Agent_Id)->%TODO: There should be a preference towards non fully connected actuators.
+ Agent = genotype:read({agent,Agent_Id}),
+ Cx_Id = Agent#agent.cx_id,
+ Cx = genotype:read({cortex,Cx_Id}),
+ N_Ids = Cx#cortex.neuron_ids,
+ A_Ids = Cx#cortex.actuator_ids,
+ A_Id = lists:nth(random:uniform(length(A_Ids)),A_Ids),
+ A = genotype:read({actuator,A_Id}),
+ case N_Ids -- A#actuator.fanin_ids of
+ [] ->
+ exit("********ERROR:add_actuatorlink:: Actuator already connected from all N_Ids");
+ Available_Ids ->
+ N_Id = lists:nth(random:uniform(length(Available_Ids)),Available_Ids),
+ link_FromElementToElement(Agent_Id,N_Id,A_Id),
+ EvoHist = Agent#agent.evo_hist,
+ U_EvoHist = [{add_actuatorlink,N_Id,A_Id}|EvoHist],
+ genotype:write(Agent#agent{evo_hist=U_EvoHist})
+ end.
+%The add_actuatorlink/1 selects a random actuator id A_Id from the cortex's actuator_ids list, and then connects A_Id to randomly selected neuron to which the A_Id is not yet connected to. The function first selects a random actuator id A_Id from the cortex's actuator_ids list. Then the function creates a list of neuron ids to which it is not yet connected by subtracting its fanin_ids list from the cortex's neuron_ids list. If the resulting id pool is empty, then the function exits with error. If the resulting id pool is not empty, a neuron id N_Id is randomly chosen from this id list, and the actuator is connected to this randomly chosen neuron. Finally, the agent's evo_hist is updated, and the updated agent is written to database.
+
+add_sensor(Agent_Id)->%TODO: There should be a preference towards adding sensors not yet used.
+ Agent = genotype:read({agent,Agent_Id}),
+ Cx_Id = Agent#agent.cx_id,
+ Cx = genotype:read({cortex,Cx_Id}),
+ S_Ids = Cx#cortex.sensor_ids,
+ SpeCon = Agent#agent.constraint,
+ Morphology = SpeCon#constraint.morphology,
+ case morphology:get_Sensors(Morphology)--[(genotype:read({sensor,S_Id}))#sensor{id=undefined,cx_id=undefined,fanout_ids=[],generation=undefined} || S_Id<-S_Ids] of
+ [] ->
+ exit("********ERROR:add_sensor(Agent_Id):: NN system is already using all available sensors");
+ Available_Sensors ->io:format("Available_Sensors"),
+ NewS_Id = {{-1,genotype:generate_UniqueId()},sensor},
+ NewSensor=(lists:nth(random:uniform(length(Available_Sensors)),Available_Sensors))#sensor{id=NewS_Id,cx_id=Cx_Id},
+ genotype:write(NewSensor),
+ N_Ids = Cx#cortex.neuron_ids,
+ N_Id = lists:nth(random:uniform(length(N_Ids)),N_Ids),
+ link_FromElementToElement(Agent_Id,NewS_Id,N_Id),
+ EvoHist = Agent#agent.evo_hist,
+ U_EvoHist = [{add_sensor,NewS_Id,N_Id}|EvoHist],
+ U_Cx = Cx#cortex{sensor_ids=[NewS_Id|S_Ids]},
+ genotype:write(U_Cx),
+ genotype:write(Agent#agent{evo_hist=U_EvoHist})
+ end.
+%The add_sensor/1 function adds and connects a new sensor to the neural network, a sensor type to which the NN is not yet connected from. After retrieving the morphology name from the constraints record retrived from the agent, the complete set of available sensors is retrevied using the morphology:get_Sensors/1 function. From this complete sensor list we subtract the sensor tuples used by the NN based system, but first we revert those sensor's id and cx_id back to undefined, since that is what the initial state of the sensor tuples are. With the NN's sensors ids and cx_ids reverted back to undefined, they can be subtracted from the compelete set of sensors. If the resulting list is empty, then the function exits with an error. On the other hand if ther esulting list is not empty, then there are still sensors which the NN is not yet using (though it does not mean that using the sensors would make the NN better, these sensors might be simply useless, and hence not previously incorporated during evolution). From this resulting list we then select a random sensor, and create for it a unique sensor id NewS_Id. A random neuron id N_Id is then selected from the cortex's neuron_ids list, and a connection is established from NewS_Id to the N_Id. The cortex's sensor_ids is updated with the new sensor's id, and the agent's evo_hist is updated with the new tuple. The updated cortex and agent records are then written to database.
+
+add_actuator(Agent_Id)->%TODO: There should be a preference towards adding actuators not yet used.
+ Agent = genotype:read({agent,Agent_Id}),
+ Cx_Id = Agent#agent.cx_id,
+ Cx = genotype:read({cortex,Cx_Id}),
+ A_Ids = Cx#cortex.actuator_ids,%TODO: Should we fill in all the fanin_ids locations, or just 1? and let evolution fill the rest?
+ SpeCon = Agent#agent.constraint,
+ Morphology = SpeCon#constraint.morphology,
+ case morphology:get_Actuators(Morphology)--[(genotype:read({actuator,A_Id}))#actuator{cx_id=undefined,id=undefined,fanin_ids=[],generation=undefined} || A_Id<-A_Ids] of
+ [] ->
+ exit("********ERROR:add_actuator(Agent_Id):: NN system is already using all available actuators");
+ Available_Actuators ->
+ NewA_Id = {{1,genotype:generate_UniqueId()},actuator},
+ NewActuator=(lists:nth(random:uniform(length(Available_Actuators)),Available_Actuators))#actuator{id=NewA_Id,cx_id=Cx_Id},
+ genotype:write(NewActuator),