Initial commit

This commit is contained in:
2022-06-22 13:15:43 +02:00
commit 0b318ac0e4
10 changed files with 209 additions and 0 deletions

4
.formatter.exs Normal file
View File

@@ -0,0 +1,4 @@
# Used by "mix format"
[
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
]

26
.gitignore vendored Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View File

@@ -0,0 +1 @@
ExUnit.start()