{"id":13438264,"url":"https://github.com/Martin1887/oxigen","last_synced_at":"2025-03-19T18:32:34.718Z","repository":{"id":54390945,"uuid":"145022068","full_name":"Martin1887/oxigen","owner":"Martin1887","description":"Fast, parallel, extensible and adaptable genetic algorithms framework written in Rust","archived":false,"fork":false,"pushed_at":"2021-07-11T18:15:53.000Z","size":213,"stargazers_count":161,"open_issues_count":2,"forks_count":20,"subscribers_count":7,"default_branch":"master","last_synced_at":"2024-05-29T20:25:22.023Z","etag":null,"topics":["genetic-algorithm","rust"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mpl-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Martin1887.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-08-16T17:55:15.000Z","updated_at":"2024-05-29T09:37:55.000Z","dependencies_parsed_at":"2022-08-13T14:11:06.189Z","dependency_job_id":null,"html_url":"https://github.com/Martin1887/oxigen","commit_stats":null,"previous_names":[],"tags_count":27,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Martin1887%2Foxigen","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Martin1887%2Foxigen/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Martin1887%2Foxigen/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Martin1887%2Foxigen/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Martin1887","download_url":"https://codeload.github.com/Martin1887/oxigen/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244483618,"owners_count":20460147,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["genetic-algorithm","rust"],"created_at":"2024-07-31T03:01:04.086Z","updated_at":"2025-03-19T18:32:34.713Z","avatar_url":"https://github.com/Martin1887.png","language":"Rust","readme":"# oxigen\n\n[![Build Status](https://travis-ci.com/Martin1887/oxigen.svg?branch=master)](https://travis-ci.com/Martin1887/oxigen)\n[![Current Crates.io Version](https://img.shields.io/crates/v/oxigen.svg)](https://crates.io/crates/oxigen)\n\nOxigen is a parallel genetic algorithm framework implemented in Rust. The name comes from the merge of `OXI`dación (Rust translated to Spanish) and `GEN`etic.\n\nThe changes introduced in each version can be found in [CHANGELOG.md](CHANGELOG.md).\n\nTo migrate between major version check the migration guide ([MIGRATE.md](MIGRATE.md)).\n\nOxigen provides the following features:\n\n* Fast and parallel genetic algorithm implementation (it solves the N Queens problem for N=255 in few seconds). For benchmarks view benchmarks section of this file.\n* Customizable mutation and selection rates with constant, linear and quadratic functions according to generations built-in (you can implement your own functions via the `MutationRate` and `SelectionRate` traits).\n* Customizable age unfitness of individuals, with no unfitness, linear and quadratic unfitness with threshold according to generations of the individual built-in (you can implement your own age functions via the `Age` trait).\n* Accumulated `Roulette`, `Tournaments` and `Cup` built-in selection functions (you can implement your own selection functions via the `Selection` trait).\n* `SingleCrossPoint`, `MultiCrossPoint` and `UniformCross` built-in crossover functions (you can implement your own crossover function via the `Crossover` trait).\n* Many built-in survival pressure functions. You can implement your own survival pressure functions via the `SurvivalPressure` trait.\n* `Niches` built-in `PopulationRefitness` function. You can implement your own population refitness functions via the `PopulationRefitness` trait.\n* `SolutionFound`, `Generation` and `Progress` and more built-in stop criteria (you can implement your own stop criteria via the `StopCriterion` trait).\n* `Genotype` trait to define the genotype of your genetic algorithm. Whatever struct can implement the `Genotype` trait under the following restrictions:\n    - It has a `iter` function that returns a `use std::slice::Iter` over its genes.\n    - It has a `into_iter` function that consumes the individual and returns a `use std::vec::IntoIter` over its genes.\n    - It has a `from_iter` function that set the genes from an iterator.\n    - It implements `Display`, `Clone`, `Send` and `Sync`.\n    - It has functions to `generate` a random individual, to `mutate` an individual, to get the `fitness` of an individual and to know if and individual `is_solution` of the problem.\n* Individual's fitness is cached to not do unnecessary recomputations (this can be disabled with `.cache_fitness(false)` if your fitness function is stochastic and so you need to recompute fitness in each generation).\n* Progress statistics can be configured to be printed every certain number of generations to a file.\n* Population individuals with their fitnesses can be configured to be printed every certain number of generations to a file.\n* Specific initial individuals can be inserted in the genetic algorithm execution.\n* Genetic executions can be resumed using the population of the last generation as initial population.\n* Coevolution is possible executing little genetic algorithm re-executions inside the fitness function.\n\n## Optional feature `global_cache`\nThe optional feature `global_cache` adds a `HashMap` saving the evaluation of each individual in the full execution.\n\nThis cache is useful when the evaluation of each individual is expensive, and it complements the individual-based cache already existing in previous versions (if an individual has been evaluated it is not reevaluated unless `cache_fitness` is `false`). In other words, this global cache saves the evaluation of new individuals that are equal to another individual that was evaluated before.\n\nNote that the global cache is not always better, since if the fitness function is cheap the cost of getting and inserting into the cache can be more expensive than it. Take also into account the increase of RAM usage of the global cache.\n\nTo enable the global cache add the feature `global_cache` in the Cargo.toml of your project and set to `true` the `cache_fitness` (always `true` by default) and `global_cache` (`true` by default when the `global_cache` is enabled) properties of your `GeneticExecution`. Example of Cargo.toml:\n```\n[dependencies]\noxigen = { version=\"2.1\", features=[\"global_cache\"] }\n```\n\n\n## Usage\n\nIn your `Cargo.toml` file add the `oxigen` dependency. Oxigen follows the [semver](https://semver.org/) specification for the names of the versions, so major version changes will never break the existent API and the last version should always be used. If a minimum version is required specify that minor version to include that version and all minor versions bigger than it.\n\n```\n[dependencies]\noxigen = \"2\"\n```\n\nTo use `oxigen` `use oxigen::prelude::*` and call the `run` method over a `GeneticExecution` instance overwriting the default hyperparameters and functions following your needs:\n\n```rust\nlet n_queens: u8 = std::env::args()\n    .nth(1)\n    .expect(\"Enter a number between 4 and 255 as argument\")\n    .parse()\n    .expect(\"Enter a number between 4 and 255 as argument\");\n\nlet progress_log = File::create(\"progress.csv\").expect(\"Error creating progress log file\");\nlet population_log = File::create(\"population.txt\").expect(\"Error creating population log file\");\nlet log2 = (f64::from(n_queens) * 4_f64).log2().ceil();\n\nlet population_size = 2_i32.pow(log2 as u32) as usize;\n\nlet (solutions, generation, progress) = GeneticExecution::\u003cu8, QueensBoard\u003e::new()\n    .population_size(population_size)\n    .genotype_size(n_queens as u8)\n    .mutation_rate(Box::new(MutationRates::Linear(SlopeParams {\n        start: f64::from(n_queens) / (8_f64 + 2_f64 * log2) / 100_f64,\n        bound: 0.005,\n        coefficient: -0.0002,\n    })))\n    .selection_rate(Box::new(SelectionRates::Linear(SlopeParams {\n        start: log2 - 2_f64,\n        bound: log2 / 1.5,\n        coefficient: -0.0005,\n    })))\n    .select_function(Box::new(SelectionFunctions::Cup))\n    .age_function(Box::new(AgeFunctions::Quadratic(\n        AgeThreshold(50),\n        AgeSlope(1_f64),\n    )))\n    .progress_log(20, progress_log)\n    .population_log(2000, population_log)\n    .run();\n```\n\nFor a full example visit the [nqueens-oxigen](nqueens-oxigen/src/main.rs) example.\n\nFor more information visit the [documentation](https://docs.rs/oxigen).\n\n\n### Resuming a previous execution\n\nSince version 1.1.0, genetic algorithm executions return the population of the last generation and new genetic executions accept a initial population. This permits to resume previous executions and it also enables coevolution, since little genetic algorithm re-executions can be launched in the fitness function.\n\nIn the following example a execution with 10000 generations is launched and after it is resumed until finding a solution with different rates.\n\n```rust\nlet n_queens: u8 = std::env::args()\n    .nth(1)\n    .expect(\"Enter a number between 4 and 255 as argument\")\n    .parse()\n    .expect(\"Enter a number between 4 and 255 as argument\");\n\nlet progress_log = File::create(\"progress.csv\").expect(\"Error creating progress log file\");\nlet population_log = File::create(\"population.txt\").expect(\"Error creating population log file\");\nlet log2 = (f64::from(n_queens) * 4_f64).log2().ceil();\n\nlet population_size = 2_i32.pow(log2 as u32) as usize;\n\nlet (_solutions, _generation, _progress, population) = GeneticExecution::\u003cu8, QueensBoard\u003e::new()\n    .population_size(population_size)\n    .genotype_size(n_queens as u8)\n    .mutation_rate(Box::new(MutationRates::Linear(SlopeParams {\n        start: f64::from(n_queens) / (8_f64 + 2_f64 * log2) / 100_f64,\n        bound: 0.005,\n        coefficient: -0.0002,\n    })))\n    .selection_rate(Box::new(SelectionRates::Linear(SlopeParams {\n        start: log2 - 2_f64,\n        bound: log2 / 1.5,\n        coefficient: -0.0005,\n    })))\n    .select_function(Box::new(SelectionFunctions::Cup))\n    .age_function(Box::new(AgeFunctions::Quadratic(\n        AgeThreshold(50),\n        AgeSlope(1_f64),\n    )))\n    .stop_criterion(Box::new(StopCriteria::Generation(10000)))\n    .run();\n\nlet (solutions, generation, progress, _population) = GeneticExecution::\u003cu8, QueensBoard\u003e::new()\n    .population_size(population_size)\n    .genotype_size(n_queens as u8)\n    .mutation_rate(Box::new(MutationRates::Linear(SlopeParams {\n        start: f64::from(n_queens) / (8_f64 + 4_f64 * log2) / 100_f64,\n        bound: 0.005,\n        coefficient: -0.0002,\n    })))\n    .selection_rate(Box::new(SelectionRates::Linear(SlopeParams {\n        start: log2 - 4_f64,\n        bound: log2 / 1.5,\n        coefficient: -0.0005,\n    })))\n    .select_function(Box::new(SelectionFunctions::Cup))\n    .age_function(Box::new(AgeFunctions::Quadratic(\n        AgeThreshold(50),\n        AgeSlope(1_f64),\n    )))\n    .population(population)\n    .progress_log(20, progress_log)\n    .population_log(2000, population_log)\n    .run();\n```\n\n\n## Building\n\nTo build oxigen, use `cargo` like for any Rust project:\n\n* `cargo build` to build in debug mode.\n* `cargo build --release` to build with optimizations.\n\nDue to a bug in cargo workspaces with optional features errors appear building from the root folder (see #12, #19), but building inside each project works flawlessly.\n\nTo run benchmarks, you will need a nightly Rust compiler. Uncomment the lines `// #![feature(test)]` and `// mod benchmarks;` from `lib.rs` and then benchmarks can be run using `cargo bench --jobs 1 --all-features`.\n\n\n## Benchmarks\n\nThe following benchmarks have been created to measure the genetic algorithm functions performance:\n\n```\nrunning 29 tests\ntest benchmarks::bench_cross_multi_point_255inds                                                           ... bench:     895,332 ns/iter (+/- 34,409)\ntest benchmarks::bench_cross_single_point_255inds                                                          ... bench:     227,517 ns/iter (+/- 4,802)\ntest benchmarks::bench_cross_uniform_255inds                                                               ... bench:     304,957 ns/iter (+/- 9,421)\ntest benchmarks::bench_distance_255                                                                        ... bench:      41,669 ns/iter (+/- 45)\ntest benchmarks::bench_fitness_1024inds                                                                    ... bench:      14,260 ns/iter (+/- 3,789)\ntest benchmarks::bench_fitness_age_1024inds                                                                ... bench:      32,495 ns/iter (+/- 5,705)\ntest benchmarks::bench_fitness_age_not_cached_1024inds                                                     ... bench:     581,263 ns/iter (+/- 3,988)\ntest benchmarks::bench_fitness_global_cache_1024inds                                                       ... bench:     343,314 ns/iter (+/- 25,763)\ntest benchmarks::bench_fitness_not_cached_1024inds                                                         ... bench:     554,870 ns/iter (+/- 32,916)\ntest benchmarks::bench_generation_run_tournaments_1024inds                                                 ... bench:   4,202,844 ns/iter (+/- 111,604)\ntest benchmarks::bench_get_fitnesses_1024inds                                                              ... bench:         777 ns/iter (+/- 17)\ntest benchmarks::bench_get_solutions_1024inds                                                              ... bench:       2,126 ns/iter (+/- 7)\ntest benchmarks::bench_mutation_1024inds                                                                   ... bench:   1,553,265 ns/iter (+/- 23,022)\ntest benchmarks::bench_refitness_niches_1024inds                                                           ... bench:      29,616 ns/iter (+/- 783)\ntest benchmarks::bench_refitness_none_1024inds                                                             ... bench:      29,756 ns/iter (+/- 3,576)\ntest benchmarks::bench_selection_cup_255inds                                                               ... bench:     357,611 ns/iter (+/- 37,254)\ntest benchmarks::bench_selection_roulette_256inds                                                          ... bench:     141,654 ns/iter (+/- 1,338)\ntest benchmarks::bench_selection_tournaments_256inds                                                       ... bench:     616,907 ns/iter (+/- 50,645)\ntest benchmarks::bench_survival_pressure_children_fight_most_similar_255inds                               ... bench:  17,748,382 ns/iter (+/- 762,602)\ntest benchmarks::bench_survival_pressure_children_fight_parents_255inds                                    ... bench:     139,405 ns/iter (+/- 2,267)\ntest benchmarks::bench_survival_pressure_children_replace_most_similar_255inds                             ... bench:  17,716,416 ns/iter (+/- 739,662)\ntest benchmarks::bench_survival_pressure_children_replace_parents_255inds                                  ... bench:     202,788 ns/iter (+/- 18,250)\ntest benchmarks::bench_survival_pressure_children_replace_parents_and_the_rest_most_similar_255inds        ... bench: 1,387,504,266 ns/iter (+/- 45,914,604)\ntest benchmarks::bench_survival_pressure_children_replace_parents_and_the_rest_random_most_similar_255inds ... bench:   9,389,378 ns/iter (+/- 1,224,136)\ntest benchmarks::bench_survival_pressure_competitive_overpopulation_255inds                                ... bench:  12,803,024 ns/iter (+/- 1,946,079)\ntest benchmarks::bench_survival_pressure_deterministic_overpopulation_255inds                              ... bench:     220,667 ns/iter (+/- 2,790)\ntest benchmarks::bench_survival_pressure_overpopulation_255inds                                            ... bench:  12,243,512 ns/iter (+/- 726,154)\ntest benchmarks::bench_survival_pressure_worst_255inds                                                     ... bench:      20,339 ns/iter (+/- 1,113)\ntest benchmarks::bench_update_progress_1024inds                                                            ... bench:       7,595 ns/iter (+/- 378)\n```\n\nThese benchmarks have been executed in a Intel Core i7 6700K with 16 GB of DDR4\nand a 1024 GB Samsung 970 Evo Plus NVMe SSD in ext4 format in Fedora 33\nwith Linux kernel 5.9.16.\n\nThe difference of performance among the different fitness benchmarks have the following explanations:\n\n * `bench_fitness` measures the performance of a cached execution without cleaning the fitnesses after each bench iteration. This cleaning was not done in previous versions of this README and so it was higher.\n * `bench_mutation` was very much faster in previous versions of this README because an error in the benchmark (empty population).\n * `bench_fitness_age` measures the performance with fitness cached in all bench iterations, so it is slightly slower.\n * Not cached benchmarks measure the performance of not cached executions, with 1 generation individuals in the last case, so the performance is similar but a bit slower for the benchmark that must apply age unfitness.\n * The `children_fight_most_similar` and `children_replace_most_similar` functions have to call the distance function `c * p` times, where `c` is the number of children and `p` is the size of the population (255 and 1024 respectively in the benchmarks).\n * The `overpopulation` and `competitive_overpopulation` functions are similar to `children_replace_most_similar` and `children_fight_most_similar` except to they are compared only with `m` individuals of the population (`m` is bigger than the number of children and smaller than the population size, 768 in the benchmarks). Therefore, 3/4 of the comparisons are done in these benchmarks compared to `children_replace_most_similar` and `children_fight_most_similar`.\n * `children_replace_parents_and_the_rest_random_most_similar` is similar to `children_replace_parents` but, after it, random individuals are chosen to fight against the most similar individual in the population until the population size is the original population size. This means between 0 and 254 times random choosing and distance computation over the entire population in function of the repeated parents in each generation.\n * `children_replace_parents_and_the_rest_most_similar` is like the previous function but it searches the pairs of most similar individuals in the population, which means p\u003csup\u003e2\u003c/sup\u003e distance function calls (2\u003csup\u003e20\u003c/sup\u003e in the benchmark).\n\n\n## Contributing\n\nContributions are absolutely, positively welcome and encouraged! Contributions come in many forms. You could:\n\n  1. Submit a feature request or bug report as an [issue](https://github.com/Martin1887/oxigen/issues).\n  2. Ask for improved documentation as an [issue](https://github.com/Martin1887/oxigen/issues).\n  3. Comment on issues that require feedback.\n  4. Contribute code via [pull requests](https://github.com/Martin1887/oxigen/pulls).\n\nWe aim to keep Oxigen's code quality at the highest level. This means that any code you contribute must be:\n\n  * **Commented:** Public items _must_ be commented.\n  * **Documented:** Exposed items _must_ have rustdoc comments with\n    examples, if applicable.\n  * **Styled:** Your code should be `rustfmt`'d when possible.\n  * **Simple:** Your code should accomplish its task as simply and\n     idiomatically as possible.\n  * **Tested:** You should add (and pass) convincing tests for any functionality you add when it is possible.\n  * **Focused:** Your code should do what it's supposed to do and nothing more.\n\nNote that unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in Oxigen by you shall be licensed under Mozilla Public License 2.0.\n\n\n## Reference\nPozo, Martín \"Oxigen: Fast, parallel, extensible and adaptable genetic algorithms framework written in Rust\".\n\n\n### Bibtex\n```tex\n@misc{\n  title={Oxigen: Fast, parallel, extensible and adaptable genetic algorithms framework written in Rust},\n  author={Pozo, Martín},\n  howpublised = \"\\url{https://github.com/Martin1887/oxigen}\"\n}\n```\n\n## License\n\nOxigen is licensed under Mozilla Public License 2.0.\n","funding_links":[],"categories":["Libraries","库 Libraries","库","人工智能（Artificial Intelligence）"],"sub_categories":["Artificial Intelligence","人工智能 Artificial Intelligence","人工智能","遗传算法（Genetic Algorithms）"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FMartin1887%2Foxigen","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FMartin1887%2Foxigen","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FMartin1887%2Foxigen/lists"}