genetic

genetic algorithm framework
git clone igris.git:dracuxan/genetic.git
Log | Files | Refs | README

commit 0606211a1e8e0a5011f0213e982e6d9fac285285
parent 605f11cae7a35fdd65bf207c77837b85008de6dd
Author: dracuxan <[email protected]>
Date:   Mon, 27 Apr 2026 16:08:36 +0530

feet: change c-based random number generator to rust based nif

Diffstat:
Mlib/genetic.ex | 22+++++++++++++---------
Alib/genetic/random.ex | 7+++++++
Alib/genetic/tree.ex | 7+++++++
Mlib/toolbox/crossover.ex | 2+-
Mmix.exs | 29+++--------------------------
Mmix.lock | 3+++
Anative/random/.cargo/config.toml | 5+++++
Anative/random/.gitignore | 1+
Anative/random/Cargo.lock | 149+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Anative/random/Cargo.toml | 13+++++++++++++
Anative/random/README.md | 20++++++++++++++++++++
Anative/random/src/lib.rs | 20++++++++++++++++++++
Anative/tree/.cargo/config.toml | 5+++++
Anative/tree/.gitignore | 1+
Anative/tree/Cargo.lock | 149+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Anative/tree/Cargo.toml | 13+++++++++++++
Anative/tree/README.md | 20++++++++++++++++++++
Anative/tree/src/lib.rs | 6++++++
Mscripts/n_queens.exs | 4++--
Mscripts/tetris.exs | 2+-
Dsrc/random.c | 17-----------------
21 files changed, 439 insertions(+), 56 deletions(-)

diff --git a/lib/genetic.ex b/lib/genetic.ex @@ -1,12 +1,5 @@ defmodule Genetic do alias Types.Chromosome - @on_load :load_nif - def load_nif do - path = ~c"./priv/genetic" - :erlang.load_nif(path, 0) - end - - def xor96, do: 0 def run(problem, opts \\ []) do population = initialize(&problem.genotype/0, opts) @@ -58,7 +51,12 @@ defmodule Genetic do end def crossover(population, opts \\ []) do - crossover_fn = Keyword.get(opts, :crossover_type, &Toolbox.Crossover.single_point/3) + crossover_fn = + Keyword.get( + opts, + :crossover_type, + &Toolbox.Crossover.single_point/3 + ) population |> Enum.reduce( @@ -103,7 +101,13 @@ defmodule Genetic do end def reinsertion(parents, offsprings, leftover, opts \\ []) do - strategy = Keyword.get(opts, :reinsertion_strategy, &Toolbox.Reinsertion.uniform/4) + strategy = + Keyword.get( + opts, + :reinsertion_strategy, + &Toolbox.Reinsertion.uniform/4 + ) + apply(strategy, [parents, offsprings, leftover, opts]) end diff --git a/lib/genetic/random.ex b/lib/genetic/random.ex @@ -0,0 +1,7 @@ +defmodule Genetic.Random do + use Rustler, otp_app: :genetic, crate: :random + + def xor96, do: error() + + defp error, do: :erlang.nif_error(:nif_not_loaded) +end diff --git a/lib/genetic/tree.ex b/lib/genetic/tree.ex @@ -0,0 +1,7 @@ +defmodule Genetic.Tree do + use Rustler, otp_app: :genetic, crate: :tree + + def add(_a, _b), do: error() + + defp error, do: :erlang.nif_error(:nif_not_loaded) +end diff --git a/lib/toolbox/crossover.ex b/lib/toolbox/crossover.ex @@ -31,7 +31,7 @@ defmodule Toolbox.Crossover do end def single_point(p1, p2, _opts \\ []) do - cx_point = rem(Genetic.xor96(), length(p1.genes)) + cx_point = rem(Genetic.Random.xor96(), length(p1.genes)) {{head1, tail1}, {head2, tail2}} = {Enum.split(p1.genes, cx_point), Enum.split(p2.genes, cx_point)} diff --git a/mix.exs b/mix.exs @@ -1,27 +1,3 @@ -defmodule Mix.Tasks.Compile.Cnif do - use Mix.Task.Compiler - - def run(_args) do - File.mkdir_p!("priv") - - {output, code} = - System.cmd( - "gcc", - [ - "-fpic", - "-shared", - "-o", - "priv/random.so", - "src/random.c" - ], - stderr_to_stdout: true - ) - - IO.puts(output) - if code == 0, do: :ok, else: {:error, []} - end -end - defmodule Genetic.MixProject do use Mix.Project @@ -31,7 +7,7 @@ defmodule Genetic.MixProject do version: "0.1.0", elixir: "~> 1.18", start_permanent: Mix.env() == :prod, - compilers: [:cnif] ++ Mix.compilers(), + compilers: Mix.compilers(), deps: deps() ] end @@ -51,7 +27,8 @@ defmodule Genetic.MixProject do {:gnuplot, "~> 1.22"}, {:benchee, "~> 1.5.0"}, {:exprof, "~> 0.2.4"}, - {:alex, "~> 0.3.2"} + {:alex, "~> 0.3.2"}, + {:rustler, "~> 0.31.0"} ] end end diff --git a/mix.lock b/mix.lock @@ -7,10 +7,13 @@ "exprintf": {:hex, :exprintf, "0.2.1", "b7e895dfb00520cfb7fc1671303b63b37dc3897c59be7cbf1ae62f766a8a0314", [:mix], [], "hexpm", "20a0e8c880be90e56a77fcc82533c5d60c643915c7ce0cc8aa1e06ed6001da28"}, "exprof": {:hex, :exprof, "0.2.4", "13ddc0575a6d24b52e7c6809d2a46e9ad63a4dd179628698cdbb6c1f6e497c98", [:mix], [{:exprintf, "~> 0.2", [hex: :exprintf, repo: "hexpm", optional: false]}], "hexpm", "0884bcb66afc421c75d749156acbb99034cc7db6d3b116c32e36f32551106957"}, "gnuplot": {:hex, :gnuplot, "1.22.270", "541da1d4be2acbcb2a53105a7c2f31d91127811858b522c1a2290ed5844b9624", [:mix], [{:ex_doc, "~> 0.28", [hex: :ex_doc, repo: "hexpm", optional: false]}], "hexpm", "2870982804ac79a93eebf31c07a507a1825bc23c782907da48d653cf970a4d41"}, + "jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"}, "libgraph": {:hex, :libgraph, "0.16.0", "3936f3eca6ef826e08880230f806bfea13193e49bf153f93edcf0239d4fd1d07", [:mix], [], "hexpm", "41ca92240e8a4138c30a7e06466acc709b0cbb795c643e9e17174a178982d6bf"}, "makeup": {:hex, :makeup, "1.2.1", "e90ac1c65589ef354378def3ba19d401e739ee7ee06fb47f94c687016e3713d1", [:mix], [{:nimble_parsec, "~> 1.4", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "d36484867b0bae0fea568d10131197a4c2e47056a6fbe84922bf6ba71c8d17ce"}, "makeup_elixir": {:hex, :makeup_elixir, "1.0.1", "e928a4f984e795e41e3abd27bfc09f51db16ab8ba1aebdba2b3a575437efafc2", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "7284900d412a3e5cfd97fdaed4f5ed389b8f2b4cb49efc0eb3bd10e2febf9507"}, "makeup_erlang": {:hex, :makeup_erlang, "1.0.3", "4252d5d4098da7415c390e847c814bad3764c94a814a0b4245176215615e1035", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "953297c02582a33411ac6208f2c6e55f0e870df7f80da724ed613f10e6706afd"}, "nimble_parsec": {:hex, :nimble_parsec, "1.4.2", "8efba0122db06df95bfaa78f791344a89352ba04baedd3849593bfce4d0dc1c6", [:mix], [], "hexpm", "4b21398942dda052b403bbe1da991ccd03a053668d147d53fb8c4e0efe09c973"}, + "rustler": {:hex, :rustler, "0.31.0", "7e5eefe61e6e6f8901e5aa3de60073d360c6320d9ec363027b0197297b80c46a", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:toml, "~> 0.6", [hex: :toml, repo: "hexpm", optional: false]}], "hexpm", "99e378459bfb9c3bda6d3548b2b3bc6f9ad97f728f76bdbae7bf5c770a4f8abd"}, "statistex": {:hex, :statistex, "1.1.0", "7fec1eb2f580a0d2c1a05ed27396a084ab064a40cfc84246dbfb0c72a5c761e5", [:mix], [], "hexpm", "f5950ea26ad43246ba2cce54324ac394a4e7408fdcf98b8e230f503a0cba9cf5"}, + "toml": {:hex, :toml, "0.7.0", "fbcd773caa937d0c7a02c301a1feea25612720ac3fa1ccb8bfd9d30d822911de", [:mix], [], "hexpm", "0690246a2478c1defd100b0c9b89b4ea280a22be9a7b313a8a058a2408a2fa70"}, } diff --git a/native/random/.cargo/config.toml b/native/random/.cargo/config.toml @@ -0,0 +1,5 @@ +[target.'cfg(target_os = "macos")'] +rustflags = [ + "-C", "link-arg=-undefined", + "-C", "link-arg=dynamic_lookup", +] diff --git a/native/random/.gitignore b/native/random/.gitignore @@ -0,0 +1 @@ +/target diff --git a/native/random/Cargo.lock b/native/random/Cargo.lock @@ -0,0 +1,149 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "random" +version = "0.1.0" +dependencies = [ + "rustler", +] + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + +[[package]] +name = "rustler" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75d458f38f550976d0e4b347ca57241c192019777e46af7af73b27783287088" +dependencies = [ + "lazy_static", + "rustler_codegen", + "rustler_sys", +] + +[[package]] +name = "rustler_codegen" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbd46408f51c0ca6a68dc36aa4f90e3554960bd1b7cc513e6ff2ccad7dd92aff" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "rustler_sys" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff76ba8524729d7c9db2b3e80f2269d1fdef39b5a60624c33fd794797e69b558" +dependencies = [ + "regex", + "unreachable", +] + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unreachable" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" +dependencies = [ + "void", +] + +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" diff --git a/native/random/Cargo.toml b/native/random/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "random" +version = "0.1.0" +authors = [] +edition = "2021" + +[lib] +name = "random" +path = "src/lib.rs" +crate-type = ["cdylib"] + +[dependencies] +rustler = "0.31.0" diff --git a/native/random/README.md b/native/random/README.md @@ -0,0 +1,20 @@ +# NIF for Elixir.Random + +## To build the NIF module: + +- Your NIF will now build along with your project. + +## To load the NIF: + +```elixir +defmodule Random do + use Rustler, otp_app: :genetic, crate: "random" + + # When your NIF is loaded, it will override this function. + def add(_a, _b), do: :erlang.nif_error(:nif_not_loaded) +end +``` + +## Examples + +[This](https://github.com/rusterlium/NifIo) is a complete example of a NIF written in Rust. diff --git a/native/random/src/lib.rs b/native/random/src/lib.rs @@ -0,0 +1,20 @@ +use rustler::NifResult; +use std::sync::Mutex; + +static RNG_STATE: Mutex<(u32, u32, u32)> = Mutex::new((123_456_789, 362_436_069, 52_128_629)); + +#[rustler::nif] +fn xor96() -> NifResult<u32> { + let mut state = RNG_STATE.lock().unwrap(); + + let (ref mut x, ref mut y, ref mut z) = *state; + + let t = *x ^ (*x << 10); + *x = *y; + *y = *z; + *z = (*z ^ (*z >> 26)) ^ (t ^ (t >> 5)); + + Ok(*z) +} + +rustler::init!("Elixir.Genetic.Random", [xor96]); diff --git a/native/tree/.cargo/config.toml b/native/tree/.cargo/config.toml @@ -0,0 +1,5 @@ +[target.'cfg(target_os = "macos")'] +rustflags = [ + "-C", "link-arg=-undefined", + "-C", "link-arg=dynamic_lookup", +] diff --git a/native/tree/.gitignore b/native/tree/.gitignore @@ -0,0 +1 @@ +/target diff --git a/native/tree/Cargo.lock b/native/tree/Cargo.lock @@ -0,0 +1,149 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + +[[package]] +name = "rustler" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75d458f38f550976d0e4b347ca57241c192019777e46af7af73b27783287088" +dependencies = [ + "lazy_static", + "rustler_codegen", + "rustler_sys", +] + +[[package]] +name = "rustler_codegen" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbd46408f51c0ca6a68dc36aa4f90e3554960bd1b7cc513e6ff2ccad7dd92aff" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "rustler_sys" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff76ba8524729d7c9db2b3e80f2269d1fdef39b5a60624c33fd794797e69b558" +dependencies = [ + "regex", + "unreachable", +] + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tree" +version = "0.1.0" +dependencies = [ + "rustler", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unreachable" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" +dependencies = [ + "void", +] + +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" diff --git a/native/tree/Cargo.toml b/native/tree/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "tree" +version = "0.1.0" +authors = [] +edition = "2021" + +[lib] +name = "tree" +path = "src/lib.rs" +crate-type = ["cdylib"] + +[dependencies] +rustler = "0.31.0" diff --git a/native/tree/README.md b/native/tree/README.md @@ -0,0 +1,20 @@ +# NIF for Elixir.Tree + +## To build the NIF module: + +- Your NIF will now build along with your project. + +## To load the NIF: + +```elixir +defmodule Tree do + use Rustler, otp_app: :genetic, crate: "tree" + + # When your NIF is loaded, it will override this function. + def add(_a, _b), do: :erlang.nif_error(:nif_not_loaded) +end +``` + +## Examples + +[This](https://github.com/rusterlium/NifIo) is a complete example of a NIF written in Rust. diff --git a/native/tree/src/lib.rs b/native/tree/src/lib.rs @@ -0,0 +1,6 @@ +#[rustler::nif] +fn add(a: i64, b: i64) -> i64 { + a + b +} + +rustler::init!("Elixir.Genetic.Tree", [add]); diff --git a/scripts/n_queens.exs b/scripts/n_queens.exs @@ -37,9 +37,9 @@ defmodule NQueens do def map_queens(soln) do size = length(soln) - for i <- 1..size do + for i <- 0..(size - 1) do row_string = - for j <- 1..size do + for j <- 0..(size - 1) do if(Enum.at(soln, i) == j) do " Q " else diff --git a/scripts/tetris.exs b/scripts/tetris.exs @@ -44,6 +44,6 @@ defmodule Tetris do def terminate?(_population, generation), do: generation == 5 end -TetrisInterface.start_link("/home/dracuxan/elixir/src/genetic/genetic/games/tetris.bin") +TetrisInterface.start_link("/home/dracuxan/elixir/src/genetic/games/tetris.bin") soln = Genetic.run(Tetris, population_size: 10) IO.puts("\nbest: #{inspect(soln)}") diff --git a/src/random.c b/src/random.c @@ -1,17 +0,0 @@ -#include <erl_nif.h> -#include <inttypes.h> -#include <stdint.h> - -static uint32_t x = 123456789, y = 362436069, z = 52128629; - -static ERL_NIF_TERM xor96(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { - uint32_t t = (x ^ (x << 10)); - x = y; - y = z; - z = (z ^ (z >> 26)) ^ (t ^ (t >> 5)); - - return enif_make_int(env, z); -} - -static ErlNifFunc nif_funcs[] = {{"xor96", 0, xor96}}; -ERL_NIF_INIT(Elixir.Genetic, nif_funcs, NULL, NULL, NULL, NULL);