Initial commit
This commit is contained in:
		
							
								
								
									
										4
									
								
								.formatter.exs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								.formatter.exs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | |||||||
|  | # Used by "mix format" | ||||||
|  | [ | ||||||
|  |   inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] | ||||||
|  | ] | ||||||
							
								
								
									
										26
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | |||||||
|  | # The directory Mix will write compiled artifacts to. | ||||||
|  | /_build/ | ||||||
|  |  | ||||||
|  | # If you run "mix test --cover", coverage assets end up here. | ||||||
|  | /cover/ | ||||||
|  |  | ||||||
|  | # The directory Mix downloads your dependencies sources to. | ||||||
|  | /deps/ | ||||||
|  |  | ||||||
|  | # Where third-party dependencies like ExDoc output generated docs. | ||||||
|  | /doc/ | ||||||
|  |  | ||||||
|  | # Ignore .fetch files in case you like to edit your project deps locally. | ||||||
|  | /.fetch | ||||||
|  |  | ||||||
|  | # If the VM crashes, it generates a dump, let's ignore it too. | ||||||
|  | erl_crash.dump | ||||||
|  |  | ||||||
|  | # Also ignore archive artifacts (built via "mix archive.build"). | ||||||
|  | *.ez | ||||||
|  |  | ||||||
|  | # Ignore package tarball (built via "mix hex.build"). | ||||||
|  | genetic-*.tar | ||||||
|  |  | ||||||
|  | # Temporary files, for example, from tests. | ||||||
|  | /tmp/ | ||||||
							
								
								
									
										21
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | |||||||
|  | # Genetic | ||||||
|  |  | ||||||
|  | **TODO: Add description** | ||||||
|  |  | ||||||
|  | ## Installation | ||||||
|  |  | ||||||
|  | If [available in Hex](https://hex.pm/docs/publish), the package can be installed | ||||||
|  | by adding `genetic` to your list of dependencies in `mix.exs`: | ||||||
|  |  | ||||||
|  | ```elixir | ||||||
|  | def deps do | ||||||
|  |   [ | ||||||
|  |     {:genetic, "~> 0.1.0"} | ||||||
|  |   ] | ||||||
|  | end | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc) | ||||||
|  | and published on [HexDocs](https://hexdocs.pm). Once published, the docs can | ||||||
|  | be found at <https://hexdocs.pm/genetic>. | ||||||
|  |  | ||||||
							
								
								
									
										77
									
								
								lib/genetic.ex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								lib/genetic.ex
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,77 @@ | |||||||
|  | defmodule Genetic do | ||||||
|  |     alias Types.Chromosome | ||||||
|  |  | ||||||
|  |     def initialize(genotype, opts \\ []) do | ||||||
|  |         population_size = Keyword.get(opts, :population_size, 100) | ||||||
|  |         for _ <- 1..population_size, do: genotype.() | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     def evaluate(population, fitness_function, opts \\ []) do | ||||||
|  |         population | ||||||
|  |         |> Enum.map( | ||||||
|  |             fn chromosome -> | ||||||
|  |                 fitness = fitness_function.(chromosome) | ||||||
|  |                 age = chromosome.age + 1 | ||||||
|  |                 %Chromosome{chromosome | fitness: fitness, age: age} | ||||||
|  |             end | ||||||
|  |         ) | ||||||
|  |         |> Enum.sort_by(& &1.fitness, &>=/2) | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     def select(population, opts \\ []) do | ||||||
|  |         population | ||||||
|  |         |> Enum.chunk_every(2) | ||||||
|  |         |> Enum.map(&List.to_tuple(&1)) | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     def crossover(population, opts \\ []) do | ||||||
|  |         population | ||||||
|  |         |> Enum.reduce([], | ||||||
|  |             fn {p1, p2}, acc -> | ||||||
|  |                 cx_point = :rand.uniform(length(p1.genes)) | ||||||
|  |                 {{h1,t1},{h2,t2}} = | ||||||
|  |                     {Enum.split(p1.genes, cx_point), | ||||||
|  |                     Enum.split(p2.genes, cx_point)} | ||||||
|  |                 {c1,c2} = {%Chromosome{p1 | genes: h1 ++ t2}, %Chromosome{p2 | genes: h2 ++ t1} } | ||||||
|  |                 [c1,c2 | acc] | ||||||
|  |             end | ||||||
|  |         ) | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     def mutation(population, opts \\ []) do  | ||||||
|  |         population | ||||||
|  |         |> Enum.map( | ||||||
|  |             fn chromosome -> | ||||||
|  |                 if :rand.uniform() < 0.05 do | ||||||
|  |                     %Chromosome{chromosome | genes: Enum.shuffle(chromosome)} | ||||||
|  |                 else | ||||||
|  |                     chromosome | ||||||
|  |                 end | ||||||
|  |             end | ||||||
|  |         ) | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     def run(problem, opts \\ []) do | ||||||
|  |         population = initialize(&problem.genotype/0) | ||||||
|  |         population  | ||||||
|  |         |> evolve(problem, opts) | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     def evolve(population, problem, opts \\ []) do | ||||||
|  |         population = evaluate(population, &problem.fitness_function/1, opts) | ||||||
|  |         best = hd(population) | ||||||
|  |         IO.write("\rCurrent Best: #{best.fitness}") | ||||||
|  |         if problem.terminate?(population) do | ||||||
|  |             best | ||||||
|  |         else | ||||||
|  |             population | ||||||
|  |             |> select(opts) | ||||||
|  |             |> crossover(opts) | ||||||
|  |             |> mutation(opts) | ||||||
|  |             |> evolve(problem, opts) | ||||||
|  |         end | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     end | ||||||
|  |  | ||||||
|  | end | ||||||
							
								
								
									
										12
									
								
								lib/types/chromosome.ex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								lib/types/chromosome.ex
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | |||||||
|  | defmodule Types.Chromosome do | ||||||
|  |     @type t :: %__MODULE__ { | ||||||
|  |         genes: Enum.t, | ||||||
|  |         size: integer(), | ||||||
|  |         fitness: number(), | ||||||
|  |         age: integer() | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     @enforce_keys :genes | ||||||
|  |     defstruct [:genes, size: 0, fitness: 0, age: 0] | ||||||
|  | end | ||||||
							
								
								
									
										28
									
								
								mix.exs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								mix.exs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | |||||||
|  | defmodule Genetic.MixProject do | ||||||
|  |   use Mix.Project | ||||||
|  |  | ||||||
|  |   def project do | ||||||
|  |     [ | ||||||
|  |       app: :genetic, | ||||||
|  |       version: "0.1.0", | ||||||
|  |       elixir: "~> 1.13", | ||||||
|  |       start_permanent: Mix.env() == :prod, | ||||||
|  |       deps: deps() | ||||||
|  |     ] | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   # Run "mix help compile.app" to learn about applications. | ||||||
|  |   def application do | ||||||
|  |     [ | ||||||
|  |       extra_applications: [:logger] | ||||||
|  |     ] | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   # Run "mix help deps" to learn about dependencies. | ||||||
|  |   defp deps do | ||||||
|  |     [ | ||||||
|  |       # {:dep_from_hexpm, "~> 0.3.0"}, | ||||||
|  |       # {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"} | ||||||
|  |     ] | ||||||
|  |   end | ||||||
|  | end | ||||||
							
								
								
									
										9
									
								
								problem.ex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								problem.ex
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | |||||||
|  | defmodule Problem do | ||||||
|  |     alias Types.Chromosome | ||||||
|  |  | ||||||
|  |     @callback genotype :: Chromosome.t | ||||||
|  |  | ||||||
|  |     @callback fitness_function(Chromosome.t) :: number() | ||||||
|  |  | ||||||
|  |     @callback terminate?(Enum.t) :: boolean() | ||||||
|  | end | ||||||
							
								
								
									
										23
									
								
								scripts/one_max.exs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								scripts/one_max.exs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | |||||||
|  | genotype = fn -> for _ <- 1..1000, do: Enum.random(0..1) end | ||||||
|  |  | ||||||
|  | fitness_function = fn chromosome -> Enum.sum(chromosome) end | ||||||
|  | max_fitness = 1000 | ||||||
|  |  | ||||||
|  | defmodule OneMax do | ||||||
|  |     @behaviour Problem | ||||||
|  |  | ||||||
|  |     @impl Problem | ||||||
|  |     def genotype() do | ||||||
|  |         for _ <- 1..1000, do: Enum.random(0.. | ||||||
|  |     end | ||||||
|  |      | ||||||
|  |     @impl Problem | ||||||
|  |     def fitness_function(chromosome) do | ||||||
|  |         Enum.sum(chromosome.genes) | ||||||
|  |     end | ||||||
|  | end | ||||||
|  |   | ||||||
|  | soln = Genetic.run(fitness_function, genotype, max_fitness) | ||||||
|  |  | ||||||
|  | IO.write("\n") | ||||||
|  | IO.inspect(soln) | ||||||
							
								
								
									
										8
									
								
								test/genetic_test.exs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								test/genetic_test.exs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | |||||||
|  | defmodule GeneticTest do | ||||||
|  |   use ExUnit.Case | ||||||
|  |   doctest Genetic | ||||||
|  |  | ||||||
|  |   test "greets the world" do | ||||||
|  |     assert Genetic.hello() == :world | ||||||
|  |   end | ||||||
|  | end | ||||||
							
								
								
									
										1
									
								
								test/test_helper.exs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								test/test_helper.exs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | |||||||
|  | ExUnit.start() | ||||||
		Reference in New Issue
	
	Block a user