AI-Genetic-Pro

 view release on metacpan or  search on metacpan

Changes  view on Meta::CPAN

Revision history for Perl extension AI::Genetic::Pro.

1.009 Mon, 09 Jan 2023 21:53:41 +0100
	- First release with Dist::Zilla.

1.008 Thu, 05 Jan 2023 11:16:26 +0100
	- Fix: MANIFEST

1.007 Sun, 16 Oct 2022 15:16:41 +0200
	- One more mprovement.

1.006 Sun, 16 Oct 2022 15:02:03 +0200
	- Improvements in a range of efficiency (thanks to Mario Roy :-)

1.005 Tue, 08 Dec 2020 21:32:15 +0100
	- Fixed some bugs in a documentation.

1.004 Sun, 15 Nov 2020 14:18:24 +0100
	- Added specification of required MCE version.

1.003 Sun, 15 Nov 2020 09:14:05 +0100
	- Fixed bug causing "internal error" in a case of cloning raw chromosomes.

1.002 Sat, 14 Nov 2020 21:33:38 +0100
	- Fixed bug in a documentation.

1.001 Sat, 14 Nov 2020 19:20:53 +0100
	- Fixed bug in a documentation.

1.000 Sat, 14 Nov 2020 18:03:39 +0100
	- Added support (long awaited ;) for multi-core processing, through MCE.
	- Added direct support for native arrays instead of packed scalars (for chromosomes).
	- Some bug fixes.

0.401 Fri, 19 Nov 2011 19:22:45 +0100
	- Some bug fixes.

0.341 Mon, 23 Mar 2009 17:21:52 +0100
	- Fixed bug in a documentation. Thanks to Randal L. Schwartz :-)

0.34  Tue, 17 Mar 2009 20:39:16 +0100 
	- Fixed bug in PMX strategy. Thanks to Maciej Misiak :-)

0.335 Sat, 07 Feb 2009 20:04:52 +0100
	- Little changes in a Makefile.PL (especially for Sun Solaris)

0.334 Fri, 23 Jan 2009 00:03:26 +0100
	- Module 'Digest::MD5' is loaded by default,

0.333 Fri, 22 Jan 2009 15:30:06 +0100
	- Some improvments in 'getFittest' function,
	- Added 'getFittest_as_arrayref' function,

0.332 Wed, 21 Jan 2009 00:31:01 +0100
	- Some changes in tests.

0.331 Tue, 20 Jan 2009 23:55:20 +0100
	- Added tests.
	- Some improvments in the 'inject' function.

0.33 Mon, 19 Jan 2009 00:38:06 +0100
	- Added 'strict' mode.
	- Added 'inject' function.

0.32 Sun, 18 Jan 2009 01:16:37 +0100
	- Fixes in rangevectors.

0.31 Sat, 17 Jan 2009 20:32:27 +0100
	- Fixes in mutation. Added changing of fitness for mutated chromosomes.

0.30 Fri, 16 Jan 2009 00:06:29 +0100
	- Fixes in as_string method.

0.29 Thu, 15 Jan 2009 21:13:15 +0100
	- Added full support for variable-length chromosomes.

0.28
	- Little fixes. Again :-)
	
0.27
	- Little fixes.

0.26 Sun, 11 Jan 2009 22:04:26 +0100
	- Little modifications in crossover.
	- Added supprot for variable-length chromosomes. Thanks to Leonid Zamdborg :-)

0.25 Mon, 24 Nov 2008 18:57:27 +0100
	- Little fix in a counter of generations.

0.24 Mon, 10 Nov 2008 11:34:12 +0100
	- Little speed up in main loop.

0.23 Mon, 10 Nov 2008 10:14:32 +0100
	- Little modification in main loop. Infinity loop available.

0.22 Wed, 05 Nov 2008 00:49:18 +0100
	- Added "-preserve" parameter.
	- Little speed up (Class::Accessor::Fast => Class::Accessor::Fast::XS).

0.21 Fri, 10 Oct 2008 03:41:39 +0200
	- Fixed mutation in rangevectors. Thanks to Christoph Meissner :-)

0.20 Sat, 04 Oct 2008 02:39:28 +0200
	- Again... Little bug in mutation (inversion; listvectors only) - fixed. Thanks to Alec Chen :-)

0.19 Wed, 24 Sep 2008 19:52:33 +0200
	- Little bug in mutation - fixed. Thanks to Alec Chen :-) 

0.18 Fri, 19 Sep 2008 21:41:46 +0200
	- Little fixes in documentation.

0.17 Fri, 19 Sep 2008 03:52:21 +0200
	- Added documentation :-)

0.16 Mon, 15 Sep 2008 12:12:44 +0200
	- Increased speed during chart generation.
	- More documentation.
	- Feature 'say' removed for compatibility.

0.15	Sun, 14 Sep 2008 02:14:02 +0200
	- Increased speed :-)
	- Changes way of mutation is made.
	- Added charts.
	- Added possibility to save/load GA.
	- More documentation.

0.1		Sat, 13 Sep 2008 02:04:25 +0200
	- First release.

LICENSE  view on Meta::CPAN

This software is Copyright (c) 2023 by Łukasz Strzelecki.

This is free software, licensed under:

  The GNU Lesser General Public License, Version 2.1, February 1999

The GNU Lesser General Public License (LGPL)
Version 2.1, February 1999

  (The master copy of this license lives on the GNU website.)

Copyright (C) 1991, 1999 Free Software Foundation, Inc. 59
51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.

[This is the first released version of the Lesser GPL. It also
counts as the successor of the GNU Library Public License,
version 2, hence the version number 2.1.]

LICENSE  view on Meta::CPAN

You may charge a fee for the physical act of transferring a
copy, and you may at your option offer warranty protection in
exchange for a fee.

2. You may modify your copy or copies of the Library or any
portion of it, thus forming a work based on the Library, and
copy and distribute such modifications or work under the
terms of Section 1 above, provided that you also meet all of
these conditions:

     a) The modified work must itself be a software
     library.
     b) You must cause the files modified to carry
     prominent notices stating that you changed the
     files and the date of any change.
     c) You must cause the whole of the work to be
     licensed at no charge to all third parties under
     the terms of this License.
     d) If a facility in the modified Library refers to a
     function or a table of data to be supplied by an
     application program that uses the facility, other
     than as an argument passed when the facility
     is invoked, then you must make a good faith
     effort to ensure that, in the event an application
     does not supply such function or table, the
     facility still operates, and performs whatever
     part of its purpose remains meaningful.

     (For example, a function in a library to
     compute square roots has a purpose that is
     entirely well-defined independent of the
     application. Therefore, Subsection 2d requires
     that any application-supplied function or table
     used by this function must be optional: if the
     application does not supply it, the square root
     function must still compute square roots.)

     These requirements apply to the modified work
     as a whole. If identifiable sections of that work
     are not derived from the Library, and can be
     reasonably considered independent and
     separate works in themselves, then this
     License, and its terms, do not apply to those
     sections when you distribute them as separate
     works. But when you distribute the same
     sections as part of a whole which is a work
     based on the Library, the distribution of the
     whole must be on the terms of this License,
     whose permissions for other licensees extend
     to the entire whole, and thus to each and every
     part regardless of who wrote it.

     Thus, it is not the intent of this section to claim
     rights or contest your rights to work written
     entirely by you; rather, the intent is to exercise
     the right to control the distribution of derivative
     or collective works based on the Library.

     In addition, mere aggregation of another work
     not based on the Library with the Library (or
     with a work based on the Library) on a volume
     of a storage or distribution medium does not
     bring the other work under the scope of this
     License.

3. You may opt to apply the terms of the ordinary GNU
General Public License instead of this License to a given
copy of the Library. To do this, you must alter all the notices
that refer to this License, so that they refer to the ordinary
GNU General Public License, version 2, instead of to this
License. (If a newer version than version 2 of the ordinary
GNU General Public License has appeared, then you can
specify that version instead if you wish.) Do not make any
other change in these notices.

LICENSE  view on Meta::CPAN

such modifications.

You must give prominent notice with each copy of the work
that the Library is used in it and that the Library and its use
are covered by this License. You must supply a copy of this
License. If the work during execution displays copyright
notices, you must include the copyright notice for the Library
among them, as well as a reference directing the user to the
copy of this License. Also, you must do one of these things:

     a) Accompany the work with the complete
     corresponding machine-readable source code
     for the Library including whatever changes were
     used in the work (which must be distributed
     under Sections 1 and 2 above); and, if the work
     is an executable linked with the Library, with
     the complete machine-readable "work that
     uses the Library", as object code and/or
     source code, so that the user can modify the
     Library and then relink to produce a modified
     executable containing the modified Library. (It
     is understood that the user who changes the
     contents of definitions files in the Library will
     not necessarily be able to recompile the
     application to use the modified definitions.)

     b) Use a suitable shared library mechanism for
     linking with the Library. A suitable mechanism
     is one that (1) uses at run time a copy of the
     library already present on the user's computer
     system, rather than copying library functions
     into the executable, and (2) will operate
     properly with a modified version of the library, if
     the user installs one, as long as the modified
     version is interface-compatible with the version
     that the work was made with.

     c) Accompany the work with a written offer,
     valid for at least three years, to give the same
     user the materials specified in Subsection 6a,
     above, for a charge no more than the cost of
     performing this distribution.

     d) If distribution of the work is made by offering
     access to copy from a designated place, offer
     equivalent access to copy the above specified
     materials from the same place.

     e) Verify that the user has already received a
     copy of these materials or that you have
     already sent this user a copy.

For an executable, the required form of the "work that uses
the Library" must include any data and utility programs
needed for reproducing the executable from it. However, as a
special exception, the materials to be distributed need not
include anything that is normally distributed (in either source
or binary form) with the major components (compiler, kernel,
and so on) of the operating system on which the executable
runs, unless that component itself accompanies the
executable.

LICENSE  view on Meta::CPAN

an executable that you distribute.

7. You may place library facilities that are a work based on
the Library side-by-side in a single library together with other
library facilities not covered by this License, and distribute
such a combined library, provided that the separate
distribution of the work based on the Library and of the other
library facilities is otherwise permitted, and provided that you
do these two things:

     a) Accompany the combined library with a
     copy of the same work based on the Library,
     uncombined with any other library facilities.
     This must be distributed under the terms of the
     Sections above.

     b) Give prominent notice with the combined
     library of the fact that part of it is a work based
     on the Library, and explaining where to find the
     accompanying uncombined form of the same
     work.

8. You may not copy, modify, sublicense, link with, or
distribute the Library except as expressly provided under this
License. Any attempt otherwise to copy, modify, sublicense,
link with, or distribute the Library is void, and will
automatically terminate your rights under this License.
However, parties who have received copies, or rights, from
you under this License will not have their licenses terminated
so long as such parties remain in full compliance.

META.json  view on Meta::CPAN

{
   "abstract" : "Efficient genetic algorithms for professional purpose with support for multiprocessing.",
   "author" : [
      "\u0141ukasz Strzelecki <lukasz@strzeleccy.eu>"
   ],
   "dynamic_config" : 0,
   "generated_by" : "Dist::Zilla version 6.017, CPAN::Meta::Converter version 2.150010",
   "license" : [
      "lgpl_2_1"
   ],
   "meta-spec" : {
      "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec",
      "version" : 2
   },
   "name" : "AI-Genetic-Pro",
   "prereqs" : {
      "configure" : {
         "requires" : {
            "ExtUtils::MakeMaker" : "0"
         }
      },
      "runtime" : {
         "requires" : {
            "Carp" : "0",
            "Class::Accessor::Fast::XS" : "0",
            "Clone" : "0",
            "Digest::MD5" : "0",
            "Exporter::Lite" : "0",
            "GD::Graph::linespoints" : "1.54",
            "List::MoreUtils" : "0",
            "List::Util" : "0",
            "MCE" : "1.874",
            "MCE::Map" : "1.874",
            "Math::Random" : "0.72",
            "Storable" : "2.05",
            "Struct::Compare" : "0",
            "Tie::Array::Packed" : "0.13",
            "UNIVERSAL::require" : "0"
         }
      }
   },
   "release_status" : "stable",
   "resources" : {
      "homepage" : "https://github.com/d-strzelec/AI-Genetic-Pro",
      "repository" : {
         "type" : "git",
         "url" : "https://github.com/d-strzelec/AI-Genetic-Pro.git",
         "web" : "https://github.com/d-strzelec/AI-Genetic-Pro"
      }
   },
   "version" : "1.009",
   "x_Dist_Zilla" : {
      "perl" : {
         "version" : "5.032001"
      },
      "plugins" : [
         {
            "class" : "Dist::Zilla::Plugin::GatherDir",
            "config" : {
               "Dist::Zilla::Plugin::GatherDir" : {
                  "exclude_filename" : [],
                  "exclude_match" : [],
                  "follow_symlinks" : 0,
                  "include_dotfiles" : 0,
                  "prefix" : "",
                  "prune_directory" : [],
                  "root" : "."
               }
            },
            "name" : "GatherDir",
            "version" : "6.017"
         },
         {
            "class" : "Dist::Zilla::Plugin::PruneCruft",
            "name" : "PruneCruft",
            "version" : "6.017"
         },
         {
            "class" : "Dist::Zilla::Plugin::PruneFiles",
            "name" : "PruneFiles",
            "version" : "6.017"
         },
         {
            "class" : "Dist::Zilla::Plugin::ManifestSkip",
            "name" : "ManifestSkip",
            "version" : "6.017"
         },
         {
            "class" : "Dist::Zilla::Plugin::MetaYAML",
            "name" : "MetaYAML",
            "version" : "6.017"
         },
         {
            "class" : "Dist::Zilla::Plugin::MetaJSON",
            "name" : "MetaJSON",
            "version" : "6.017"
         },
         {
            "class" : "Dist::Zilla::Plugin::License",
            "name" : "License",
            "version" : "6.017"
         },
         {
            "class" : "Dist::Zilla::Plugin::MakeMaker",
            "config" : {
               "Dist::Zilla::Role::TestRunner" : {
                  "default_jobs" : 1
               }
            },
            "name" : "MakeMaker",
            "version" : "6.017"
         },
         {
            "class" : "Dist::Zilla::Plugin::Manifest",
            "name" : "Manifest",
            "version" : "6.017"
         },
         {
            "class" : "Dist::Zilla::Plugin::PkgVersion",
            "name" : "PkgVersion",
            "version" : "6.017"
         },
         {
            "class" : "Dist::Zilla::Plugin::ReadmeFromPod",
            "name" : "ReadmeFromPod",
            "version" : "0.37"
         },
         {
            "class" : "Dist::Zilla::Plugin::MetaConfig",
            "name" : "MetaConfig",
            "version" : "6.017"
         },
         {
            "class" : "Dist::Zilla::Plugin::Prereqs",
            "config" : {
               "Dist::Zilla::Plugin::Prereqs" : {
                  "phase" : "runtime",
                  "type" : "requires"
               }
            },
            "name" : "Prereqs",
            "version" : "6.017"
         },
         {
            "class" : "Dist::Zilla::Plugin::GithubMeta",
            "name" : "GithubMeta",
            "version" : "0.58"
         },
         {
            "class" : "Dist::Zilla::Plugin::Git::Commit",
            "config" : {
               "Dist::Zilla::Plugin::Git::Commit" : {
                  "add_files_in" : [],
                  "commit_msg" : "Release v%v%n%n%c",
                  "signoff" : 0
               },
               "Dist::Zilla::Role::Git::DirtyFiles" : {
                  "allow_dirty" : [
                     "Changes",
                     "dist.ini"
                  ],
                  "allow_dirty_match" : [
                     "(?^u:.*(?:\\.pm|README|\\*.t)$)"
                  ],
                  "changelog" : "Changes"
               },
               "Dist::Zilla::Role::Git::Repo" : {
                  "git_version" : "2.30.2",
                  "repo_root" : "."
               },
               "Dist::Zilla::Role::Git::StringFormatter" : {
                  "time_zone" : "local"
               }
            },
            "name" : "Git::Commit",
            "version" : "2.047"
         },
         {
            "class" : "Dist::Zilla::Plugin::Git::Push",
            "config" : {
               "Dist::Zilla::Plugin::Git::Push" : {
                  "push_to" : [
                     "d-strzelec"
                  ],
                  "remotes_must_exist" : 1
               },
               "Dist::Zilla::Role::Git::Repo" : {
                  "git_version" : "2.30.2",
                  "repo_root" : "."
               }
            },
            "name" : "Git::Push",
            "version" : "2.047"
         },
         {
            "class" : "Dist::Zilla::Plugin::TestRelease",
            "name" : "TestRelease",
            "version" : "6.017"
         },
         {
            "class" : "Dist::Zilla::Plugin::ConfirmRelease",
            "name" : "ConfirmRelease",
            "version" : "6.017"
         },
         {
            "class" : "Dist::Zilla::Plugin::UploadToCPAN",
            "name" : "UploadToCPAN",
            "version" : "6.017"
         },
         {
            "class" : "Dist::Zilla::Plugin::FinderCode",
            "name" : ":InstallModules",
            "version" : "6.017"
         },
         {
            "class" : "Dist::Zilla::Plugin::FinderCode",
            "name" : ":IncModules",
            "version" : "6.017"
         },
         {
            "class" : "Dist::Zilla::Plugin::FinderCode",
            "name" : ":TestFiles",
            "version" : "6.017"
         },
         {
            "class" : "Dist::Zilla::Plugin::FinderCode",
            "name" : ":ExtraTestFiles",
            "version" : "6.017"
         },
         {
            "class" : "Dist::Zilla::Plugin::FinderCode",
            "name" : ":ExecFiles",
            "version" : "6.017"
         },
         {
            "class" : "Dist::Zilla::Plugin::FinderCode",
            "name" : ":PerlExecFiles",
            "version" : "6.017"
         },
         {
            "class" : "Dist::Zilla::Plugin::FinderCode",
            "name" : ":ShareFiles",
            "version" : "6.017"
         },
         {
            "class" : "Dist::Zilla::Plugin::FinderCode",
            "name" : ":MainModule",
            "version" : "6.017"
         },
         {
            "class" : "Dist::Zilla::Plugin::FinderCode",
            "name" : ":AllFiles",
            "version" : "6.017"
         },
         {
            "class" : "Dist::Zilla::Plugin::FinderCode",
            "name" : ":NoFiles",
            "version" : "6.017"
         }
      ],
      "zilla" : {
         "class" : "Dist::Zilla::Dist::Builder",
         "config" : {
            "is_trial" : 0
         },
         "version" : "6.017"
      }
   },
   "x_generated_by_perl" : "v5.32.1",
   "x_serialization_backend" : "Cpanel::JSON::XS version 4.25",
   "x_spdx_expression" : "LGPL-2.1"
}

META.yml  view on Meta::CPAN

---
abstract: 'Efficient genetic algorithms for professional purpose with support for multiprocessing.'
author:
  - 'Łukasz Strzelecki <lukasz@strzeleccy.eu>'
build_requires: {}
configure_requires:
  ExtUtils::MakeMaker: '0'
dynamic_config: 0
generated_by: 'Dist::Zilla version 6.017, CPAN::Meta::Converter version 2.150010'
license: lgpl
meta-spec:
  url: http://module-build.sourceforge.net/META-spec-v1.4.html
  version: '1.4'
name: AI-Genetic-Pro
requires:
  Carp: '0'
  Class::Accessor::Fast::XS: '0'
  Clone: '0'
  Digest::MD5: '0'
  Exporter::Lite: '0'
  GD::Graph::linespoints: '1.54'
  List::MoreUtils: '0'
  List::Util: '0'
  MCE: '1.874'
  MCE::Map: '1.874'
  Math::Random: '0.72'
  Storable: '2.05'
  Struct::Compare: '0'
  Tie::Array::Packed: '0.13'
  UNIVERSAL::require: '0'
resources:
  homepage: https://github.com/d-strzelec/AI-Genetic-Pro
  repository: https://github.com/d-strzelec/AI-Genetic-Pro.git
version: '1.009'
x_Dist_Zilla:
  perl:
    version: '5.032001'
  plugins:
    -
      class: Dist::Zilla::Plugin::GatherDir
      config:
        Dist::Zilla::Plugin::GatherDir:
          exclude_filename: []
          exclude_match: []
          follow_symlinks: 0
          include_dotfiles: 0
          prefix: ''
          prune_directory: []
          root: .
      name: GatherDir
      version: '6.017'
    -
      class: Dist::Zilla::Plugin::PruneCruft
      name: PruneCruft
      version: '6.017'
    -
      class: Dist::Zilla::Plugin::PruneFiles
      name: PruneFiles
      version: '6.017'
    -
      class: Dist::Zilla::Plugin::ManifestSkip
      name: ManifestSkip
      version: '6.017'
    -
      class: Dist::Zilla::Plugin::MetaYAML
      name: MetaYAML
      version: '6.017'
    -
      class: Dist::Zilla::Plugin::MetaJSON
      name: MetaJSON
      version: '6.017'
    -
      class: Dist::Zilla::Plugin::License
      name: License
      version: '6.017'
    -
      class: Dist::Zilla::Plugin::MakeMaker
      config:
        Dist::Zilla::Role::TestRunner:
          default_jobs: 1
      name: MakeMaker
      version: '6.017'
    -
      class: Dist::Zilla::Plugin::Manifest
      name: Manifest
      version: '6.017'
    -
      class: Dist::Zilla::Plugin::PkgVersion
      name: PkgVersion
      version: '6.017'
    -
      class: Dist::Zilla::Plugin::ReadmeFromPod
      name: ReadmeFromPod
      version: '0.37'
    -
      class: Dist::Zilla::Plugin::MetaConfig
      name: MetaConfig
      version: '6.017'
    -
      class: Dist::Zilla::Plugin::Prereqs
      config:
        Dist::Zilla::Plugin::Prereqs:
          phase: runtime
          type: requires
      name: Prereqs
      version: '6.017'
    -
      class: Dist::Zilla::Plugin::GithubMeta
      name: GithubMeta
      version: '0.58'
    -
      class: Dist::Zilla::Plugin::Git::Commit
      config:
        Dist::Zilla::Plugin::Git::Commit:
          add_files_in: []
          commit_msg: 'Release v%v%n%n%c'
          signoff: 0
        Dist::Zilla::Role::Git::DirtyFiles:
          allow_dirty:
            - Changes
            - dist.ini
          allow_dirty_match:
            - (?^u:.*(?:\.pm|README|\*.t)$)
          changelog: Changes
        Dist::Zilla::Role::Git::Repo:
          git_version: 2.30.2
          repo_root: .
        Dist::Zilla::Role::Git::StringFormatter:
          time_zone: local
      name: Git::Commit
      version: '2.047'
    -
      class: Dist::Zilla::Plugin::Git::Push
      config:
        Dist::Zilla::Plugin::Git::Push:
          push_to:
            - d-strzelec
          remotes_must_exist: 1
        Dist::Zilla::Role::Git::Repo:
          git_version: 2.30.2
          repo_root: .
      name: Git::Push
      version: '2.047'
    -
      class: Dist::Zilla::Plugin::TestRelease
      name: TestRelease
      version: '6.017'
    -
      class: Dist::Zilla::Plugin::ConfirmRelease
      name: ConfirmRelease
      version: '6.017'
    -
      class: Dist::Zilla::Plugin::UploadToCPAN
      name: UploadToCPAN
      version: '6.017'
    -
      class: Dist::Zilla::Plugin::FinderCode
      name: ':InstallModules'
      version: '6.017'
    -
      class: Dist::Zilla::Plugin::FinderCode
      name: ':IncModules'
      version: '6.017'
    -
      class: Dist::Zilla::Plugin::FinderCode
      name: ':TestFiles'
      version: '6.017'
    -
      class: Dist::Zilla::Plugin::FinderCode
      name: ':ExtraTestFiles'
      version: '6.017'
    -
      class: Dist::Zilla::Plugin::FinderCode
      name: ':ExecFiles'
      version: '6.017'
    -
      class: Dist::Zilla::Plugin::FinderCode
      name: ':PerlExecFiles'
      version: '6.017'
    -
      class: Dist::Zilla::Plugin::FinderCode
      name: ':ShareFiles'
      version: '6.017'
    -
      class: Dist::Zilla::Plugin::FinderCode
      name: ':MainModule'
      version: '6.017'
    -
      class: Dist::Zilla::Plugin::FinderCode
      name: ':AllFiles'
      version: '6.017'
    -
      class: Dist::Zilla::Plugin::FinderCode
      name: ':NoFiles'
      version: '6.017'
  zilla:
    class: Dist::Zilla::Dist::Builder
    config:
      is_trial: '0'
    version: '6.017'
x_generated_by_perl: v5.32.1
x_serialization_backend: 'YAML::Tiny version 1.73'
x_spdx_expression: LGPL-2.1

Makefile.PL  view on Meta::CPAN

# This file was automatically generated by Dist::Zilla::Plugin::MakeMaker v6.017.
use strict;
use warnings;



use ExtUtils::MakeMaker;

my %WriteMakefileArgs = (
  "ABSTRACT" => "Efficient genetic algorithms for professional purpose with support for multiprocessing.",
  "AUTHOR" => "\x{141}ukasz Strzelecki <lukasz\@strzeleccy.eu>",
  "CONFIGURE_REQUIRES" => {
    "ExtUtils::MakeMaker" => 0
  },
  "DISTNAME" => "AI-Genetic-Pro",
  "LICENSE" => "lgpl",
  "NAME" => "AI::Genetic::Pro",
  "PREREQ_PM" => {
    "Carp" => 0,
    "Class::Accessor::Fast::XS" => 0,
    "Clone" => 0,
    "Digest::MD5" => 0,
    "Exporter::Lite" => 0,
    "GD::Graph::linespoints" => "1.54",
    "List::MoreUtils" => 0,
    "List::Util" => 0,
    "MCE" => "1.874",
    "MCE::Map" => "1.874",
    "Math::Random" => "0.72",
    "Storable" => "2.05",
    "Struct::Compare" => 0,
    "Tie::Array::Packed" => "0.13",
    "UNIVERSAL::require" => 0
  },
  "VERSION" => "1.009",
  "test" => {
    "TESTS" => "t/*.t"
  }
);


my %FallbackPrereqs = (
  "Carp" => 0,
  "Class::Accessor::Fast::XS" => 0,
  "Clone" => 0,
  "Digest::MD5" => 0,
  "Exporter::Lite" => 0,
  "GD::Graph::linespoints" => "1.54",
  "List::MoreUtils" => 0,
  "List::Util" => 0,
  "MCE" => "1.874",
  "MCE::Map" => "1.874",
  "Math::Random" => "0.72",
  "Storable" => "2.05",
  "Struct::Compare" => 0,
  "Tie::Array::Packed" => "0.13",
  "UNIVERSAL::require" => 0
);


unless ( eval { ExtUtils::MakeMaker->VERSION(6.63_03) } ) {
  delete $WriteMakefileArgs{TEST_REQUIRES};
  delete $WriteMakefileArgs{BUILD_REQUIRES};
  $WriteMakefileArgs{PREREQ_PM} = \%FallbackPrereqs;
}

delete $WriteMakefileArgs{CONFIGURE_REQUIRES}
  unless eval { ExtUtils::MakeMaker->VERSION(6.52) };

WriteMakefile(%WriteMakefileArgs);

README  view on Meta::CPAN

NAME

    AI::Genetic::Pro - Efficient genetic algorithms for professional
    purpose with support for multiprocessing.

SYNOPSIS

        use AI::Genetic::Pro;
        
        sub fitness {
            my ($ga, $chromosome) = @_;
            return oct('0b' . $ga->as_string($chromosome)); 
        }
        
        sub terminate {
            my ($ga) = @_;
            my $result = oct('0b' . $ga->as_string($ga->getFittest));
            return $result == 4294967295 ? 1 : 0;
        }
        
        my $ga = AI::Genetic::Pro->new(        
            -fitness         => \&fitness,        # fitness function
            -terminate       => \&terminate,      # terminate function
            -type            => 'bitvector',      # type of chromosomes
            -population      => 1000,             # population
            -crossover       => 0.9,              # probab. of crossover
            -mutation        => 0.01,             # probab. of mutation
            -parents         => 2,                # number  of parents
            -selection       => [ 'Roulette' ],   # selection strategy
            -strategy        => [ 'Points', 2 ],  # crossover strategy
            -cache           => 0,                # cache results
            -history         => 1,                # remember best results
            -preserve        => 3,                # remember the bests
            -variable_length => 1,                # turn variable length ON
            -mce             => 1,                # optional MCE support
            -workers         => 3,                # number of workers (MCE)
        );
            
        # init population of 32-bit vectors
        $ga->init(32);
            
        # evolve 10 generations
        $ga->evolve(10);
        
        # best score
        print "SCORE: ", $ga->as_value($ga->getFittest), ".\n";
        
        # save evolution path as a chart
        $ga->chart(-filename => 'evolution.png');
         
        # save state of GA
        $ga->save('genetic.sga');
        
        # load state of GA
        $ga->load('genetic.sga');

DESCRIPTION

    This module provides efficient implementation of a genetic algorithm
    for professional purpose with support for multiprocessing. It was
    designed to operate as fast as possible even on very large populations
    and big individuals/chromosomes. AI::Genetic::Pro was inspired by
    AI::Genetic, so it is in most cases compatible (there are some
    changes). Additionally AI::Genetic::Pro isn't a pure Perl solution, so
    it doesn't have limitations of its ancestor (such as slow-down in the
    case of big populations ( >10000 ) or vectors with more than 33
    fields).

    If You are looking for a pure Perl solution, consider AI::Genetic.

    Speed

      To increase speed XS code is used, however with portability in mind.
      This distribution was tested on Windows and Linux platforms (and
      should work on any other).

      Multicore support is available through Many-Core Engine (MCE). You
      can gain the most speed up for big populations or time/CPU consuming
      fitness functions, however for small populations and/or simple
      fitness function better choice will be single-process version.

      You can get even more speed up if you turn on use of native arrays
      (parameter: native) instead of packing chromosomes into single
      scalar. However you have to remember about expensive memory use in
      that case.

    Memory

      This module was designed to use as little memory as possible. A
      population of size 10000 consisting of 92-bit vectors uses only ~24MB
      (AI::Genetic would use about 78MB). However - if you use MCE - there
      will be bigger memory consumption. This is consequence of necessity
      of synchronization between many processes.

    Advanced options

      To provide more flexibility AI::Genetic::Pro supports many
      statistical distributions, such as uniform, natural, chi_square and
      others. This feature can be used in selection and/or crossover. See
      the documentation below.

METHODS

    $ga->new( %options )

      Constructor. It accepts options in hash-value style. See options and
      an example below.

      -fitness

	This defines a fitness function. It expects a reference to a
	subroutine.

      -terminate

	This defines a terminate function. It expects a reference to a
	subroutine.

      -type

	This defines the type of chromosomes. Currently, AI::Genetic::Pro
	supports four types:

	bitvector

	  Individuals/chromosomes of this type have genes that are bits.
	  Each gene can be in one of two possible states, on or off.

	listvector

	  Each gene of a "listvector" individual/chromosome can assume one
	  string value from a specified list of possible string values.

	rangevector

	  Each gene of a "rangevector" individual/chromosome can assume one
	  integer value from a range of possible integer values. Note that
	  only integers are supported. The user can always transform any
	  desired fractional values by multiplying and dividing by an
	  appropriate power of 10.

	combination

	  Each gene of a "combination" individual/chromosome can assume one
	  string value from a specified list of possible string values. All
	  genes are unique.

      -population

	This defines the size of the population, i.e. how many chromosomes
	simultaneously exist at each generation.

      -crossover

	This defines the crossover rate. The fairest results are achieved
	with crossover rate ~0.95.

      -mutation

	This defines the mutation rate. The fairest results are achieved
	with mutation rate ~0.01.

      -preserve

	This defines injection of the bests chromosomes into a next
	generation. It causes a little slow down, however (very often) much
	better results are achieved. You can specify, how many chromosomes
	will be preserved, i.e.

            -preserve => 1, # only one chromosome will be preserved
            # or
            -preserve => 9, # 9 chromosomes will be preserved
            # and so on...

	Attention! You cannot preserve more chromosomes than exist in your
	population.

      -variable_length

	This defines whether variable-length chromosomes are turned on
	(default off) and a which types of mutation are allowed. See below.

	level 0

	  Feature is inactive (default). Example:

                  -variable_length => 0
                  
              # chromosomes (i.e. bitvectors)
              0 1 0 0 1 1 0 1 1 1 0 1 0 1
              0 0 1 1 0 1 1 1 1 0 0 1 1 0
              0 1 1 1 0 1 0 0 1 1 0 1 1 1
              0 1 0 0 1 1 0 1 1 1 1 0 1 0
              # ...and so on

	level 1

	  Feature is active, but chromosomes can varies only on the right
	  side, Example:

                  -variable_length => 1
                  
              # chromosomes (i.e. bitvectors)
              0 1 0 0 1 1 0 1 1 1 
              0 0 1 1 0 1 1 1 1
              0 1 1 1 0 1 0 0 1 1 0 1 1 1
              0 1 0 0 1 1 0 1 1 1
              # ...and so on
                  

	level 2

	  Feature is active and chromosomes can varies on the left side and
	  on the right side; unwanted values/genes on the left side are
	  replaced with undef, ie.

                  -variable_length => 2
           
              # chromosomes (i.e. bitvectors)
              x x x 0 1 1 0 1 1 1 
              x x x x 0 1 1 1 1
              x 1 1 1 0 1 0 0 1 1 0 1 1 1
              0 1 0 0 1 1 0 1 1 1
              # where 'x' means 'undef'
              # ...and so on

	  In this situation returned chromosomes in an array context
	  ($ga->as_array($chromosome)) can have undef values on the left
	  side (only). In a scalar context each undefined value is replaced
	  with a single space. If You don't want to see any undef or space,
	  just use as_array_def_only and as_string_def_only instead of
	  as_array and as_string.

      -parents

	This defines how many parents should be used in a crossover.

      -selection

	This defines how individuals/chromosomes are selected to crossover.
	It expects an array reference listed below:

            -selection => [ $type, @params ]

	where type is one of:

	RouletteBasic

	  Each individual/chromosome can be selected with probability
	  proportional to its fitness.

	Roulette

	  First the best individuals/chromosomes are selected. From this
	  collection parents are selected with probability poportional to
	  their fitness.

	RouletteDistribution

	  Each individual/chromosome has a portion of roulette wheel
	  proportional to its fitness. Selection is done with the specified
	  distribution. Supported distributions and parameters are listed
	  below.

	  -selection => [ 'RouletteDistribution', 'uniform' ]

	    Standard uniform distribution. No additional parameters are
	    needed.

	  -selection => [ 'RouletteDistribution', 'normal', $av, $sd ]

	    Normal distribution, where $av is average (default: size of
	    population /2) and $$sd is standard deviation (default: size of
	    population).

	  -selection => [ 'RouletteDistribution', 'beta', $aa, $bb ]

	    Beta distribution. The density of the beta is:

                X^($aa - 1) * (1 - X)^($bb - 1) / B($aa , $bb) for 0 < X < 1.

	    $aa and $bb are set by default to number of parents.

	    Argument restrictions: Both $aa and $bb must not be less than
	    1.0E-37.

	  -selection => [ 'RouletteDistribution', 'binomial' ]

	    Binomial distribution. No additional parameters are needed.

	  -selection => [ 'RouletteDistribution', 'chi_square', $df ]

	    Chi-square distribution with $df degrees of freedom. $df by
	    default is set to size of population.

	  -selection => [ 'RouletteDistribution', 'exponential', $av ]

	    Exponential distribution, where $av is average . $av by default
	    is set to size of population.

	  -selection => [ 'RouletteDistribution', 'poisson', $mu ]

	    Poisson distribution, where $mu is mean. $mu by default is set
	    to size of population.

	Distribution

	  Chromosomes/individuals are selected with specified distribution.
	  See below.

	  -selection => [ 'Distribution', 'uniform' ]

	    Standard uniform distribution. No additional parameters are
	    needed.

	  -selection => [ 'Distribution', 'normal', $av, $sd ]

	    Normal distribution, where $av is average (default: size of
	    population /2) and $$sd is standard deviation (default: size of
	    population).

	  -selection => [ 'Distribution', 'beta', $aa, $bb ]

	    Beta distribution. The density of the beta is:

                X^($aa - 1) * (1 - X)^($bb - 1) / B($aa , $bb) for 0 < X < 1.

	    $aa and $bb are set by default to number of parents.

	    Argument restrictions: Both $aa and $bb must not be less than
	    1.0E-37.

	  -selection => [ 'Distribution', 'binomial' ]

	    Binomial distribution. No additional parameters are needed.

	  -selection => [ 'Distribution', 'chi_square', $df ]

	    Chi-square distribution with $df degrees of freedom. $df by
	    default is set to size of population.

	  -selection => [ 'Distribution', 'exponential', $av ]

	    Exponential distribution, where $av is average . $av by default
	    is set to size of population.

	  -selection => [ 'Distribution', 'poisson', $mu ]

	    Poisson distribution, where $mu is mean. $mu by default is set
	    to size of population.

      -strategy

	This defines the astrategy of crossover operation. It expects an
	array reference listed below:

            -strategy => [ $type, @params ]

	where type is one of:

	PointsSimple

	  Simple crossover in one or many points. The best
	  chromosomes/individuals are selected for the new generation. For
	  example:

              -strategy => [ 'PointsSimple', $n ]

	  where $n is the number of points for crossing.

	PointsBasic

	  Crossover in one or many points. In basic crossover selected
	  parents are crossed and one (randomly-chosen) child is moved to
	  the new generation. For example:

              -strategy => [ 'PointsBasic', $n ]

	  where $n is the number of points for crossing.

	Points

	  Crossover in one or many points. In normal crossover selected
	  parents are crossed and the best child is moved to the new
	  generation. For example:

              -strategy => [ 'Points', $n ]

	  where $n is number of points for crossing.

	PointsAdvenced

	  Crossover in one or many points. After crossover the best
	  chromosomes/individuals from all parents and chidren are selected
	  for the new generation. For example:

              -strategy => [ 'PointsAdvanced', $n ]

	  where $n is the number of points for crossing.

	Distribution

	  In distribution crossover parents are crossed in points selected
	  with the specified distribution. See below.

	  -strategy => [ 'Distribution', 'uniform' ]

	    Standard uniform distribution. No additional parameters are
	    needed.

	  -strategy => [ 'Distribution', 'normal', $av, $sd ]

	    Normal distribution, where $av is average (default: number of
	    parents/2) and $sd is standard deviation (default: number of
	    parents).

	  -strategy => [ 'Distribution', 'beta', $aa, $bb ]

	    Beta distribution. The density of the beta is:

                X^($aa - 1) * (1 - X)^($bb - 1) / B($aa , $bb) for 0 < X < 1.

	    $aa and $bb are set by default to the number of parents.

	    Argument restrictions: Both $aa and $bb must not be less than
	    1.0E-37.

	  -strategy => [ 'Distribution', 'binomial' ]

	    Binomial distribution. No additional parameters are needed.

	  -strategy => [ 'Distribution', 'chi_square', $df ]

	    Chi-squared distribution with $df degrees of freedom. $df by
	    default is set to the number of parents.

	  -strategy => [ 'Distribution', 'exponential', $av ]

	    Exponential distribution, where $av is average . $av by default
	    is set to the number of parents.

	  -strategy => [ 'Distribution', 'poisson', $mu ]

	    Poisson distribution, where $mu is mean. $mu by default is set
	    to the number of parents.

	PMX

	  PMX method defined by Goldberg and Lingle in 1985. Parameters:
	  none.

	OX

	  OX method defined by Davis (?) in 1985. Parameters: none.

      -cache

	This defines whether a cache should be used. Allowed values are 1
	or 0 (default: 0).

      -history

	This defines whether history should be collected. Allowed values
	are 1 or 0 (default: 0).

      -native

	This defines whether native arrays should be used instead of
	packing each chromosome into signle scalar. Turning this option can
	give you speed up, but much more memory will be used. Allowed
	values are 1 or 0 (default: 0).

      -mce

	This defines whether Many-Core Engine (MCE) should be used during
	processing. This can give you significant speed up on many-core/CPU
	systems, but it'll increase memory consumption. Allowed values are
	1 or 0 (default: 0).

      -workers

	This option has any meaning only if MCE is turned on. This defines
	how many process will be used during processing. Default will be
	used one proces per core (most efficient).

      -strict

	This defines if the check for modifying chromosomes in a
	user-defined fitness function is active. Directly modifying
	chromosomes is not allowed and it is a highway to big trouble. This
	mode should be used only for testing, because it is slow.

    $ga->inject($chromosomes)

      Inject new, user defined, chromosomes into the current population.
      See example below:

          # example for bitvector
          my $chromosomes = [
              [ 1, 1, 0, 1, 0, 1 ],
              [ 0, 0, 0, 1, 0, 1 ],
              [ 0, 1, 0, 1, 0, 0 ],
              ...
          ];
          
          # inject
          $ga->inject($chromosomes);

      If You want to delete some chromosomes from population, just splice
      them:

          my @remove = qw(1 2 3 9 12);
              for my $idx (sort { $b <=> $a }  @remove){
              splice @{$ga->chromosomes}, $idx, 1;
          }

    $ga->population($population)

      Set/get size of the population. This defines the size of the
      population, i.e. how many chromosomes to simultaneously exist at each
      generation.

    $ga->indType()

      Get type of individuals/chromosomes. Currently supported types are:

      bitvector

	Chromosomes will be just bitvectors. See documentation of new
	method.

      listvector

	Chromosomes will be lists of specified values. See documentation of
	new method.

      rangevector

	Chromosomes will be lists of values from specified range. See
	documentation of new method.

      combination

	Chromosomes will be unique lists of specified values. This is used
	for example in the Traveling Salesman Problem. See the
	documentation of the new method.

      In example:

          my $type = $ga->type();

    $ga->type()

      Alias for indType.

    $ga->crossProb()

      This method is used to query and set the crossover rate.

    $ga->crossover()

      Alias for crossProb.

    $ga->mutProb()

      This method is used to query and set the mutation rate.

    $ga->mutation()

      Alias for mutProb.

    $ga->parents($parents)

      Set/get number of parents in a crossover.

    $ga->init($args)

      This method initializes the population with random
      individuals/chromosomes. It MUST be called before any call to
      evolve(). It expects one argument, which depends on the type of
      individuals/chromosomes:

      bitvector

	For bitvectors, the argument is simply the length of the bitvector.

            $ga->init(10);

	This initializes a population where each individual/chromosome has
	10 genes.

      listvector

	For listvectors, the argument is an anonymous list of lists. The
	number of sub-lists is equal to the number of genes of each
	individual/chromosome. Each sub-list defines the possible string
	values that the corresponding gene can assume.

            $ga->init([
                       [qw/red blue green/],
                       [qw/big medium small/],
                       [qw/very_fat fat fit thin very_thin/],
                      ]);

	This initializes a population where each individual/chromosome has
	3 genes and each gene can assume one of the given values.

      rangevector

	For rangevectors, the argument is an anonymous list of lists. The
	number of sub-lists is equal to the number of genes of each
	individual/chromosome. Each sub-list defines the minimum and
	maximum integer values that the corresponding gene can assume.

            $ga->init([
                       [1, 5],
                       [0, 20],
                       [4, 9],
                      ]);

	This initializes a population where each individual/chromosome has
	3 genes and each gene can assume an integer within the
	corresponding range.

      combination

	For combination, the argument is an anonymous list of possible
	values of gene.

            $ga->init( [ 'a', 'b', 'c' ] );

	This initializes a population where each chromosome has 3 genes and
	each gene is a unique combination of 'a', 'b' and 'c'. For example
	genes looks something like that:

            [ 'a', 'b', 'c' ]    # gene 1
            [ 'c', 'a', 'b' ]    # gene 2
            [ 'b', 'c', 'a' ]    # gene 3
            # ...and so on...

    $ga->evolve($n)

      This method causes the GA to evolve the population for the specified
      number of generations. If its argument is 0 or undef GA will evolve
      the population to infinity unless a terminate function is specified.

    $ga->getHistory()

      Get history of the evolution. It is in a format listed below:

              [
                      # gen0   gen1   gen2   ...          # generations
                      [ max0,  max1,  max2,  ... ],       # max values
                      [ mean,  mean1, mean2, ... ],       # mean values
                      [ min0,  min1,  min2,  ... ],       # min values
              ]

    $ga->getAvgFitness()

      Get max, mean and min score of the current generation. In example:

          my ($max, $mean, $min) = $ga->getAvgFitness();

    $ga->getFittest($n, $unique)

      This function returns a list of the fittest chromosomes from the
      current population. You can specify how many chromosomes should be
      returned and if the returned chromosomes should be unique. See
      example below.

          # only one - the best
          my ($best) = $ga->getFittest;
      
          # or 5 bests chromosomes, NOT unique
          my @bests = $ga->getFittest(5);
      
          # or 7 bests and UNIQUE chromosomes
          my @bests = $ga->getFittest(7, 1);

      If you want to get a large number of chromosomes, try to use the
      getFittest_as_arrayref function instead (for efficiency).

    $ga->getFittest_as_arrayref($n, $unique)

      This function is very similar to getFittest, but it returns a
      reference to an array instead of a list.

    $ga->generation()

      Get the number of the current generation.

    $ga->people()

      Returns an anonymous list of individuals/chromosomes of the current
      population.

      IMPORTANT: the actual array reference used by the AI::Genetic::Pro
      object is returned, so any changes to it will be reflected in $ga.

    $ga->chromosomes()

      Alias for people.

    $ga->chart(%options)

      Generate a chart describing changes of min, mean, and max scores in
      your population. To satisfy your needs, you can pass the following
      options:

      -filename

	File to save a chart in (obligatory).

      -title

	Title of a chart (default: Evolution).

      -x_label

	X label (default: Generations).

      -y_label

	Y label (default: Value).

      -format

	Format of values, like sprintf (default: '%.2f').

      -legend1

	Description of min line (default: Min value).

      -legend2

	Description of min line (default: Mean value).

      -legend3

	Description of min line (default: Max value).

      -width

	Width of a chart (default: 640).

      -height

	Height of a chart (default: 480).

      -font

	Path to font (in *.ttf format) to be used (default: none).

      -logo

	Path to logo (png/jpg image) to embed in a chart (default: none).

      For example:

                $ga->chart(-width => 480, height => 320, -filename => 'chart.png');

    $ga->save($file)

      Save the current state of the genetic algorithm to the specified
      file.

    $ga->load($file)

      Load a state of the genetic algorithm from the specified file.

    $ga->as_array($chromosome)

      In list context return an array representing the specified
      chromosome. In scalar context return an reference to an array
      representing the specified chromosome. If variable_length is turned
      on and is set to level 2, an array can have some undef values. To get
      only not undef values use as_array_def_only instead of as_array.

    $ga->as_array_def_only($chromosome)

      In list context return an array representing the specified
      chromosome. In scalar context return an reference to an array
      representing the specified chromosome. If variable_length is turned
      off, this function is just an alias for as_array. If variable_length
      is turned on and is set to level 2, this function will return only
      not undef values from chromosome. See example below:

          # -variable_length => 2, -type => 'bitvector'
              
          my @chromosome = $ga->as_array($chromosome)
          # @chromosome looks something like that
          # ( undef, undef, undef, 1, 0, 1, 1, 1, 0 )
              
          @chromosome = $ga->as_array_def_only($chromosome)
          # @chromosome looks something like that
          # ( 1, 0, 1, 1, 1, 0 )

    $ga->as_string($chromosome)

      Return a string representation of the specified chromosome. See
      example below:

              # -type => 'bitvector'
              
              my $string = $ga->as_string($chromosome);
              # $string looks something like that
              # 1___0___1___1___1___0 
              
              # or 
              
              # -type => 'listvector'
              
              $string = $ga->as_string($chromosome);
              # $string looks something like that
              # element0___element1___element2___element3...

      Attention! If variable_length is turned on and is set to level 2, it
      is possible to get undef values on the left side of the vector. In
      the returned string undef values will be replaced with spaces. If you
      don't want to see any spaces, use as_string_def_only instead of
      as_string.

    $ga->as_string_def_only($chromosome)

      Return a string representation of specified chromosome. If
      variable_length is turned off, this function is just alias for
      as_string. If variable_length is turned on and is set to level 2,
      this function will return a string without undef values. See example
      below:

              # -variable_length => 2, -type => 'bitvector'
              
              my $string = $ga->as_string($chromosome);
              # $string looks something like that
              #  ___ ___ ___1___1___0 
              
              $string = $ga->as_string_def_only($chromosome);
              # $string looks something like that
              # 1___1___0 

    $ga->as_value($chromosome)

      Return the score of the specified chromosome. The value of chromosome
      is calculated by the fitness function.

SUPPORT

    AI::Genetic::Pro is still under development; however, it is used in
    many production environments.

TODO

    Examples.

    More tests.

    More warnings about incorrect parameters.

REPORTING BUGS

    When reporting bugs/problems please include as much information as
    possible. It may be difficult for me to reproduce the problem as almost
    every setup is different.

    A small script which yields the problem will probably be of help.

THANKS

    Mario Roy for suggestions about efficiency.

    Miles Gould for suggestions and some fixes (even in this documentation!
    :-).

    Alun Jones for fixing memory leaks.

    Tod Hagan for reporting a bug (rangevector values truncated to signed
    8-bit quantities) and supplying a patch.

    Randal L. Schwartz for reporting a bug in this documentation.

    Maciej Misiak for reporting problems with combination (and a bug in a
    PMX strategy).

    LEONID ZAMDBORG for recommending the addition of variable-length
    chromosomes as well as supplying relevant code samples, for testing and
    at the end reporting some bugs.

    Christoph Meissner for reporting a bug.

    Alec Chen for reporting some bugs.

AUTHOR

    Strzelecki Lukasz <lukasz@strzeleccy.eu>

SEE ALSO

    AI::Genetic Algorithm::Evolutionary

COPYRIGHT

    Copyright (c) Strzelecki Lukasz. All rights reserved. This program is
    free software; you can redistribute it and/or modify it under the same
    terms as Perl itself.

lib/AI/Genetic/Pro.pm  view on Meta::CPAN

use List::Util 						qw( sum );
use List::MoreUtils 				qw( minmax first_index apply );
#use Data::Dumper; 					$Data::Dumper::Sortkeys = 1;
use Tie::Array::Packed;
use UNIVERSAL::require;
#-----------------------------------------------------------------------
use AI::Genetic::Pro::Array::Type 	qw( get_package_by_element_size );
use AI::Genetic::Pro::Chromosome;
#-----------------------------------------------------------------------
__PACKAGE__->mk_accessors(qw(
	mce
	type
	population
	terminate
	chromosomes 
	crossover 
	native
	parents 		_parents 
	history 		_history
	fitness 		_fitness 		_fitness_real
	cache
	mutation 		_mutator
	strategy 		_strategist
	selection 		_selector 
	_translations
	generation
	preserve		
	variable_length
	_fix_range
	_package
	_length
	strict			_strict
	workers
	size
	_init
));
#=======================================================================
# Additional modules
use constant STORABLE	=> 'Storable';
use constant GD 		=> 'GD::Graph::linespoints'; 
#=======================================================================
my $_Cache = { };
my $_temp_chromosome;
#=======================================================================
sub new {
	my ( $class, %args ) = ( shift, @_ );
	
	#-------------------------------------------------------------------
	my %opts = map { if(ref $_){$_}else{ /^-?(.*)$/o; $1 }} @_;
	my $self = bless \%opts, $class;
	
	#-------------------------------------------------------------------
	$AI::Genetic::Pro::Array::Type::Native = 1 if $self->native;
	
	#-------------------------------------------------------------------
	croak(q/Type of chromosomes cannot be "combination" if "variable length" feature is active!/)
		if $self->type eq q/combination/ and $self->variable_length;
	croak(q/You must specify a crossover strategy with -strategy!/)
		unless defined ($self->strategy);
	croak(q/Type of chromosomes cannot be "combination" if strategy is not one of: OX, PMX!/)
		if $self->type eq q/combination/ and ($self->strategy->[0] ne q/OX/ and $self->strategy->[0] ne q/PMX/);
	croak(q/Strategy cannot be "/,$self->strategy->[0],q/" if "variable length" feature is active!/ )
		if ($self->strategy->[0] eq 'PMX' or $self->strategy->[0] eq 'OX') and $self->variable_length;
	
	#-------------------------------------------------------------------
	$self->_set_strict if $self->strict;

	#-------------------------------------------------------------------
	return $self unless $self->mce;

	#-------------------------------------------------------------------
	delete $self->{ mce };
	'AI::Genetic::Pro::MCE'->use or die q[Cannot raise multicore support: ] . $@;
	
	return AI::Genetic::Pro::MCE->new( $self, \%args );
}
#=======================================================================
sub _Cache { $_Cache; }
#=======================================================================
# INIT #################################################################
#=======================================================================
sub _set_strict {
	my ($self) = @_;
	
	# fitness
	my $fitness = $self->fitness();
	my $replacement = sub {
		my @tmp = @{$_[1]};
		my $ret = $fitness->(@_);
		my @cmp = @{$_[1]};
		die qq/Chromosome was modified in a fitness function from "@tmp" to "@{$_[1]}"!\n/ unless compare(\@tmp, \@cmp);
		return $ret;
	};
	$self->fitness($replacement);
}
#=======================================================================
sub _fitness_cached {
	my ($self, $chromosome) = @_;
	
	#my $key = md5_hex(${tied(@$chromosome)});
	my $key = md5_hex( $self->_package ? md5_hex( ${ tied( @$chromosome ) } ) : join( q[:], @$chromosome ) );
	return $_Cache->{$key} if exists $_Cache->{$key};
	
	$_Cache->{$key} = $self->_fitness_real->($self, $chromosome);
	return $_Cache->{$key};
}
#=======================================================================
sub _init_cache {
	my ($self) = @_;
		
	$self->_fitness_real($self->fitness);
	$self->fitness(\&_fitness_cached);
	return;
}
#=======================================================================
sub _check_data_ref {
	my ($self, $data_org) = @_;
	my $data = clone($data_org);
	my $ars;
	for(0..$#$data){
		next if $ars->{$data->[$_]};
		$ars->{$data->[$_]} = 1;
		unshift @{$data->[$_]}, undef;
	}
	return $data;
}
#=======================================================================
# we have to find C to (in some cases) incrase value of range
# due to design model
sub _find_fix_range {
	my ($self, $data) = @_;

	for my $idx (0..$#$data){
		if($data->[$idx]->[1] < 1){ 
			my $const = 1 - $data->[$idx]->[1];
			push @{$self->_fix_range}, $const; 
			$data->[$idx]->[1] += $const;
			$data->[$idx]->[2] += $const;
		}else{ push @{$self->_fix_range}, 0; }
	}

	return $data;
}
#=======================================================================
sub init { 
	my ( $self, $data ) = @_;
	
	croak q/You have to pass some data to "init"!/ unless $data;
	#-------------------------------------------------------------------
	$self->generation(0);
	$self->_init( $data );
	$self->_fitness( { } );
	$self->_fix_range( [ ] );
	$self->_history( [  [ ], [ ], [ ] ] );
	$self->_init_cache if $self->cache;
	#-------------------------------------------------------------------
	
	if($self->type eq q/listvector/){
		croak(q/You have to pass array reference if "type" is set to "listvector"/) unless ref $data eq 'ARRAY';
		$self->_translations( $self->_check_data_ref($data) );
	}elsif($self->type eq q/bitvector/){
		croak(q/You have to pass integer if "type" is set to "bitvector"/) if $data !~ /^\d+$/o;
		$self->_translations( [ [ 0, 1 ] ] );
		$self->_translations->[$_] = $self->_translations->[0] for 1..$data-1;
	}elsif($self->type eq q/combination/){
		croak(q/You have to pass array reference if "type" is set to "combination"/) unless ref $data eq 'ARRAY';
		$self->_translations( [ clone($data) ] );
		$self->_translations->[$_] = $self->_translations->[0] for 1..$#$data;
	}elsif($self->type eq q/rangevector/){
		croak(q/You have to pass array reference if "type" is set to "rangevector"/) unless ref $data eq 'ARRAY';
		$self->_translations( $self->_find_fix_range( $self->_check_data_ref($data) ));
	}else{
		croak(q/You have to specify first "type" of vector!/);
	}
	
	my $size = 0;

	if($self->type ne q/rangevector/){ for(@{$self->_translations}){ $size = $#$_ if $#$_ > $size; } }
#	else{ for(@{$self->_translations}){ $size = $_->[1] if $_->[1] > $size; } }
	else{ for(@{$self->_translations}){ $size = $_->[2] if $_->[2] > $size; } }		# Provisional patch for rangevector values truncated to signed  8-bit quantities. Thx to Tod Hagan

	my $package = get_package_by_element_size($size);
	$self->_package($package);

	my $length = ref $data ? sub { $#$data; } : sub { $data - 1 };
	if($self->variable_length){
		$length = ref $data ? sub { 1 + int( rand( $#{ $self->_init } ) ); } : sub { 1 + int( rand( $self->_init - 1) ); };
	}

	$self->_length( $length );

	$self->chromosomes( [ ] );
	push @{$self->chromosomes}, 
		AI::Genetic::Pro::Chromosome->new($self->_translations, $self->type, $package, $length->())
			for 1..$self->population;
	
	$self->_calculate_fitness_all();
}
#=======================================================================
# SAVE / LOAD ##########################################################
#=======================================================================
sub spew {
	#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	STORABLE->use( qw( store retrieve freeze thaw ) ) or croak(q/You need "/.STORABLE.q/" module to save a state of "/.__PACKAGE__.q/"!/);
	$Storable::Deparse = 1;
	$Storable::Eval = 1;
	#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	my ( $self ) = @_;
 	
	my $clone = { 
		_selector	=> undef,
		_strategist	=> undef,
		_mutator	=> undef,
	};
	
	$clone->{ chromosomes } = [ map { ${ tied( @$_ ) } } @{ $self->chromosomes } ] 
		if $self->_package;
	
	foreach my $key(keys %$self){
		next if exists $clone->{$key};
		$clone->{$key} = $self->{$key};
	}
	
	return $clone;
}
#=======================================================================
sub slurp {
	my ( $self, $dump ) = @_;

	if( my $typ = $self->_package ){ 
		@{ $dump->{ chromosomes } } = map {
			my $arr = $typ->make_with_packed( $_ );
			bless $arr, q[AI::Genetic::Pro::Chromosome];
		} @{ $dump->{ chromosomes } };
	}
    
    %$self = %$dump;
    
	return 1;
}
#=======================================================================
sub save { 
	my ( $self, $file ) = @_;
	
	croak(q/You have to specify file!/) unless defined $file;
	
	store( $self->spew, $file );
}
#=======================================================================
sub load { 
	#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	STORABLE->use( qw( store retrieve freeze thaw ) ) or croak(q/You need "/.STORABLE.q/" module to load a state of "/.__PACKAGE__.q/"!/);	
	$Storable::Deparse = 1;
	$Storable::Eval = 1;
	#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	my ($self, $file) = @_;
	croak(q/You have to specify file!/) unless defined $file;

	my $clone = retrieve($file);
	return carp('Incorrect file!') unless $clone;
	
	return $self->slurp( $clone );
}
#=======================================================================
# CHARTS ###############################################################
#=======================================================================
sub chart { 
	GD->require or croak(q/You need "/.GD.q/" module to draw chart of evolution!/);	
	my ($self, %params) = (shift, @_);

	my $graph = GD()->new(($params{-width} || 640), ($params{-height} || 480));

	my $data = $self->getHistory;

	if(defined $params{-font}){
    	$graph->set_title_font  ($params{-font}, 12);
    	$graph->set_x_label_font($params{-font}, 10);
    	$graph->set_y_label_font($params{-font}, 10);
    	$graph->set_legend_font ($params{-font},  8);
	}
	
    $graph->set_legend(
    	$params{legend1} || q/Max value/,
    	$params{legend2} || q/Mean value/,
    	$params{legend3} || q/Min value/,
    );

    $graph->set(
        x_label_skip        => int(($data->[0]->[-1]*4)/100),
        x_labels_vertical   => 1,
        x_label_position    => .5,
        y_label_position    => .5,
        y_long_ticks        => 1,   # poziome linie
        x_ticks             => 1,   # poziome linie

        l_margin            => 10,
        b_margin            => 10,
        r_margin            => 10,
        t_margin            => 10,

        show_values         => (defined $params{-show_values} ? 1 : 0),
        values_vertical     => 1,
        values_format       => ($params{-format} || '%.2f'),

        zero_axis           => 1,
        #interlaced          => 1,
        logo_position       => 'BR',
        legend_placement    => 'RT',

        bgclr               => 'white',
        boxclr              => '#FFFFAA',
        transparent         => 0,

        title       		=> ($params{'-title'}   || q/Evolution/ ),
        x_label     		=> ($params{'-x_label'} || q/Generation/),
        y_label     		=> ($params{'-y_label'} || q/Value/     ),
        
        ( $params{-logo} && -f $params{-logo} ? ( logo => $params{-logo} ) : ( ) )
    );
	
	
    my $gd = $graph->plot( [ [ 0..$#{$data->[0]} ], @$data ] ) or croak($@);
    open(my $fh, '>', $params{-filename}) or croak($@);
    binmode $fh;
    print $fh $gd->png;
    close $fh;
    
    return 1;
}
#=======================================================================
# TRANSLATIONS #########################################################
#=======================================================================
sub as_array_def_only {
	my ($self, $chromosome) = @_;
	
	return $self->as_array($chromosome) 
		if not $self->variable_length or $self->variable_length < 2;
	
	if( $self->type eq q/bitvector/ ){
		return $self->as_array($chromosome);
	}else{
		my $ar = $self->as_array($chromosome);
		my $idx = first_index { $_ } @$ar;
		my @array = @$ar[$idx..$#$chromosome];
		return @array if wantarray;
		return \@array;
	}
}
#=======================================================================
sub as_array {
	my ($self, $chromosome) = @_;
	
	if($self->type eq q/bitvector/){
		# This could lead to internal error, bacause of underlaying Tie::Array::Packed
		#return @$chromosome if wantarray;
		#return $chromosome;
		
		my @chr = @$chromosome;
		return @chr if wantarray;
		return \@chr;
		
	}elsif($self->type eq q/rangevector/){
		my $fix_range = $self->_fix_range;
		my $c = -1;
		#my @array = map { $c++; warn "WARN: $c | ",scalar @$chromosome,"\n" if not defined $fix_range->[$c]; $_ ? $_ - $fix_range->[$c] : undef } @$chromosome;
		my @array = map { $c++; $_ ? $_ - $fix_range->[$c] : undef } @$chromosome;
		
		return @array if wantarray;
		return \@array;
	}else{
		my $cnt = 0;
		my @array = map { $self->_translations->[$cnt++]->[$_] } @$chromosome;
		return @array if wantarray;
		return \@array;
	}
}
#=======================================================================
sub as_string_def_only {	
	my ($self, $chromosome) = @_;
	
	return $self->as_string($chromosome) 
		if not $self->variable_length or $self->variable_length < 2;

	my $array = $self->as_array_def_only($chromosome);
	
	return join(q//, @$array) if $self->type eq q/bitvector/;
	return join(q/___/, @$array);
}
#=======================================================================
sub as_string {	
	return join(q//, @{$_[1]}) if $_[0]->type eq q/bitvector/;
	return 	join(q/___/, map { defined $_ ? $_ : q/ / } $_[0]->as_array($_[1]));
}
#=======================================================================
sub as_value { 
	my ($self, $chromosome) = @_;
	croak(q/You MUST call 'as_value' as method of 'AI::Genetic::Pro' object./)
		unless defined $_[0] and ref $_[0] and ( ref $_[0] eq 'AI::Genetic::Pro' or ref $_[0] eq 'AI::Genetic::Pro::MCE');
	croak(q/You MUST pass 'AI::Genetic::Pro::Chromosome' object to 'as_value' method./) 
		unless defined $_[1] and ref $_[1] and ref $_[1] eq 'AI::Genetic::Pro::Chromosome';
	return $self->fitness->($self, $chromosome);  
}
#=======================================================================
# ALGORITHM ############################################################
#=======================================================================
sub _calculate_fitness_all {
	my ($self) = @_;
	
	$self->_fitness( { } );
	$self->_fitness->{$_} = $self->fitness()->($self, $self->chromosomes->[$_]) 
		for 0..$#{$self->chromosomes};

# sorting the population is not necessary	
#	my (@chromosomes, %fitness);
#	for my $idx (sort { $self->_fitness->{$a} <=> $self->_fitness->{$b} } keys %{$self->_fitness}){
#		push @chromosomes, $self->chromosomes->[$idx];
#		$fitness{$#chromosomes} = $self->_fitness->{$idx};
#		delete $self->_fitness->{$idx};
#		delete $self->chromosomes->[$idx];
#	}
#	
#	$self->_fitness(\%fitness);
#	$self->chromosomes(\@chromosomes);

	return;
}
#=======================================================================
sub _select_parents {
	my ($self) = @_;
	unless($self->_selector){
		croak "You must specify a selection strategy!"
			unless defined $self->selection;
		my @tmp = @{$self->selection};
		my $selector = q/AI::Genetic::Pro::Selection::/ . shift @tmp;
		$selector->require or die $!;
		$self->_selector($selector->new(@tmp));
	}
	
	$self->_parents($self->_selector->run($self));
	
	return;
}
#=======================================================================
sub _crossover {
	my ($self) = @_;
	
	unless($self->_strategist){
		my @tmp = @{$self->strategy};
		my $strategist = q/AI::Genetic::Pro::Crossover::/ . shift @tmp;
		$strategist->require or die $!;
		$self->_strategist($strategist->new(@tmp));
	}

	my $a = $self->_strategist->run($self);
	$self->chromosomes( $a );
	
	return;
}
#=======================================================================
sub _mutation {
	my ($self) = @_;
	
	unless($self->_mutator){
		my $mutator = q/AI::Genetic::Pro::Mutation::/ . ucfirst(lc($self->type));
		unless($mutator->require){
			$mutator = q/AI::Genetic::Pro::Mutation::Listvector/;
			$mutator->require;
		}
		$self->_mutator($mutator->new);
	}
	
	return $self->_mutator->run($self);
}
#=======================================================================
sub _save_history {
	my @tmp;
	if($_[0]->history){ @tmp = $_[0]->getAvgFitness; }
	else { @tmp = (undef, undef, undef); }
	
	push @{$_[0]->_history->[0]}, $tmp[0]; 
	push @{$_[0]->_history->[1]}, $tmp[1];
	push @{$_[0]->_history->[2]}, $tmp[2];
	return 1;
}
#=======================================================================
sub inject {
	my ($self, $candidates) = @_;
	
	for(@$candidates){
		push @{$self->chromosomes}, 
			AI::Genetic::Pro::Chromosome->new_from_data($self->_translations, $self->type, $self->_package, $_, $self->_fix_range);
		$self->_fitness->{$#{$self->chromosomes}} = $self->fitness()->($self, $self->chromosomes->[-1]);

	}			
	$self->_strict( [ ] );
	$self->population( $self->population + scalar( @$candidates ) );

	return 1;
}
#=======================================================================
sub _state {
	my ( $self ) = @_;
	
	my @res;
	
	if( $self->_package ){
		@res = map { 
			[
				${ tied( @{ $self->chromosomes->[ $_ ] } ) },
				$self->_fitness->{ $_ },
			]
		} 0 .. $self->population - 1
	}else{
		@res = map { 
			[
				$self->chromosomes->[ $_ ],
				$self->_fitness->{ $_ },
			]
		} 0 .. $self->population - 1
	}
	
	return \@res;
}
#=======================================================================
sub evolve {
	my ($self, $generations) = @_;

	# generations must be defined
	$generations ||= -1; 	 
	
	if($self->strict and $self->_strict){
		for my $idx (0..$#{$self->chromosomes}){
			croak(q/Chromosomes was modified outside the 'evolve' function!/) unless $self->chromosomes->[$idx] and $self->_strict->[$idx];
			my @tmp0 = @{$self->chromosomes->[$idx]};
			my @tmp1 = @{$self->_strict->[$idx]};
			croak(qq/Chromosome was modified outside the 'evolve' function from "@tmp0" to "@tmp1"!/) unless compare(\@tmp0, \@tmp1);
		}
	}
	
	# split into two loops just for speed
	unless($self->preserve){
		for(my $i = 0; $i != $generations; $i++){
			# terminate ----------------------------------------------------
			last if $self->terminate and $self->terminate->($self);
			# update generation --------------------------------------------
			$self->generation($self->generation + 1);
			# update history -----------------------------------------------
			$self->_save_history;
			# selection ----------------------------------------------------
			$self->_select_parents();
			# crossover ----------------------------------------------------
			$self->_crossover();
			# mutation -----------------------------------------------------
			$self->_mutation();
		}
	}else{
		croak('You cannot preserve more chromosomes than is in population!') if $self->preserve > $self->population;
		my @preserved;
		for(my $i = 0; $i != $generations; $i++){
			# terminate ----------------------------------------------------
			last if $self->terminate and $self->terminate->($self);
			# update generation --------------------------------------------
			$self->generation($self->generation + 1);
			# update history -----------------------------------------------
			$self->_save_history;
			#---------------------------------------------------------------
			# preservation of N unique chromosomes
			@preserved = map { clone($_) } @{ $self->getFittest_as_arrayref($self->preserve - 1, 1) };
			# selection ----------------------------------------------------
			$self->_select_parents();
			# crossover ----------------------------------------------------
			$self->_crossover();
			# mutation -----------------------------------------------------
			$self->_mutation();
			#---------------------------------------------------------------
			for(@preserved){
				my $idx = int rand @{$self->chromosomes};
				$self->chromosomes->[$idx] = $_;
				$self->_fitness->{$idx} = $self->fitness()->($self, $_);
			}
		}
	}
	
	if($self->strict){
		$self->_strict( [ ] );
		push @{$self->_strict}, $_->clone for @{$self->chromosomes};
	}
}
#=======================================================================
# ALIASES ##############################################################
#=======================================================================
sub people { $_[0]->chromosomes() }
#=======================================================================
sub getHistory { $_[0]->_history()  }
#=======================================================================
sub mutProb { shift->mutation(@_) }
#=======================================================================
sub crossProb { shift->crossover(@_) }
#=======================================================================
sub intType { shift->type() }
#=======================================================================
# STATS ################################################################
#=======================================================================
sub getFittest_as_arrayref { 
	my ($self, $n, $uniq) = @_;
	$n ||= 1;
	
	$self->_calculate_fitness_all() unless scalar %{ $self->_fitness };
	my @keys = sort { $self->_fitness->{$a} <=> $self->_fitness->{$b} } 0..$#{$self->chromosomes};
	
	if($uniq){
		my %grep;
		my $chromosomes = $self->chromosomes;
		if( my $pkg = $self->_package ){
			my %tmp;
			@keys = grep { 
				my $key = ${ tied( @{ $chromosomes->[ $_ ] } ) };
				#my $key = md5_hex( ${ tied( @{ $chromosomes->[ $_ ] } ) } ); # ?
				$tmp{ $key } && 0 or $tmp{ $key } = 1;
			} @keys;
			#@keys = grep { 
			#		my $add_to_list = 0;
			#		my $key = md5_hex(${tied(@{$chromosomes->[$_]})});
			#		unless($grep{$key}) { 
			#			$grep{$key} = 1; 
			#			$add_to_list = 1;
			#		}
			#		$add_to_list;
			#	} @keys;
		}else{
			my %tmp;
			@keys = grep { 
				my $key = md5_hex( join( q[:], @{ $chromosomes->[ $_ ] } ) );
				$tmp{ $key } && 0 or $tmp{ $key } = 1;
			} @keys;
		}
	}
	
	$n = scalar @keys if $n > scalar @keys;
	return [ reverse @{$self->chromosomes}[ splice @keys, $#keys - $n + 1, $n ] ];
}
#=======================================================================
sub getFittest { return wantarray ? @{ shift->getFittest_as_arrayref(@_) } : shift @{ shift->getFittest_as_arrayref(@_) }; }
#=======================================================================
sub getAvgFitness {
	my ($self) = @_;
	
	my @minmax = minmax values %{$self->_fitness};
	my $mean = sum(values %{$self->_fitness}) / scalar values %{$self->_fitness};
	return $minmax[1], int($mean), $minmax[0];
}
#=======================================================================
1;


__END__

=head1 NAME

AI::Genetic::Pro - Efficient genetic algorithms for professional purpose with support for multiprocessing.

=head1 SYNOPSIS

    use AI::Genetic::Pro;
    
    sub fitness {
        my ($ga, $chromosome) = @_;
        return oct('0b' . $ga->as_string($chromosome)); 
    }
    
    sub terminate {
        my ($ga) = @_;
        my $result = oct('0b' . $ga->as_string($ga->getFittest));
        return $result == 4294967295 ? 1 : 0;
    }
    
    my $ga = AI::Genetic::Pro->new(        
        -fitness         => \&fitness,        # fitness function
        -terminate       => \&terminate,      # terminate function
        -type            => 'bitvector',      # type of chromosomes
        -population      => 1000,             # population
        -crossover       => 0.9,              # probab. of crossover
        -mutation        => 0.01,             # probab. of mutation
        -parents         => 2,                # number  of parents
        -selection       => [ 'Roulette' ],   # selection strategy
        -strategy        => [ 'Points', 2 ],  # crossover strategy
        -cache           => 0,                # cache results
        -history         => 1,                # remember best results
        -preserve        => 3,                # remember the bests
        -variable_length => 1,                # turn variable length ON
        -mce             => 1,                # optional MCE support
        -workers         => 3,                # number of workers (MCE)
    );
	
    # init population of 32-bit vectors
    $ga->init(32);
	
    # evolve 10 generations
    $ga->evolve(10);
    
    # best score
    print "SCORE: ", $ga->as_value($ga->getFittest), ".\n";
    
    # save evolution path as a chart
    $ga->chart(-filename => 'evolution.png');
     
    # save state of GA
    $ga->save('genetic.sga');
    
    # load state of GA
    $ga->load('genetic.sga');

=head1 DESCRIPTION

This module provides efficient implementation of a genetic algorithm for
professional purpose with support for multiprocessing. It was designed to operate as fast as possible
even on very large populations and big individuals/chromosomes. C<AI::Genetic::Pro> 
was inspired by C<AI::Genetic>, so it is in most cases compatible 
(there are some changes). Additionally C<AI::Genetic::Pro> isn't a pure Perl solution, so it 
doesn't have limitations of its ancestor (such as slow-down in the
case of big populations ( >10000 ) or vectors with more than 33 fields).

lib/AI/Genetic/Pro.pm  view on Meta::CPAN


=item -mutation 

This defines the mutation rate. The fairest results are achieved with mutation
rate ~0.01.

=item -preserve

This defines injection of the bests chromosomes into a next generation. It causes a little slow down, however (very often) much better results are achieved. You can specify, how many chromosomes will be preserved, i.e.

    -preserve => 1, # only one chromosome will be preserved
    # or
    -preserve => 9, # 9 chromosomes will be preserved
    # and so on...

Attention! You cannot preserve more chromosomes than exist in your population.

=item -variable_length

This defines whether variable-length chromosomes are turned on (default off)
and a which types of mutation are allowed. See below.

=over 8

=item level 0

Feature is inactive (default). Example:

	-variable_length => 0
	
    # chromosomes (i.e. bitvectors)
    0 1 0 0 1 1 0 1 1 1 0 1 0 1
    0 0 1 1 0 1 1 1 1 0 0 1 1 0
    0 1 1 1 0 1 0 0 1 1 0 1 1 1
    0 1 0 0 1 1 0 1 1 1 1 0 1 0
    # ...and so on

=item level 1 

Feature is active, but chromosomes can varies B<only on the right side>, Example:

	-variable_length => 1
	
    # chromosomes (i.e. bitvectors)
    0 1 0 0 1 1 0 1 1 1 
    0 0 1 1 0 1 1 1 1
    0 1 1 1 0 1 0 0 1 1 0 1 1 1
    0 1 0 0 1 1 0 1 1 1
    # ...and so on
	
=item level 2 

Feature is active and chromosomes can varies B<on the left side and on 
the right side>; unwanted values/genes on the left side are replaced with C<undef>, ie.
 
	-variable_length => 2
 
    # chromosomes (i.e. bitvectors)
    x x x 0 1 1 0 1 1 1 
    x x x x 0 1 1 1 1
    x 1 1 1 0 1 0 0 1 1 0 1 1 1
    0 1 0 0 1 1 0 1 1 1
    # where 'x' means 'undef'
    # ...and so on

In this situation returned chromosomes in an array context ($ga-E<gt>as_array($chromosome)) 
can have B<undef> values on the left side (only). In a scalar context each 
undefined value is replaced with a single space. If You don't want to see
any C<undef> or space, just use C<as_array_def_only> and C<as_string_def_only> 
instead of C<as_array> and C<as_string>.

=back

=item -parents  

This defines how many parents should be used in a crossover.

=item -selection

This defines how individuals/chromosomes are selected to crossover. It expects an array reference listed below:

    -selection => [ $type, @params ]

where type is one of:

=over 8

=item B<RouletteBasic>

Each individual/chromosome can be selected with probability proportional to its fitness.

=item B<Roulette>

lib/AI/Genetic/Pro.pm  view on Meta::CPAN


=item C<-selection =E<gt> [ 'RouletteDistribution', 'normal', $av, $sd ]>

Normal distribution, where C<$av> is average (default: size of population /2) and $C<$sd> is standard deviation (default: size of population).


=item C<-selection =E<gt> [ 'RouletteDistribution', 'beta', $aa, $bb ]>

I<Beta> distribution.  The density of the beta is:

    X^($aa - 1) * (1 - X)^($bb - 1) / B($aa , $bb) for 0 < X < 1.

C<$aa> and C<$bb> are set by default to number of parents.

B<Argument restrictions:> Both $aa and $bb must not be less than 1.0E-37.

=item C<-selection =E<gt> [ 'RouletteDistribution', 'binomial' ]>

Binomial distribution. No additional parameters are needed.

=item C<-selection =E<gt> [ 'RouletteDistribution', 'chi_square', $df ]>

lib/AI/Genetic/Pro.pm  view on Meta::CPAN

Standard uniform distribution. No additional parameters are needed.

=item C<-selection =E<gt> [ 'Distribution', 'normal', $av, $sd ]>

Normal distribution, where C<$av> is average (default: size of population /2) and $C<$sd> is standard deviation (default: size of population).

=item C<-selection =E<gt> [ 'Distribution', 'beta', $aa, $bb ]>

I<Beta> distribution.  The density of the beta is:

    X^($aa - 1) * (1 - X)^($bb - 1) / B($aa , $bb) for 0 < X < 1.

C<$aa> and C<$bb> are set by default to number of parents.

B<Argument restrictions:> Both $aa and $bb must not be less than 1.0E-37.

=item C<-selection =E<gt> [ 'Distribution', 'binomial' ]>

Binomial distribution. No additional parameters are needed.

=item C<-selection =E<gt> [ 'Distribution', 'chi_square', $df ]>

lib/AI/Genetic/Pro.pm  view on Meta::CPAN


=back

=back

=item -strategy 

This defines the astrategy of crossover operation. It expects an array
reference listed below:

    -strategy => [ $type, @params ]

where type is one of:

=over 4

=item PointsSimple

Simple crossover in one or many points. The best chromosomes/individuals are
selected for the new generation. For example:

    -strategy => [ 'PointsSimple', $n ]

where C<$n> is the number of points for crossing.

=item PointsBasic

Crossover in one or many points. In basic crossover selected parents are
crossed and one (randomly-chosen) child is moved to the new generation. For
example:

    -strategy => [ 'PointsBasic', $n ]

where C<$n> is the number of points for crossing.

=item Points

Crossover in one or many points. In normal crossover selected parents are crossed and the best child is moved to the new generation. For example:

    -strategy => [ 'Points', $n ]

where C<$n> is number of points for crossing.

=item PointsAdvenced

Crossover in one or many points. After crossover the best
chromosomes/individuals from all parents and chidren are selected for the  new
generation. For example:

    -strategy => [ 'PointsAdvanced', $n ]

where C<$n> is the number of points for crossing.

=item Distribution

In I<distribution> crossover parents are crossed in points selected with the
specified distribution. See below.

=over 8

lib/AI/Genetic/Pro.pm  view on Meta::CPAN

Standard uniform distribution. No additional parameters are needed.

=item C<-strategy =E<gt> [ 'Distribution', 'normal', $av, $sd ]>

Normal distribution, where C<$av> is average (default: number of parents/2) and C<$sd> is standard deviation (default: number of parents).

=item C<-strategy =E<gt> [ 'Distribution', 'beta', $aa, $bb ]>

I<Beta> distribution.  The density of the beta is:

    X^($aa - 1) * (1 - X)^($bb - 1) / B($aa , $bb) for 0 < X < 1.

C<$aa> and C<$bb> are set by default to the number of parents.

B<Argument restrictions:> Both $aa and $bb must not be less than 1.0E-37.

=item C<-strategy =E<gt> [ 'Distribution', 'binomial' ]>

Binomial distribution. No additional parameters are needed.

=item C<-strategy =E<gt> [ 'Distribution', 'chi_square', $df ]>

lib/AI/Genetic/Pro.pm  view on Meta::CPAN

This defines if the check for modifying chromosomes in a user-defined fitness
function is active. Directly modifying chromosomes is not allowed and it is 
a highway to big trouble. This mode should be used only for testing, because it is B<slow>.

=back

=item I<$ga>-E<gt>B<inject>($chromosomes)

Inject new, user defined, chromosomes into the current population. See example below:

    # example for bitvector
    my $chromosomes = [
        [ 1, 1, 0, 1, 0, 1 ],
        [ 0, 0, 0, 1, 0, 1 ],
        [ 0, 1, 0, 1, 0, 0 ],
        ...
    ];
    
    # inject
    $ga->inject($chromosomes);

If You want to delete some chromosomes from population, just C<splice> them:

    my @remove = qw(1 2 3 9 12);
	for my $idx (sort { $b <=> $a }  @remove){
        splice @{$ga->chromosomes}, $idx, 1;
    }

=item I<$ga>-E<gt>B<population>($population)

Set/get size of the population. This defines the size of the population, i.e. how many chromosomes to simultaneously exist at each generation.

=item I<$ga>-E<gt>B<indType>()

Get type of individuals/chromosomes. Currently supported types are:

=over 4

lib/AI/Genetic/Pro.pm  view on Meta::CPAN

=item C<combination>

Chromosomes will be unique lists of specified values. This is used for example
in the I<Traveling Salesman Problem>. See the documentation of the C<new>
method.

=back

In example:

    my $type = $ga->type();

=item I<$ga>-E<gt>B<type>()

Alias for C<indType>.

=item I<$ga>-E<gt>B<crossProb>()

This method is used to query and set the crossover rate.

=item I<$ga>-E<gt>B<crossover>()

lib/AI/Genetic/Pro.pm  view on Meta::CPAN

=item I<$ga>-E<gt>B<init>($args)

This method initializes the population with random individuals/chromosomes. It MUST be called before any call to C<evolve()>. It expects one argument, which depends on the type of individuals/chromosomes:

=over 4

=item B<bitvector>

For bitvectors, the argument is simply the length of the bitvector.

    $ga->init(10);

This initializes a population where each individual/chromosome has 10 genes.

=item B<listvector>

For listvectors, the argument is an anonymous list of lists. The number of sub-lists is equal to the number of genes of each individual/chromosome. Each sub-list defines the possible string values that the corresponding gene can assume.

    $ga->init([
               [qw/red blue green/],
               [qw/big medium small/],
               [qw/very_fat fat fit thin very_thin/],
              ]);

This initializes a population where each individual/chromosome has 3 genes and each gene can assume one of the given values.

=item B<rangevector>

For rangevectors, the argument is an anonymous list of lists. The number of sub-lists is equal to the number of genes of each individual/chromosome. Each sub-list defines the minimum and maximum integer values that the corresponding gene can assume.

    $ga->init([
               [1, 5],
               [0, 20],
               [4, 9],
              ]);

This initializes a population where each individual/chromosome has 3 genes and each gene can assume an integer within the corresponding range.

=item B<combination>

For combination, the argument is an anonymous list of possible values of gene.

    $ga->init( [ 'a', 'b', 'c' ] );

This initializes a population where each chromosome has 3 genes and each gene
is a unique combination of 'a', 'b' and 'c'. For example genes looks something
like that:

    [ 'a', 'b', 'c' ]    # gene 1
    [ 'c', 'a', 'b' ]    # gene 2
    [ 'b', 'c', 'a' ]    # gene 3
    # ...and so on...

=back

=item I<$ga>-E<gt>B<evolve>($n)

This method causes the GA to evolve the population for the specified number of
generations. If its argument is 0 or C<undef> GA will evolve the population to
infinity unless a C<terminate> function is specified.

=item I<$ga>-E<gt>B<getHistory>()

Get history of the evolution. It is in a format listed below:

	[
		# gen0   gen1   gen2   ...          # generations
		[ max0,  max1,  max2,  ... ],       # max values
		[ mean,  mean1, mean2, ... ],       # mean values
		[ min0,  min1,  min2,  ... ],       # min values
	]

=item I<$ga>-E<gt>B<getAvgFitness>()

Get I<max>, I<mean> and I<min> score of the current generation. In example:

    my ($max, $mean, $min) = $ga->getAvgFitness();

=item I<$ga>-E<gt>B<getFittest>($n, $unique)

This function returns a list of the fittest chromosomes from the current
population.  You can specify how many chromosomes should be returned and if
the returned chromosomes should be unique. See example below.

    # only one - the best
    my ($best) = $ga->getFittest;

    # or 5 bests chromosomes, NOT unique
    my @bests = $ga->getFittest(5);

    # or 7 bests and UNIQUE chromosomes
    my @bests = $ga->getFittest(7, 1);

If you want to get a large number of chromosomes, try to use the
C<getFittest_as_arrayref> function instead (for efficiency).

=item I<$ga>-E<gt>B<getFittest_as_arrayref>($n, $unique)

This function is very similar to C<getFittest>, but it returns a reference 
to an array instead of a list. 

=item I<$ga>-E<gt>B<generation>()

lib/AI/Genetic/Pro.pm  view on Meta::CPAN

=item -font

Path to font (in *.ttf format) to be used (default: none).

=item -logo

Path to logo (png/jpg image) to embed in a chart (default: none).

=item For example:

	$ga->chart(-width => 480, height => 320, -filename => 'chart.png');

=back

=item I<$ga>-E<gt>B<save>($file)

Save the current state of the genetic algorithm to the specified file.

=item I<$ga>-E<gt>B<load>($file)

Load a state of the genetic algorithm from the specified file. 

lib/AI/Genetic/Pro.pm  view on Meta::CPAN


=item I<$ga>-E<gt>B<as_array_def_only>($chromosome)

In list context return an array representing the specified chromosome. 
In scalar context return an reference to an array representing the specified 
chromosome. If I<variable_length> is turned off, this function is just an
alias for C<as_array>. If I<variable_length> is turned on and is set to 
level 2, this function will return only C<not undef> values from chromosome. 
See example below:

    # -variable_length => 2, -type => 'bitvector'
	
    my @chromosome = $ga->as_array($chromosome)
    # @chromosome looks something like that
    # ( undef, undef, undef, 1, 0, 1, 1, 1, 0 )
	
    @chromosome = $ga->as_array_def_only($chromosome)
    # @chromosome looks something like that
    # ( 1, 0, 1, 1, 1, 0 )

=item I<$ga>-E<gt>B<as_string>($chromosome)

Return a string representation of the specified chromosome. See example below:

	# -type => 'bitvector'
	
	my $string = $ga->as_string($chromosome);
	# $string looks something like that
	# 1___0___1___1___1___0 
	
	# or 
	
	# -type => 'listvector'
	
	$string = $ga->as_string($chromosome);
	# $string looks something like that
	# element0___element1___element2___element3...

Attention! If I<variable_length> is turned on and is set to level 2, it is 
possible to get C<undef> values on the left side of the vector. In the returned
string C<undef> values will be replaced with B<spaces>. If you don't want
to see any I<spaces>, use C<as_string_def_only> instead of C<as_string>.

=item I<$ga>-E<gt>B<as_string_def_only>($chromosome)

Return a string representation of specified chromosome. If I<variable_length> 
is turned off, this function is just alias for C<as_string>. If I<variable_length> 
is turned on and is set to level 2, this function will return a string without
C<undef> values. See example below:

	# -variable_length => 2, -type => 'bitvector'
	
	my $string = $ga->as_string($chromosome);
	# $string looks something like that
	#  ___ ___ ___1___1___0 
	
	$string = $ga->as_string_def_only($chromosome);
	# $string looks something like that
	# 1___1___0 

=item I<$ga>-E<gt>B<as_value>($chromosome)

Return the score of the specified chromosome. The value of I<chromosome> is 
calculated by the fitness function.

=back

=head1 SUPPORT

lib/AI/Genetic/Pro/Array/Type.pm  view on Meta::CPAN

package AI::Genetic::Pro::Array::Type;
$AI::Genetic::Pro::Array::Type::VERSION = '1.009';
use warnings;
use strict;
use Exporter::Lite;
use Tie::Array::Packed;
#=======================================================================
our @EXPORT_OK = qw(
	get_package_by_element_size
	get_array_ref_by_element_size
);
#-----------------------------------------------------------------------
our $Native = 0;
#=======================================================================
sub get_package_by_element_size {
	return if $Native;
	
	my $size = shift;
	
	my $type =	#$size <			   32	? undef										:	#  Pure Perl array
				#$size <			   32	? 'AI::Genetic::Pro::Array::Tied'			:	#  Pure Perl array
			 	$size <     		  128	? 'Tie::Array::Packed::Char'				: 	#  8 bits
				$size <     		  256	? 'Tie::Array::Packed::UnsignedChar'		:	#  8 bits
				$size <  		   65_537	? 'Tie::Array::Packed::ShortNative'			:	# 16 bits
				$size < 		  131_073	? 'Tie::Array::Packed::UnsignedShortNative'	:	# 16 bits
				$size < 	2_147_483_648	? 'Tie::Array::Packed::Integer'				:	# 32 bits
				$size < 	4_294_967_297	? 'Tie::Array::Packed::UnsignedInteger'		:	# 32 bits; MAX
				undef;
				
	return unless $type;
	return $type;
}
#=======================================================================
sub get_array_ref_by_element_size {
	my $package = get_package_by_element_size(shift);
	my @array;
	tie @array, $package if $package;
	return \@array;
}
#=======================================================================
1;

lib/AI/Genetic/Pro/Chromosome.pm  view on Meta::CPAN

package AI::Genetic::Pro::Chromosome;
$AI::Genetic::Pro::Chromosome::VERSION = '1.009';
use warnings;
use strict;
use List::Util qw(shuffle first);
use List::MoreUtils qw(first_index);
use Tie::Array::Packed;
#use Math::Random qw(random_uniform_integer);
#=======================================================================
sub new {
	my ($class, $data, $type, $package, $length) = @_;

	my @genes;	
	tie @genes, $package if $package;
	
	if($type eq q/bitvector/){
		#@genes = random_uniform_integer(scalar @$data, 0, 1); 			# this is fastest, but uses more memory
		@genes = map { rand > 0.5 ? 1 : 0 } 0..$length;					# this is faster
		#@genes =  split(q//, unpack("b*", rand 99999), $#$data + 1);	# slow
	}elsif($type eq q/combination/){ 
		#@genes = shuffle 0..$#{$data->[0]}; 
		@genes = shuffle 0..$length; 
	}elsif($type eq q/rangevector/){
  		@genes = map { $_->[1] + int rand($_->[2] - $_->[1] + 1) } @$data[0..$length];
	}else{ 
		@genes = map { 1 + int(rand( $#{ $data->[$_] })) } 0..$length; 
	}

	return bless \@genes, $class;
}
#=======================================================================
sub new_from_data {
	my ($class, $data, $type, $package, $values, $fix_range) = @_;

	die qq/\nToo many elements in the injected chromosome of type "$type": @$values\n/ if $#$values > $#$data;

	my @genes;	
	tie @genes, $package if $package;
	
	if($type eq q/bitvector/){ 
		die qq/\nInproper value in the injected chromosome of type "$type": @$values\n/ 
			if first { not defined $_ or ($_ != 0 and $_ != 1) } @$values;
		@genes = @$values; 
	}elsif($type eq q/combination/){
		die qq/\nToo few elements in the injected chromosome of type "$type": @$values\n/ 
			if $#$values != $#{$data->[0]};
		for my $idx(0..$#$values){
			my $id = first_index { $_ eq $values->[$idx] } @{$data->[0]};	# pomijamy poczatkowy undef
			die qq/\nInproper element in the injected chromosome of type "$type": @$values\n/ if $id == -1;
			push @genes, $id;
		}
	}elsif($type eq q/rangevector/){
		for my $idx(0..$#$values){
			if(defined $values->[$idx]){
				my $min = $data->[$idx]->[1] - $fix_range->[$idx];
				my $max = $data->[$idx]->[2] - $fix_range->[$idx];
				die qq/\nValue out of scope in the injected chromosome of type "$type": @$values\n/ 
					if $values->[$idx] > $max or $values->[$idx] < $min;
				push @genes, $values->[$idx] + $fix_range->[$idx];
			}else{ push @genes, 0; }
		}
	}else{
		for my $idx(0..$#$values){
			my $id = first_index { 
				not defined $values->[$idx] and not defined $_ or 
				defined $_ and defined $values->[$idx] and $_ eq $values->[$idx] 
					} @{$data->[$idx]};	# pomijamy poczatkowy undef
			die qq/\nInproper element in the injected chromosome of type "$type": @$values\n/ if $id == -1;
			push @genes, $id;
		}
	}
	
	return bless \@genes, $class;
}
#=======================================================================
sub clone
{
	my ( $self ) = @_;
	
	my $cln;
	if( my $obj = tied( @$self ) ){
		$cln = $obj->make_clone;
	}else{
		@$cln = @$self;
	}
	
	return bless( $cln );
	
	#my $genes = tied(@{$self})->make_clone;
	#return bless($genes);
}

#=======================================================================
1;

lib/AI/Genetic/Pro/Crossover/Distribution.pm  view on Meta::CPAN

package AI::Genetic::Pro::Crossover::Distribution;
$AI::Genetic::Pro::Crossover::Distribution::VERSION = '1.009';
use warnings;
use strict;
#use Data::Dumper; $Data::Dumper::Sortkeys = 1;
use Math::Random qw(
	random_uniform_integer 
	random_normal 
	random_beta
	random_binomial
	random_chi_square
	random_exponential
	random_poisson
);
use List::MoreUtils qw(first_index);
#=======================================================================
sub new { 
	my ($class, $type, @params) = @_;
	bless { 
			type 	=> $type,
			params	=> \@params,
		}, $class; 
}
#=======================================================================
sub run {
	my ($self, $ga) = @_;
	
	my ($chromosomes, $parents, $crossover) = ($ga->chromosomes, $ga->_parents, $ga->crossover);
	my ($fitness, $_fitness) = ($ga->fitness, $ga->_fitness);
	my $high  = scalar @{$chromosomes->[0]};
	my @children;
	#-------------------------------------------------------------------
	while(my $elders = shift @$parents){
		my @elders = unpack 'I*', $elders;
		
		unless(scalar @elders){
			$_fitness->{scalar(@children)} = $fitness->($ga, $chromosomes->[$elders[0]]);
			push @children, $chromosomes->[$elders[0]];
			next;
		}
		
		#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		my $len = scalar @elders;
		my @seq;
		if($self->{type} eq q/uniform/){
			@seq = random_uniform_integer($high, 0, $#elders);
		}elsif($self->{type} eq q/normal/){
			my $av = defined $self->{params}->[0] ? $self->{params}->[0] : $len/2;
			my $sd = defined $self->{params}->[1] ? $self->{params}->[1] : $len;
			@seq = map { $_ % $len } random_normal($high, $av, $sd);
		}elsif($self->{type} eq q/beta/){
			my $aa = defined $self->{params}->[0] ? $self->{params}->[0] : $len;
			my $bb = defined $self->{params}->[1] ? $self->{params}->[1] : $len;
			@seq = map { int($_ * $len) } random_beta($high, $aa, $bb);
		}elsif($self->{type} eq q/binomial/){
			@seq = random_binomial($high, $#elders, rand);
		}elsif($self->{type} eq q/chi_square/){
			my $df = defined $self->{params}->[0] ? $self->{params}->[0] : $len;
			@seq = map { $_ % $len } random_chi_square($high, $df);
		}elsif($self->{type} eq q/exponential/){
			my $av = defined $self->{params}->[0] ? $self->{params}->[0] : $len/2;
			@seq = map { $_ % $len } random_exponential($high, $av);
		}elsif($self->{type} eq q/poisson/){
			my $mu = defined $self->{params}->[0] ? $self->{params}->[0] : $len/2;
			@seq = map { $_ % $len } random_poisson($high, $mu) ;
		}else{
			die qq/Unknown distribution "$self->{type}" in "crossover"!\n/;
		}
		
		#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		my ($min, $max) = (0, $#{$chromosomes->[0]} - 1);
		if($ga->variable_length){
			for my $el(@elders){
				my $idx = first_index { $_ } @{$chromosomes->[$el]};
				$min = $idx if $idx > $min;
				$max = $#{$chromosomes->[$el]} if $#{$chromosomes->[$el]} < $max;
			}
		}
		
		$elders[0] = $chromosome->[$elders[0]]->clone;
		for(0..$#seq){
			next if not $seq[$_] or $_ < $min or $_ > $max;
			$elders[0]->[$_] = $chromosomes->[$elders[$seq[$_]]]->[$_];
		}
		#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		
		push @children, $elders[ 0 ];
	}
	#-------------------------------------------------------------------
	
	return \@children;
}
#=======================================================================
1;

lib/AI/Genetic/Pro/Crossover/OX.pm  view on Meta::CPAN

package AI::Genetic::Pro::Crossover::OX;
$AI::Genetic::Pro::Crossover::OX::VERSION = '1.009';
use warnings;
use strict;
use List::MoreUtils qw(first_index);
#use Data::Dumper; $Data::Dumper::Sortkeys = 1;
#=======================================================================
sub new { bless \$_[0], $_[0]; }
#=======================================================================
sub save_fitness {
	my ($self, $ga, $idx) = @_;
	$ga->_fitness->{$idx} = $ga->fitness->($ga, $ga->chromosomes->[$idx]);
	return $ga->chromosomes->[$idx];
}
#=======================================================================
sub run {
	my ($self, $ga) = @_;
	
	my ($chromosomes, $parents, $crossover) = ($ga->chromosomes, $ga->_parents, $ga->crossover);
	my ($fitness, $_fitness) = ($ga->fitness, $ga->_fitness);
	my @children;
	#-------------------------------------------------------------------
	while(my $elders = shift @$parents){
		my @elders = unpack 'I*', $elders;
		
		unless(scalar @elders){
			push @children, $chromosomes->[$elders[0]];
			next;
		}
		
		my @points = sort { $a <=> $b } map { 1 + int(rand $#{$chromosomes->[0]}) } 0..1;
		
		@elders = sort {
					my @av = @{$a}[$points[0]..$points[1]];
					my @bv = @{$b}[$points[0]..$points[1]];
					
					for my $e(@av){
						splice(@$b, (first_index { $_ == $e } @$b), 1);
					}
					splice @$b, $points[0], 0, @av;
					
					for my $e(@bv){
						splice(@$a, (first_index { $_ == $e } @$a), 1);
					}
					splice @$a, $points[0], 0, @bv;
					0;
					
						} map { 
							$chromosomes->[$_]->clone;
								} @elders;
		
		
		my %elders = map { $_ => $fitness->($ga, $elders[$_]) } 0..$#elders;
		my $max = (sort { $elders{$a} <=> $elders{$b} } keys %elders)[-1];
		$_fitness->{scalar(@children)} = $elders{$max};
		
		push @children, $elders[$max];
	}
	#-------------------------------------------------------------------
	
	return \@children;
}
#=======================================================================
1;

lib/AI/Genetic/Pro/Crossover/PMX.pm  view on Meta::CPAN

package AI::Genetic::Pro::Crossover::PMX;
$AI::Genetic::Pro::Crossover::PMX::VERSION = '1.009';
use warnings;
use strict;
use List::MoreUtils qw(indexes);
#use Data::Dumper; $Data::Dumper::Sortkeys = 1;
#=======================================================================
sub new { bless \$_[0], $_[0]; }
#=======================================================================
sub dup {
    my ($ar) = @_;

    my %seen;
    my @dup = grep { if($seen{$_}){ 1 }else{ $seen{$_} = 1; 0} } @$ar;
    return \@dup if @dup;
    return;
}
#=======================================================================
sub run {
	my ($self, $ga) = @_;
	
	my ($chromosomes, $parents, $crossover) = ($ga->chromosomes, $ga->_parents, $ga->crossover);
	my ($fitness, $_fitness) = ($ga->fitness, $ga->_fitness);
	my @children;
	#-------------------------------------------------------------------
	while(my $elders = shift @$parents){
		my @elders = unpack 'I*', $elders;
		
		unless(scalar @elders){
			push @children, $chromosomes->[$elders[0]];
			next;
		}
		
		my @points = sort { $a <=> $b } map { 1 + int(rand $#{$chromosomes->[0]}) } 0..1;
		
		@elders = sort {
					my @av = @{$a}[$points[0]..$points[1]-1];
					my @bv = splice @$b, $points[0], $points[1] - $points[0], @av;
					splice @$a, $points[0], $points[1] - $points[0], @bv;
					
					my %av; @av{@av} = @bv;
					my %bv; @bv{@bv} = @av;

					while(my $dup = dup($a)){
    					foreach my $val (@$dup){
        					my ($ind) = grep { $_ < $points[0] or $_ >= $points[1] } indexes { $_ == $val } @$a;
        					$a->[$ind] = $bv{$val};
    					}
					}

					while(my $dup = dup($b)){
    					foreach my $val (@$dup){
        					my ($ind) = grep { $_ < $points[0] or $_ >= $points[1] } indexes { $_ == $val } @$b;
        					$b->[$ind] = $av{$val};
    					}
					}
					
					0;
						} map { 
							$chromosomes->[$_]->clone
								} @elders;
		
		
		my %elders = map { $_ => $fitness->($ga, $elders[$_]) } 0..$#elders;
		my $max = (sort { $elders{$a} <=> $elders{$b} } keys %elders)[-1];
		$_fitness->{scalar(@children)} = $elders{$max};
		
		push @children, $elders[$max];
	}
	#-------------------------------------------------------------------
	
	return \@children;
}
#=======================================================================
1;

lib/AI/Genetic/Pro/Crossover/Points.pm  view on Meta::CPAN

package AI::Genetic::Pro::Crossover::Points;
$AI::Genetic::Pro::Crossover::Points::VERSION = '1.009';
use warnings;
use strict;
use List::MoreUtils qw(first_index);
#use Data::Dumper; $Data::Dumper::Sortkeys = 1;
#=======================================================================
sub new { bless { points => $_[1] ? $_[1] : 1 }, $_[0]; }
#=======================================================================
sub run {
	my ($self, $ga) = @_;
	
	my ($chromosomes, $parents, $crossover) = ($ga->chromosomes, $ga->_parents, $ga->crossover);
	my ($fitness, $_fitness) = ($ga->fitness, $ga->_fitness);
	my @children;
	#-------------------------------------------------------------------
	while(my $elders = shift @$parents){
		my @elders = unpack 'I*', $elders;
		
		unless(scalar @elders){
			$_fitness->{scalar(@children)} = $fitness->($ga, $chromosomes->[$elders[0]]);
			push @children, $chromosomes->[$elders[0]];
			next;
		}

		my ($min, $max) = (0, $#{$chromosomes->[0]});
		if($ga->variable_length){
			for my $el(@elders){
				my $idx = first_index { $_ } @{$chromosomes->[$el]};
				$min = $idx if $idx > $min;
				$max = $#{$chromosomes->[$el]} if $#{$chromosomes->[$el]} < $max;
			}
		}
		
		my @points;
		if($min < $max and $max - $min > 2){
			my $range = $max - $min;
			@points = map { $min + int(rand $range) } 1..$self->{points};
		}

		@elders = map { $chromosomes->[$_]->clone } @elders;
		
		for my $pt(@points){
			@elders = sort {
						splice @$b, 0, $pt, splice( @$a, 0, $pt, @$b[0..$pt-1] );
						0;
							} @elders;
		}
		
		my %elders = map { $_ => $fitness->($ga, $elders[$_]) } 0..$#elders;
		my $maximum = (sort { $elders{$a} <=> $elders{$b} } keys %elders)[-1];
		$_fitness->{scalar(@children)} = $elders{$maximum};
		
		push @children, $elders[$maximum];
	}
	#-------------------------------------------------------------------
	return \@children;
}
#=======================================================================
1;

lib/AI/Genetic/Pro/Crossover/PointsAdvanced.pm  view on Meta::CPAN

$AI::Genetic::Pro::Crossover::PointsAdvanced::VERSION = '1.009';
use warnings;
use strict;
use List::MoreUtils qw(first_index);
#use Data::Dumper; $Data::Dumper::Sortkeys = 1;
#use AI::Genetic::Pro::Array::PackTemplate;
#=======================================================================
sub new { bless { points => $_[1] ? $_[1] : 1 }, $_[0]; }
#=======================================================================
sub run {
	my ($self, $ga) = @_;
	
	my ($chromosomes, $parents, $crossover) = ($ga->chromosomes, $ga->_parents, $ga->crossover);
	my ($fitness, $_fitness) = ($ga->fitness, $ga->_fitness);
	#-------------------------------------------------------------------
	while(my $elders = shift @$parents){
		my @elders = unpack 'I*', $elders;

		unless(scalar @elders){
			push @$chromosomes, $chromosomes->[$elders[0]];
			next;
		}
	
		my ($min, $max) = (0, $#{$chromosomes->[0]} - 1);
		if($ga->variable_length){
			for my $el(@elders){
				my $idx = first_index { $_ } @{$chromosomes->[$el]};
				$min = $idx if $idx > $min;
				$max = $#{$chromosomes->[$el]} if $#{$chromosomes->[$el]} < $max;
			}
		}
		
		my @points;
		if($min < $max and $max - $min > 2){
			my $range = $max - $min;
			@points = map { $min + int(rand $range) } 1..$self->{points};
		}

		@elders = map { $chromosomes->[$_]->clone } @elders;
		for my $pt(@points){
			@elders = sort {
						splice @$b, 0, $pt, splice( @$a, 0, $pt, @$b[0..$pt-1] );
						0;
							} @elders;
		}
		
		push @$chromosomes, @elders;
	}
	#-------------------------------------------------------------------
	# wybieranie potomkow ze zbioru starych i nowych osobnikow
	@$chromosomes = sort { $fitness->($ga, $a) <=> $fitness->($ga, $b) } @$chromosomes;
	splice @$chromosomes, 0, scalar(@$chromosomes) - $ga->population;
	%$_fitness = map { $_ => $fitness->($ga, $chromosomes->[$_]) } 0..$#$chromosomes;
	#-------------------------------------------------------------------
	return $chromosomes;
}
#=======================================================================
1;

lib/AI/Genetic/Pro/Crossover/PointsBasic.pm  view on Meta::CPAN

package AI::Genetic::Pro::Crossover::PointsBasic;
$AI::Genetic::Pro::Crossover::PointsBasic::VERSION = '1.009';
use warnings;
use strict;
use List::MoreUtils qw(first_index);
#use Data::Dumper; $Data::Dumper::Sortkeys = 1;
#=======================================================================
sub new { bless { points => $_[1] ? $_[1] : 1 }, $_[0]; }
#=======================================================================
sub run {
	my ($self, $ga) = @_;
	
	my ($chromosomes, $parents, $crossover) = ($ga->chromosomes, $ga->_parents, $ga->crossover);
	my ($fitness, $_fitness) = ($ga->fitness, $ga->_fitness);
	my @children;
	#-------------------------------------------------------------------
	while(my $elders = shift @$parents){
		my @elders = unpack 'I*', $elders;
		
		unless(scalar @elders){
			$_fitness->{scalar(@children)} = $fitness->($ga, $chromosomes->[$elders[0]]);
			push @children, $chromosomes->[$elders[0]];
			next;
		}
		
		my ($min, $max) = (0, $#{$chromosomes->[0]} - 1);
		if($ga->variable_length){
			for my $el(@elders){
				my $idx = first_index { $_ } @{$chromosomes->[$el]};
				$min = $idx if $idx > $min;
				$max = $#{$chromosomes->[$el]} if $#{$chromosomes->[$el]} < $max;
			}
		}
		
		my @points;
		if($min < $max and $max - $min > 2){
			my $range = $max - $min;
			@points = map { $min + int(rand $range) } 1..$self->{points};
		}

		@elders = map { $chromosomes->[$_]->clone } @elders;
		for my $pt(@points){
			@elders = sort {
						splice @$b, 0, $pt, splice( @$a, 0, $pt, @$b[0..$pt-1] );
						0;
							} @elders;
		}

		my $idx = int rand @elders;
		$_fitness->{scalar(@children)} = $fitness->($ga, $elders[$idx]);
		push @children, $elders[ $idx ];
	}
	#-------------------------------------------------------------------
	
	return \@children;
}
#=======================================================================
1;

lib/AI/Genetic/Pro/Crossover/PointsSimple.pm  view on Meta::CPAN

package AI::Genetic::Pro::Crossover::PointsSimple;
$AI::Genetic::Pro::Crossover::PointsSimple::VERSION = '1.009';
use warnings;
use strict;
use List::MoreUtils qw(first_index);
#use Data::Dumper; $Data::Dumper::Sortkeys = 1;
#=======================================================================
sub new { bless { points => $_[1] ? $_[1] : 1 }, $_[0]; }
#=======================================================================
sub run {
	my ($self, $ga) = @_;
	
	my ($chromosomes, $parents, $crossover) = ($ga->chromosomes, $ga->_parents, $ga->crossover);
	my ($fitness, $_fitness) = ($ga->fitness, $ga->_fitness);
	my @children;
	#-------------------------------------------------------------------
	while(my $elders = shift @$parents){
		my @elders = unpack 'I*', $elders;

		unless(scalar @elders){
			push @children, $chromosomes->[$elders[0]];
			next;
		}
		
		my ($min, $max) = (0, $#{$chromosomes->[0]} - 1);
		if($ga->variable_length){
			for my $el(@elders){
				my $idx = first_index { $_ } @{$chromosomes->[$el]};
				$min = $idx if $idx > $min;
				$max = $#{$chromosomes->[$el]} if $#{$chromosomes->[$el]} < $max;
			}
		}
		
		my @points;
		if($min < $max and $max - $min > 2){
			my $range = $max - $min;
			@points = map { $min + int(rand $range) } 1..$self->{points};
		}
		
		@elders = map { $chromosomes->[$_]->clone } @elders;
		for my $pt(@points){
			@elders = sort {
						splice @$b, 0, $pt, splice( @$a, 0, $pt, @$b[0..$pt-1] );
						0;
							} @elders;
		}
		
		push @children, @elders;
	}
	#-------------------------------------------------------------------
	# wybieranie potomkow ze zbioru nowych osobnikow
	@children = sort { $fitness->($ga, $a) <=> $fitness->($ga, $b) } @children;
	splice @children, 0, scalar(@children) - scalar(@$chromosomes);
	%$_fitness = map { $_ => $fitness->($ga, $children[$_]) } 0..$#children;
	#-------------------------------------------------------------------
	return \@children;
}
#=======================================================================
1;

lib/AI/Genetic/Pro/MCE.pm  view on Meta::CPAN

use List::Util 						qw( shuffle );
use MCE( Sereal => 0 );
#use MCE::Loop;
use MCE::Map;
use MCE::Util;
#-----------------------------------------------------------------------	
$Storable::Deparse 	= 1;
$Storable::Eval 	= 1;
#-----------------------------------------------------------------------
__PACKAGE__->mk_accessors( qw(
	_pop
	_tpl
));
#=======================================================================
sub new {
	my ( $cls, $obj, $tpl ) = @_;
	
	my $self = bless $obj, $cls;
	
	#-------------------------------------------------------------------
	$self->_init_mce;
	$self->_init_pop;
	
	#-------------------------------------------------------------------
	$AI::Genetic::Pro::Array::Type::Native = 1 if $self->native;
	
	#-------------------------------------------------------------------
	delete $tpl->{ $_ } for qw( -history -mce -population -workers );
	$self->_tpl( $tpl );
	
	#-------------------------------------------------------------------
	return $self;
}
#=======================================================================
sub _init_pop {
	my ( $self ) = @_;
	
	my $pop = int( $self->population / $self->workers );
	my $rst = $self->population % $self->workers;
	
	my @pop = ( $pop ) x $self->workers;
	$pop[ 0 ] += $rst;
	
	$self->_pop( \@pop );
}
#=======================================================================
sub _calculate_fitness_all {
	my ($self) = @_;
	
	# Faster version. Thanks to Mario Roy :-)
	my %fit = mce_map_s {
			$_ => $self->fitness()->( $self, $self->chromosomes->[ $_ ] )
		} 0, $#{ $self->chromosomes };

	# The old one
	#my %fit = mce_map {
	#		$_ => $self->fitness()->( $self, $self->chromosomes->[ $_ ] )
	#	} 0 .. $#{ $self->chromosomes };

	$self->_fitness( \%fit );
	
	return;
}
#=======================================================================
sub _init_mce {
	my ( $self ) = @_;
	
	#-------------------------------------------------------------------
	$self->workers( MCE::Util::get_ncpu() ) unless $self->workers;
	
	#-------------------------------------------------------------------
	MCE::Map->init(
		chunk_size 	=> 1,					# Thanks Roy :-)
		#chunk_size => q[auto],				# The old one
		max_workers => $self->workers,
		posix_exit => 1,					# Thanks Roy :-)
	);
	
	#-------------------------------------------------------------------
	return;
}
#=======================================================================
sub init {
	my ( $self, $val ) = @_;
	
	#-------------------------------------------------------------------
	my $pop = $self->population;
	$self->population( 1 );
	$self->SUPER::init(  $val  );
	$self->population( $pop );
	
	#-------------------------------------------------------------------
	my $one = shift @{ $self->chromosomes };	
	my $tpl = $self->_tpl;
	
	my @lst = mce_map {
		my $arg = clone( $tpl );
		$arg->{ -population } = $_;
		my $gal = AI::Genetic::Pro->new( %$arg );
		$gal->init( $val );
		@{ $gal->_state };
		
	} @{ $self->_pop };
	
	#-------------------------------------------------------------------
	return $self->_adopt( \@lst );
}
#=======================================================================
sub _adopt {
	my ( $self, $lst ) = @_;
	
	if( my $typ = $self->_package ){
		for my $idx ( 0 .. $#$lst ){
			$lst->[ $idx ]->[ 0 ] = $typ->make_with_packed( $lst->[ $idx ]->[ 0 ] );
			bless $lst->[ $idx ]->[ 0 ], q[AI::Genetic::Pro::Chromosome];
		}
	}
	
	my ( @chr, %fit, @rhc, %tif );
	for my $sth ( @$lst ){
		push @chr, $sth->[ 0 ];
		$fit{ $#chr } = $sth->[ 1 ];
	}
	
	#@$lst = ( );
	
	my @idx = shuffle 0 .. $#chr;
	
	for my $i ( @idx ){
		push @rhc, $chr[ $i ];
		$tif{ $#rhc } = $fit{ $i };
	}
	
	$self->_fitness	  ( \%tif );
	$self->chromosomes( \@rhc );
	
	return;
}
#=======================================================================
sub _chunks {
	my ( $self ) = @_;
	
	my $cnt = 0;
	my @chk;
	
	for my $idx ( 0 .. $#{ $self->_pop } ){
		my $pos = 0;
		my %tmp = map { $pos++ => $self->_fitness->{ $_ } } $cnt .. $cnt + $self->_pop->[ $idx ] -1 ;
		my @tmp = splice @{ $self->chromosomes }, 0, $self->_pop->[ $idx ];
		$cnt += @tmp;
		
		if( $self->_package ){
			push @chk, [
				[ map { ${ tied( @$_ ) } } @tmp ],
				\%tmp,
			];
		}else{
			push @chk, [
				\@tmp,
				\%tmp,
			];
		}
	}
	
	return \@chk;
}
#=======================================================================
sub evolve {
	my ( $self, $generations ) = @_;

	$generations ||= -1; 	 

	for(my $i = 0; $i != $generations; $i++){
		
		# terminate ----------------------------------------------------
		last if $self->terminate and $self->terminate->( $self );
		
		# update generation --------------------------------------------
		$self->generation($self->generation + 1);
		
		# update history -----------------------------------------------
		$self->_save_history;

		my $tpl = $self->_tpl;
		my @lst = mce_map {
			#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
			my $ary = $_;
			#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
			my $arg = clone( $tpl );
			$arg->{ -population } = 1;
			my $gal = AI::Genetic::Pro->new( %$arg );
			$gal->init( 1 );
			#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
			if( my $typ = $self->_package ){
				for my $idx ( 0 .. $#{ $ary->[ 0 ] } ){
					$ary->[ 0 ][ $idx ] = $typ->make_with_packed( $ary->[ 0 ][ $idx ] );
					bless $ary->[ 0 ][ $idx ], q[AI::Genetic::Pro::Chromosome];
				}	
			}
			#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
			$gal->population ( scalar( @{ $ary->[ 0 ] } ) );
			$gal->chromosomes( $ary->[ 0 ] );
			$gal->_fitness	 ( $ary->[ 1 ] );
			$gal->strict	 ( 0 );
			#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
			$gal->evolve	 ( 1 );
			#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
			@{ $gal->_state };
			
		} $self->_chunks;

		$self->_adopt( \@lst );
	}

	return;
}
#=======================================================================
1;

lib/AI/Genetic/Pro/Mutation/Bitvector.pm  view on Meta::CPAN

package AI::Genetic::Pro::Mutation::Bitvector;
$AI::Genetic::Pro::Mutation::Bitvector::VERSION = '1.009';
use warnings;
use strict;
#use Data::Dumper; $Data::Dumper::Sortkeys = 1;
#=======================================================================
sub new { bless \$_[0], $_[0]; }
#=======================================================================
sub run {
	my ($self, $ga) = @_;

	# this is declared here just for speed
	my $mutation = $ga->mutation;
	my $chromosomes = $ga->chromosomes;
	my $_translations = $ga->_translations;
	my ($fitness, $_fitness) = ($ga->fitness, $ga->_fitness);
	
	# main loop
	for my $idx (0..$#$chromosomes){
		next if rand() >= $mutation;
		
		if($ga->variable_length){
			my $rand = rand();
			if($rand < 0.16 and $#{$chromosomes->[$idx]} > 1){
				pop @{$chromosomes->[$idx]};
			}elsif($rand < 0.32 and $#{$chromosomes->[$idx]} > 1){
				shift @{$chromosomes->[$idx]};
			}elsif($rand < 0.48 and $#{$chromosomes->[$idx]} < $#$_translations){
				push @{$chromosomes->[$idx]}, rand > 0.5 ? 0 : 1;
			}elsif($rand < 0.64 and $#{$chromosomes->[$idx]} < $#$_translations){
				unshift @{$chromosomes->[$idx]}, rand > 0.5 ? 0 : 1;
			}elsif($rand < 0.8){
				##tied(@{$chromosomes->[$idx]})->reverse;
				
				#if( my $obj = tied( @{ $chromosomes->[ $idx ] } ) ){
				#	$obj->reverse;
				#}else{
				#	@{ $chromosomes->[ $idx ] } = reverse @{ $chromosomes->[ $idx ] };
				#}
				#@{ $chromosomes->[ $idx ] } = reverse @{ $chromosomes->[ $idx ] };
				
			}else{
				my $id = int rand @{$chromosomes->[$idx]};
				$chromosomes->[$idx]->[$id] = $chromosomes->[$idx]->[$id] ? 0 : 1;
			}
		}else{
			my $id = int rand @{$chromosomes->[$idx]};
			$chromosomes->[$idx]->[$id] = $chromosomes->[$idx]->[$id] ? 0 : 1;	
		}
		# we need to change fitness
		$_fitness->{$idx} = $fitness->($ga, $chromosomes->[$idx]);
	}
	
	return 1;
}
#=======================================================================
# too slow; mutation is too dangerous in this solution
sub run0 {
	my ($self, $ga) = @_;

	my $mutation = $ga->mutation; # this is declared here just for speed
	foreach my $chromosome (@{$ga->{chromosomes}}){
		if(rand() < $mutation){ tied(@$chromosome)->reverse; }
		else{
			for(0..$#$chromosome){
				next if rand > $mutation;
				$chromosome->[$_] = $chromosome->[$_] ? 0 : 1;
			}
		}
	}
	
	return 1;
}
#=======================================================================
1;

lib/AI/Genetic/Pro/Mutation/Combination.pm  view on Meta::CPAN

package AI::Genetic::Pro::Mutation::Combination;
$AI::Genetic::Pro::Mutation::Combination::VERSION = '1.009';
use warnings;
use strict;
use List::MoreUtils qw(first_index);
#use Data::Dumper; $Data::Dumper::Sortkeys = 1;
#=======================================================================
sub new { bless \$_[0], $_[0]; }
#=======================================================================
sub run {
	my ($self, $ga) = @_;

	# this is declared here just for speed
	my $mutation = $ga->mutation;
	my $chromosomes = $ga->chromosomes;
	my $_translations = $ga->_translations;
	my ($fitness, $_fitness) = ($ga->fitness, $ga->_fitness);
	my $inv = $mutation / 2;
	
	# main loop
	for my $idx (0..$#$chromosomes){
		
		my $rand = rand;
		
		if($rand < $inv) { tied(@{$chromosomes->[$idx]})->reverse; }
		elsif($rand < $mutation){
			my $el = int rand @{$chromosomes->[$idx]};
			my $new = int rand @{$_translations->[0]};
			next if $new == $chromosomes->[$idx]->[$el];
			
			my $id = first_index { $_ == $new } @{$chromosomes->[$idx]};
			$chromosomes->[$idx]->[$id] = $chromosomes->[$idx]->[$el] if defined $id and $id != -1;
			$chromosomes->[$idx]->[$el] = $new;
		}
		
		# we need to change fitness
		$_fitness->{$idx} = $fitness->($ga, $chromosomes->[$idx]);
	}
	
	return 1;
}
#=======================================================================
1;

lib/AI/Genetic/Pro/Mutation/Listvector.pm  view on Meta::CPAN

package AI::Genetic::Pro::Mutation::Listvector;
$AI::Genetic::Pro::Mutation::Listvector::VERSION = '1.009';
use warnings;
use strict;
use List::MoreUtils qw(first_index);
#use Data::Dumper; $Data::Dumper::Sortkeys = 1;
#=======================================================================
sub new { bless \$_[0], $_[0]; }
#=======================================================================
sub run {
	my ($self, $ga) = @_;

	# this is declared here just for speed
	my $mutation = $ga->mutation;
	my $chromosomes = $ga->chromosomes;
	my $_translations = $ga->_translations;
	my ($fitness, $_fitness) = ($ga->fitness, $ga->_fitness);
	
	# main loop
	for my $idx (0..$#$chromosomes){
		next if rand() >= $mutation;

		if($ga->variable_length){
			my $rand = rand();
			my $min = first_index { $_ } @{$chromosomes->[$idx]};
			my $range = $#{$chromosomes->[$idx]} - $min + 1;
		
			if($rand < 0.4 and $range > 2){
				if($rand < 0.2 and $ga->variable_length > 1){ $chromosomes->[$idx]->[$min] = 0; }
				else{ pop @{$chromosomes->[$idx]};	}
			}elsif($rand < 0.8 and $range < $#$_translations){
				if($rand < 0.6 and $ga->variable_length > 1 and not $chromosomes->[$idx]->[0]){
					$chromosomes->[$idx]->[ $min - 1 ] = 1 + int rand $#{$_translations->[ $min - 1 ]};
				}elsif(exists $_translations->[scalar @{$chromosomes->[$idx]}]){
					push @{$chromosomes->[$idx]}, 1 + int rand $#{$_translations->[scalar @{$chromosomes->[$idx]}]};
				}
			}else{
				my $id = $min + int rand($range - 1);
				$chromosomes->[$idx]->[$id] = 1 + int rand $#{$_translations->[$id]};
			}
		}else{
			my $id = int rand @{$chromosomes->[$idx]};
			$chromosomes->[$idx]->[$id] = 1 + int rand $#{$_translations->[$id]};
		}
		
		# we need to change fitness
		$_fitness->{$idx} = $fitness->($ga, $chromosomes->[$idx]);
	}
	
	return 1;
}
#=======================================================================
1;

lib/AI/Genetic/Pro/Mutation/Rangevector.pm  view on Meta::CPAN

$AI::Genetic::Pro::Mutation::Rangevector::VERSION = '1.009';
use warnings;
use strict;
use List::MoreUtils qw(first_index);
use Math::Random qw(random_uniform_integer);
#use Data::Dumper; $Data::Dumper::Sortkeys = 1;
#=======================================================================
sub new { bless \$_[0], $_[0]; }
#=======================================================================
sub run {
	my ($self, $ga) = @_;

	# this is declared here just for speed
	my $mutation = $ga->mutation;
	my $chromosomes = $ga->chromosomes;
	my $_translations = $ga->_translations;
	my ($fitness, $_fitness) = ($ga->fitness, $ga->_fitness);

	# main loop
	for my $idx (0..$#$chromosomes){
		next if rand() >= $mutation;

		if($ga->variable_length){
			my $rand = rand();

			my $min = first_index { $_ } @{$chromosomes->[$idx]};
			my $range = $#{$chromosomes->[$idx]} - $min + 1;
		
			if($rand < 0.4 and $range > 2){
				if($rand < 0.2 and $ga->variable_length > 1){ $chromosomes->[$idx]->[$min] = 0; }
				else{ pop @{$chromosomes->[$idx]};	}
			}elsif($rand < 0.8 and $range < scalar @{$_translations}){
				if($rand < 0.6 and $ga->variable_length > 1 and not $chromosomes->[$idx]->[0]){
					$chromosomes->[$idx]->[ $min - 1 ] = random_uniform_integer(1, @{$_translations->[ $min - 1 ]}[1..2]);
				}elsif(exists $_translations->[scalar @{$chromosomes->[$idx]}]){
					push @{$chromosomes->[$idx]}, random_uniform_integer(1, @{$_translations->[scalar @{$chromosomes->[$idx]}]}[1..2]);	
				}
			}else{
				my $id = $min + int rand($range - 1);
				$chromosomes->[$idx]->[$id] = random_uniform_integer(1, @{$_translations->[$id]}[1..2]);	
			}
		}else{
			my $id = int rand @{$chromosomes->[$idx]};
			$chromosomes->[$idx]->[$id] = random_uniform_integer(1, @{$_translations->[$id]}[1..2]);	
		}
		
		# we need to change fitness
		$_fitness->{$idx} = $fitness->($ga, $chromosomes->[$idx]);
	}
	
	return 1;
}
#=======================================================================
sub run0 {
	my ($self, $ga) = @_;

	# this is declared here just for speed
	my $mutation = $ga->mutation;
	
	# main loop
	foreach my $chromosome (@{$ga->{chromosomes}}){
		next if rand() <= $mutation;
		
		if($ga->variable_length){
			my $rand = rand();
			if($rand < 0.33 and $#$chromosome > 1){
				pop @$chromosome;
			}elsif($rand < 0.66 and $#$chromosome < $#{$ga->_translations}){
				push @$chromosome, random_uniform_integer(1, @{$ga->_translations->[scalar @$chromosome]});
			}else{
				my $idx = int rand @$chromosome;
				$chromosome->[$idx] = random_uniform_integer(1, @{$ga->_translations->[$idx]});	
			}
		}else{
			my $idx = int rand @$chromosome;
			$chromosome->[$idx] = random_uniform_integer(1, @{$ga->_translations->[$idx]});		
		}
	}
	
	return 1;
}
#=======================================================================
1;

lib/AI/Genetic/Pro/Selection/Distribution.pm  view on Meta::CPAN

package AI::Genetic::Pro::Selection::Distribution;
$AI::Genetic::Pro::Selection::Distribution::VERSION = '1.009';
use warnings;
use strict;
#use Data::Dumper; $Data::Dumper::Sortkeys = 1;
#use AI::Genetic::Pro::Array::PackTemplate;
use Math::Random qw(
	random_uniform_integer 
	random_normal 
	random_beta
	random_binomial
	random_chi_square
	random_exponential
	random_poisson
);
use Carp 'croak';
#=======================================================================
sub new { 
	my ($class, $type, @params) = @_;
	bless { 
			type 	=> $type,
			params	=> \@params,
		}, $class; 
}
#=======================================================================
sub run {
	my ($self, $ga) = @_;
	
	my ($fitness, $chromosomes) = ($ga->_fitness, $ga->chromosomes);
	croak "You must set a number of parents to use the Distribution strategy"
		unless defined($ga->parents);
	my $parents = $ga->parents;
	my @parents;
	my $high = scalar @$chromosomes;
	#-------------------------------------------------------------------
	if($self->{type} eq q/uniform/){
		push @parents, 
			pack 'I*', random_uniform_integer($parents, 0, $#$chromosomes) 
				for 0..$#$chromosomes;
	}elsif($self->{type} eq q/normal/){
		my $av = defined $self->{params}->[0] ? $self->{params}->[0] : $#$chromosomes/2;
		my $sd = defined $self->{params}->[1] ? $self->{params}->[1] : $#$chromosomes;
		push @parents, 
			pack 'I*', map { int $_ % $high } random_normal($parents, $av, $sd)  
				for 0..$#$chromosomes;
	}elsif($self->{type} eq q/beta/){
		my $aa = defined $self->{params}->[0] ? $self->{params}->[0] : $parents;
		my $bb = defined $self->{params}->[1] ? $self->{params}->[1] : $parents;
		push @parents, 
			pack 'I*', map { int($_ * $high) } random_beta($parents, $aa, $bb)
				for 0..$#$chromosomes;
	}elsif($self->{type} eq q/binomial/){
		push @parents, 
			pack 'I*', random_binomial($parents, $#$chromosomes, rand) 
				for 0..$#$chromosomes;
	}elsif($self->{type} eq q/chi_square/){
		my $df = defined $self->{params}->[0] ? $self->{params}->[0] : $#$chromosomes;
		push @parents,
			pack 'I*', map { int $_ % $high } random_chi_square($parents, $df)
				for 0..$#$chromosomes;
	}elsif($self->{type} eq q/exponential/){
		my $av = defined $self->{params}->[0] ? $self->{params}->[0] : $#$chromosomes/2;
		push @parents, 
			pack 'I*', map { int $_ % $high } random_exponential($parents, $av)  
				for 0..$#$chromosomes;
	}elsif($self->{type} eq q/poisson/){
		my $mu = defined $self->{params}->[0] ? $self->{params}->[0] : $#$chromosomes/2;
		push @parents,
			pack 'I*', map { int $_ % $high } random_poisson($parents, $mu)
				for 0..$#$chromosomes;
	}else{
		die qq/Unknown distribution "$self->{type}" in "selection"!\n/;
	}
	
	#-------------------------------------------------------------------
	return \@parents;
}
#=======================================================================

1;

lib/AI/Genetic/Pro/Selection/Roulette.pm  view on Meta::CPAN

use strict;
#use Data::Dumper; $Data::Dumper::Sortkeys = 1;
use List::Util qw(sum min);
use List::MoreUtils qw(first_index);
use Carp 'croak';

#=======================================================================
sub new { bless \$_[0], $_[0]; }
#=======================================================================
sub run {
	my ($self, $ga) = @_;
	
	my ($fitness) = ($ga->_fitness);
	my (@parents, @elders);
	#-------------------------------------------------------------------
	my $count = $#{$ga->chromosomes};
	my $const = min values %$fitness;
	$const = $const < 0 ? abs($const) : 0;
	my $total = sum( map { $_ < 0 ? $_ + $const : $_ } values %$fitness);
	$total ||= 1;
	
	# elders
	for my $idx (0..$count){
		push @elders, $idx for 1..int((($fitness->{$idx} + $const) / $total) * $count);
	}
	
	if((my $add = $count - scalar @elders) > 0){
		my $idx = $elders[rand @elders];
		push @elders, int rand($count) for 0..$add;
	}
	
	croak "You must set a crossover probability to use the Roulette strategy"
		unless defined($ga->crossover);
	croak "You must set a number of parents to use the Roulette strategy"
		unless defined($ga->parents);

	# parents
	for(0..$count){
		if(rand > $ga->crossover){
			push @parents, pack 'I*', $elders[ rand @elders ]
		}else{
			my @group;
			push @group, $elders[ rand @elders ] for 1..$ga->parents;
			push @parents, pack 'I*', @group;
		}
	}

	#-------------------------------------------------------------------
	return \@parents;
}
#=======================================================================

1;

lib/AI/Genetic/Pro/Selection/RouletteBasic.pm  view on Meta::CPAN

use warnings;
use strict;
use List::Util qw(min);
#use Data::Dumper; $Data::Dumper::Sortkeys = 1;
use List::MoreUtils qw(first_index);
use Carp 'croak';
#=======================================================================
sub new { bless \$_[0], $_[0]; }
#=======================================================================
sub run {
	my ($self, $ga) = @_;
	
	my ($fitness, $chromosomes) = ($ga->_fitness, $ga->chromosomes);
	croak "You must set a number of parents to use the RouletteBasic strategy"
		unless defined($ga->parents);
	my $parents = $ga->parents;
	my (@parents, @wheel);
	my $const = min values %$fitness;
	$const = $const < 0 ? abs($const) : 0;
	my $total = 0;
	#-------------------------------------------------------------------
	foreach my $key (keys %$fitness){
		$total += $fitness->{$key} + $const;
		push @wheel, [ $key, $total ];
	}
	
	for(0..$#$chromosomes){
		my @group;
		for(1..$parents){
			my $rand = rand($total);
			my $idx = first_index { $_->[1] > $rand } @wheel;
			if($idx == 0){ $idx = 1 }
			elsif($idx == -1 ) { $idx = scalar @wheel; }
			push @group, $wheel[$idx-1]->[0];
		}
		push @parents, pack 'I*', @group;
	}
	
	#-------------------------------------------------------------------
	return \@parents;
}
#=======================================================================

1;

lib/AI/Genetic/Pro/Selection/RouletteDistribution.pm  view on Meta::CPAN

package AI::Genetic::Pro::Selection::RouletteDistribution;
$AI::Genetic::Pro::Selection::RouletteDistribution::VERSION = '1.009';
use warnings;
use strict;
use List::Util qw(min);
use List::MoreUtils qw(first_index);
#use Data::Dumper; $Data::Dumper::Sortkeys = 1;
#use AI::Genetic::Pro::Array::PackTemplate;
use Math::Random qw(
	random_uniform
	random_normal 
	random_beta
	random_binomial
	random_chi_square
	random_exponential
	random_poisson
);
use Carp 'croak';
#=======================================================================
sub new { 
	my ($class, $type, @params) = @_;
	bless { 
			type 	=> $type,
			params	=> \@params,
		}, $class; 
}
#=======================================================================
sub roulette {
	my ($total, $wheel) = @_;
	my $rand = rand($total);
	my $idx = first_index { $_->[1] > $rand } @$wheel;
	if($idx == 0){ $idx = 1 }
	elsif($idx == -1 ) { $idx = scalar @$wheel; }
	return $wheel->[$idx-1]->[0];
}
#=======================================================================
sub run {
	my ($self, $ga) = @_;
	
	my ($fitness, $chromosomes) = ($ga->_fitness, $ga->chromosomes);
	croak "You must set a number of parents for the RouletteDistribution strategy"
		unless defined($ga->parents);
	my $parents = $ga->parents;
	my $high = scalar @$chromosomes;
	my (@parents, @wheel);
	my $const = min values %$fitness;
	$const = $const < 0 ? abs($const) : 0;
	my $total = 0;
	#-------------------------------------------------------------------
	foreach my $key (keys %$fitness){
		$total += $fitness->{$key} + $const;
		push @wheel, [ $key, $total ];
	}
	#-------------------------------------------------------------------
	if($self->{type} eq q/uniform/){
		push @parents, 
			pack 'I*', 
				map { roulette($total, \@wheel) }
				 	random_uniform($parents, 0, $total) 
						for 0..$#$chromosomes;
	}elsif($self->{type} eq q/normal/){
		my $av = defined $self->{params}->[0] ? $self->{params}->[0] : $#$chromosomes/2;
		my $sd = defined $self->{params}->[1] ? $self->{params}->[1] : $#$chromosomes;
		push @parents, 
			pack 'I*', 
				map { roulette($total, \@wheel) }
					map { int $_ % $high } random_normal($parents, $av, $sd)  
						for 0..$#$chromosomes;
	}elsif($self->{type} eq q/beta/){
		my $aa = defined $self->{params}->[0] ? $self->{params}->[0] : $parents;
		my $bb = defined $self->{params}->[1] ? $self->{params}->[1] : $parents;
		push @parents, 
			pack 'I*', 
				map { roulette($total, \@wheel) }
					map { int($_ * $high) } random_beta($parents, $aa, $bb)
						for 0..$#$chromosomes;
	}elsif($self->{type} eq q/binomial/){
		push @parents, 
			pack 'I*', 
				map { roulette($total, \@wheel) }
					random_binomial($parents, $#$chromosomes, rand) 
						for 0..$#$chromosomes;
	}elsif($self->{type} eq q/chi_square/){
		my $df = defined $self->{params}->[0] ? $self->{params}->[0] : $#$chromosomes;
		push @parents,
			pack 'I*', 
				map { roulette($total, \@wheel) }
					map { int $_ % $high } random_chi_square($parents, $df)
						for 0..$#$chromosomes;
	}elsif($self->{type} eq q/exponential/){
		my $av = defined $self->{params}->[0] ? $self->{params}->[0] : $#$chromosomes/2;
		push @parents, 
			pack 'I*', 
				map { roulette($total, \@wheel) }
					map { int $_ % $high } random_exponential($parents, $av)  
						for 0..$#$chromosomes;
	}elsif($self->{type} eq q/poisson/){
		my $mu = defined $self->{params}->[0] ? $self->{params}->[0] : $#$chromosomes/2;
		push @parents,
			pack 'I*', 
				map { roulette($total, \@wheel) }
					map { int $_ % $high } random_poisson($parents, $mu)
						for 0..$#$chromosomes;
	}else{
		die qq/Unknown distribution "$self->{type}" in "selection"!\n/;
	}
	
	#-------------------------------------------------------------------
	return \@parents;
}
#=======================================================================

1;

t/01_inject.t  view on Meta::CPAN

use Struct::Compare;
use AI::Genetic::Pro;

use constant BITS => 32;

my @Win; 
push @Win, 1 for 1..BITS;
my $Win = sum( \@Win );

sub sum {
	my ($ar) = @_;
	my $counter = 0;
	for(0..$#$ar){
		$counter += $ar->[$_] if $ar->[$_];
	}
	return $counter;
}

sub fitness {
	my ($ga, $chromosome) = @_;
	return sum(scalar $ga->as_array($chromosome));
}

sub terminate {
    my ($ga) = @_;
	return 1 if $Win == $ga->as_value($ga->getFittest);
	return;
}

my $ga = AI::Genetic::Pro->new(        
        -fitness         => \&fitness,        # fitness function
        -terminate       => \&terminate,      # terminate function
        -type            => 'bitvector',      # type of chromosomes
        -population      => 100,              # population
        -crossover       => 0.9,              # probab. of crossover
        -mutation        => 0.05,             # probab. of mutation
        -parents         => 2,                # number  of parents
        -selection       => [ 'Roulette' ],   # selection strategy
        -strategy        => [ 'Points', 2 ],  # crossover strategy
        -cache           => 0,                # cache results
        -history         => 0,                # remember best results
        -preserve        => 0,                # remember the bests
        -variable_length => 0,                # turn variable length OFF
);

# init population of 32-bit vectors
$ga->init(BITS);

my $population = [ ];
for my $chromosome(@{$ga->chromosomes}){
	push @$population, $chromosome->clone;
}

my @data;
for(0..BITS){
	my @chromosome;
	push @chromosome, rand() < 0.5 ? 1 : 0 for 1..BITS;
	push @data, \@chromosome;
}

push @$population, @data;
$ga->inject(\@data);

my $OK = 1;
for(0..$#$population){
	my @tmp0 = @{$population->[$_]};
	my @tmp1 = @{$ga->chromosomes->[$_]};
	unless(compare(\@tmp0, \@tmp1)){
		$OK = 0;
		last;
	}
}

ok($OK);

t/02_cache.t  view on Meta::CPAN


use FindBin qw($Bin);
use lib $Bin;
use Test::More qw(no_plan);
use Time::HiRes;
use AI::Genetic::Pro;

use constant BITS => 32;

sub sum {
	my ($ar) = @_;
	my $counter = 0;
	for(0..$#$ar){
		$counter += $ar->[$_] if $ar->[$_];
	}
	return $counter;
}

sub fitness {
	my ($ga, $chromosome) = @_;
	return sum(scalar $ga->as_array($chromosome));
}

my $ga = AI::Genetic::Pro->new(        
        -fitness         => \&fitness,        # fitness function
        -terminate       => sub { return; },  # terminate function
        -type            => 'bitvector',      # type of chromosomes
        -population      => 10,               # population
        -crossover       => 0.9,              # probab. of crossover
        -mutation        => 0.05,             # probab. of mutation
        -parents         => 2,                # number  of parents
        -selection       => [ 'Roulette' ],   # selection strategy
        -strategy        => [ 'Points', 2 ],  # crossover strategy
        -cache           => 0,                # cache results
        -history         => 0,                # remember best results
        -preserve        => 0,                # remember the bests
        -variable_length => 0,                # turn variable length OFF
);

# init population of 32-bit vectors
$ga->init(BITS);
$ga->chromosomes( [ ] );
$ga->inject( [ [ qw( 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) ] ] );
my $start = [Time::HiRes::gettimeofday()];
$ga->as_value($ga->chromosomes->[0]) for 0..10000;
my $time0 =Time::HiRes::tv_interval($start);


$ga = AI::Genetic::Pro->new(        
        -fitness         => \&fitness,        # fitness function
        -terminate       => sub { return; },  # terminate function
        -type            => 'bitvector',      # type of chromosomes
        -population      => 10,               # population
        -crossover       => 0.9,              # probab. of crossover
        -mutation        => 0.05,             # probab. of mutation
        -parents         => 2,                # number  of parents
        -selection       => [ 'Roulette' ],   # selection strategy
        -strategy        => [ 'Points', 2 ],  # crossover strategy
        -cache           => 1,                # cache results
        -history         => 0,                # remember best results
        -preserve        => 0,                # remember the bests
        -variable_length => 0,                # turn variable length OFF
);

# init population of 32-bit vectors
$ga->init(BITS);
$ga->chromosomes( [ ] );
$ga->inject( [ [ qw( 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) ] ] );
$start = [Time::HiRes::gettimeofday()];
$ga->as_value($ga->chromosomes->[0]) for 0..10000;
my $time1 =Time::HiRes::tv_interval($start);

t/03_bitvectors_constant_length.t  view on Meta::CPAN

use Test::More qw(no_plan);
use AI::Genetic::Pro;

use constant BITS => 32;

my @Win; 
push @Win, 1 for 1..BITS;
my $Win = sum( \@Win );

sub sum {
	my ($ar) = @_;
	my $counter = 0;
	for(0..$#$ar){
		$counter += $ar->[$_] if $ar->[$_];
	}
	return $counter;
}

sub fitness {
	my ($ga, $chromosome) = @_;
	return sum(scalar $ga->as_array($chromosome));
}

sub terminate {
    my ($ga) = @_;
	return 1 if $Win == $ga->as_value($ga->getFittest);
	return;
}

my $ga = AI::Genetic::Pro->new(        
        -fitness         => \&fitness,        # fitness function
        -terminate       => \&terminate,      # terminate function
        -type            => 'bitvector',      # type of chromosomes
        -population      => 100,              # population
        -crossover       => 0.9,              # probab. of crossover
        -mutation        => 0.05,             # probab. of mutation
        -parents         => 2,                # number  of parents
        -selection       => [ 'Roulette' ],   # selection strategy
        -strategy        => [ 'Points', 2 ],  # crossover strategy
        -cache           => 1,                # cache results
        -history         => 0,                # remember best results
        -preserve        => 0,                # remember the bests
        -variable_length => 0,                # turn variable length OFF
);

# init population of 32-bit vectors
$ga->init(BITS);

my @helper = (
	[ qw( 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 ) ],
	[ qw( 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 ) ],
	[ qw( 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 ) ],
	[ qw( 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 ) ],
	[ qw( 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 ) ],
	[ qw( 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 ) ],
	[ qw( 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 ) ],
	[ qw( 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 ) ],
	[ qw( 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 ) ],
	[ qw( 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 ) ],
	[ qw( 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 ) ],
	[ qw( 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 ) ],
);
$ga->inject(\@helper);

# evolve 1000 generations
$ga->evolve(1000);

ok($Win == $ga->as_value($ga->getFittest));

t/04_bitvectors_variable_length_I.t  view on Meta::CPAN

use Test::More qw(no_plan);
use AI::Genetic::Pro;

use constant BITS => 32;

my @Win; 
push @Win, 1 for 0..BITS-1;
my $Win = sum( \@Win );

sub sum {
	my ($ar) = @_;
	my $counter = 0;
	for(0..$#$ar){
		$counter += $ar->[$_] if $ar->[$_];
	}
	return $counter;
}

sub fitness {
	my ($ga, $chromosome) = @_;
	return sum(scalar $ga->as_array($chromosome));
}

sub terminate {
    my ($ga) = @_;
	return 1 if $Win == $ga->as_value($ga->getFittest);
	return;
}

my $ga = AI::Genetic::Pro->new(        
        -fitness         => \&fitness,        # fitness function
        -terminate       => \&terminate,      # terminate function
        -type            => 'bitvector',      # type of chromosomes
        -population      => 100,              # population
        -crossover       => 0.9,              # probab. of crossover
        -mutation        => 0.05,             # probab. of mutation
        -parents         => 2,                # number  of parents
        -selection       => [ 'Roulette' ],   # selection strategy
        -strategy        => [ 'Points', 2 ],  # crossover strategy
        -cache           => 1,                # cache results
        -history         => 0,                # remember best results
        -preserve        => 0,                # remember the bests
        -variable_length => 1,                # turn variable length OFF
);

# init population of 32-bit vectors
$ga->init(BITS);

my @helper = (
	[ qw( 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 ) ],
	[ qw( 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 ) ],
	[ qw( 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 ) ],
	[ qw( 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 ) ],
	[ qw( 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 ) ],
	[ qw( 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 ) ],
	[ qw( 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 ) ],
	[ qw( 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 ) ],
	[ qw( 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 ) ],
	[ qw( 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 ) ],
	[ qw( 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 ) ],
	[ qw( 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 ) ],
);
$ga->inject(\@helper);


# evolve 1000 generations
$ga->evolve(1000);

ok($Win == $ga->as_value($ga->getFittest));

t/05_bitvectors_variable_length_II.t  view on Meta::CPAN

use Test::More qw(no_plan);
use AI::Genetic::Pro;

use constant BITS => 32;

my @Win; 
push @Win, 1 for 0..BITS-1;
my $Win = sum( \@Win );

sub sum {
	my ($ar) = @_;
	my $counter = 0;
	for(0..$#$ar){
		$counter += $ar->[$_] if $ar->[$_];
	}
	return $counter;
}

sub fitness {
	my ($ga, $chromosome) = @_;
	return sum(scalar $ga->as_array($chromosome));
}

sub terminate {
    my ($ga) = @_;
	return 1 if $Win == $ga->as_value($ga->getFittest);
	return;
}

my $ga = AI::Genetic::Pro->new(        
        -fitness         => \&fitness,        # fitness function
        -terminate       => \&terminate,      # terminate function
        -type            => 'bitvector',      # type of chromosomes
        -population      => 100,              # population
        -crossover       => 0.9,              # probab. of crossover
        -mutation        => 0.05,             # probab. of mutation
        -parents         => 2,                # number  of parents
        -selection       => [ 'Roulette' ],   # selection strategy
        -strategy        => [ 'Points', 2 ],  # crossover strategy
        -cache           => 1,                # cache results
        -history         => 0,                # remember best results
        -preserve        => 0,                # remember the bests
        -variable_length => 2,                # turn variable length OFF
);

# init population of 32-bit vectors
$ga->init(BITS);

my @helper = (
	[ qw( 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 ) ],
	[ qw( 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 ) ],
	[ qw( 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 ) ],
	[ qw( 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 ) ],
	[ qw( 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 ) ],
	[ qw( 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 ) ],
	[ qw( 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 ) ],
	[ qw( 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 ) ],
	[ qw( 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 ) ],
	[ qw( 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 ) ],
	[ qw( 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 ) ],
	[ qw( 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 ) ],
);
$ga->inject(\@helper);

# evolve 1000 generations
$ga->evolve(1000);

ok($Win == $ga->as_value($ga->getFittest));



( run in 0.525 second using v1.01-cache-2.11-cpan-4d50c553e7e )