Skip to content

Commit

Permalink
Minor changes
Browse files Browse the repository at this point in the history
  • Loading branch information
mcrespoae committed May 3, 2024
1 parent ecf7ca7 commit e7df21e
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 20 deletions.
46 changes: 34 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,51 +1,61 @@

# Genetic Algorithm: Solving the One Max Problem in Rust

This project implements a genetic algorithm to solve the One Max Problem, which involves finding a binary string of maximum length where all bits are set to 1.
This project implements a genetic algorithm to solve the One Max Problem, which involves finding a binary string of maximum length where all bits are set to 1. It showcases the use of Rust, multithreading and Make. The codebase has been tested on macOS and Windows 11 platforms.

## Requirements
- [Rust](https://www.rust-lang.org/tools/install).
- [Make](https://www.gnu.org/software/make/) *(Optional)* . For Windows users, consider installing Make via [Chocolatey](https://chocolatey.org/install) using `choco install make `

- [Rust](https://www.rust-lang.org/tools/install).
- [Make](https://www.gnu.org/software/make/) *(Optional)* . For Windows users, consider installing Make via [Chocolatey](https://chocolatey.org/install) using ` choco install make `

## Installation

To set up this project, begin by cloning the repository and navigating to the project directory. Install the dependencies and compile the project by running:

```bash
cargo build
```

Alternatively, you can execute it using Make:

```bash
make build-preview
```

Or *(with the release flag)*:

```bash
make build
```

## Project tree

- `Makefile`: Contains useful project related commands.
- `src`
- `main.rs`: Contains the main entry point for running the algorithm.
- `one_max_genetic_algorithm.rs`: Implementation of the genetic algorithm.
- `results.rs`: Contains a class for storing and computing the results of the genetic algorithm.
- `utils.rs`: Contains helper functions.
- `main.rs`: Contains the main entry point for running the algorithm.
- `one_max_genetic_algorithm.rs`: Implementation of the genetic algorithm.
- `results.rs`: Contains a class for storing and computing the results of the genetic algorithm.
- `utils.rs`: Contains helper functions.
- `tests`
- `test_one_max_genetic_algorithm.rs`: Unit and integration tests for the genetic algorithm.
- `test_results.rs`: Unittests for the Results class.
- `test_utils.rs`: Unittests for the utils file.
- `test_one_max_genetic_algorithm.rs`: Unit and integration tests for the genetic algorithm.
- `test_results.rs`: Unittests for the Results class.
- `test_utils.rs`: Unittests for the utils file.

## External Dependencies

- `tqdm`: Used for displaying progress bars during execution.
- `rand`: Provides random number generation.
- `num_cpus`: Provides the count of CPUs on the current machine to adjust the number of threads in concurrency.
- `assert_approx_eq`: Useful for unittesting.

## Usage

To run the genetic algorithm, execute:

```bash
cargo run
```

You can adjust various parameters in `main.rs` to customize the genetic algorithm's behavior. These parameters include:

- `RUN_TIMES`: Number of times to run the genetic algorithm.
Expand All @@ -65,12 +75,15 @@ If you're using Make, you can also execute the main file using the following com
```bash
make run
```

This command will generate a build with the release flag and run it. To run it without the release flag, you can:

```bash
make run-preview
```

## Algorithm Overview

The genetic algorithm proceeds as follows:

1. **Initialization**: Initialize a population of binary strings randomly.
Expand All @@ -82,29 +95,38 @@ The genetic algorithm proceeds as follows:
7. **Termination**: Repeat steps 2-6 until a termination condition is met, such as reaching a maximum number of generations or achieving a target fitness level.

## Results

The project includes functionality for processing the genetic algorithm with different mutation rates and crossover rates to determine the optimal combination for solving the One Max Problem efficiently; the results are displayed, showing the best mutation rate and crossover rate found during the processing.

## Optimization

The genetic algorithm employs concurrent processing techniques for parallel execution, enhancing runtime performance.

## Testing

The code is equipped with comprehensive unit and integration tests to ensure reliability.

To execute them, use the command:

```bash
cargo test
```

Or, they can be executed using the Make command.

```bash
make test
```
**_Note:_** When running the `cargo test` command, expensive tests are ignored. However, when using `make test`, all tests, including the ignored ones, will be triggered.

**Note:** When running the `cargo test` command, expensive tests are ignored. However, when using `make test`, all tests, including the ignored ones, will be triggered.

## CI/CD

This project features a lightweight CI/CD setup in the form of GitHub workflows. It includes a single job comprising several steps for auditing vulnerabilities, testing, and linting options.

## Python implementation
I've also developed this repository in [Python](https://github.com/mcrespoae/one-max-genetic-algorithm-python), achieving an average execution time that's 3.4 times slower compared to Rust.

I've also developed this repository in [Python](https://github.com/mcrespoae/one-max-genetic-algorithm-python), achieving an average execution time that could be up to 10 times slower compared to Rust.

## Contributors

Expand Down
18 changes: 10 additions & 8 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ mod results;
use results::Results;

// Constants
const RUN_TIMES: usize = 10;
const GENERATIONS: u32 = 500;
const POPULATION_SIZE: usize = 90;
const GENOME_LENGTH: usize = 25;
const RUN_TIMES: usize = 8;
const GENERATIONS: u32 = 400;
const POPULATION_SIZE: usize = 50;
const GENOME_LENGTH: usize = 35;
const SELECT_PARENT_MODE: &str = "tournament"; // tournament or roulette. Tournament usually converges faster and yields better results.
const TARGET_GENERATION_FITNESS: f64 = 0.995; // When a generation is considered fit enough to skip the next iterations. Values close to 1.0 will yield better results.
const TARGET_GENERATION_FITNESS: f64 = 0.998; // When a generation is considered fit enough to skip the next iterations. Values close to 1.0 will yield better results.
const TARGET_PROBLEM_FITNESS: f64 = 0.999; // When the problem is marked as solved. Values very close to 1.0 will not stop the execution.
const MUTATION_RATE_MIN: f64 = 0.001;
const MUTATION_RATE_MAX: f64 = 0.01;
Expand Down Expand Up @@ -86,7 +86,7 @@ pub fn process_genetic_algorithm(mutation_rate_values: &[f64], crossover_rate_va
prev_best_score = score;
}

if prev_local_score < score && i != 0 {
if prev_local_score < (score * 0.9) && i != 0 {
// Skip this loop since the score is not improving
pbar.update(crossover_rate_values.len() - i).unwrap();
break;
Expand Down Expand Up @@ -124,17 +124,19 @@ fn main() {
Population Size: {}
Genome Length: {}
Parent selection mode: {}
Mutation Rate: {:.4} to {:.4}
Crossover Rate: {:.4} to {:.4}",
Mutation Rate: {:.4} to {:.4} with {} steps
Crossover Rate: {:.4} to {:.4} with {} steps",
RUN_TIMES,
GENERATIONS,
POPULATION_SIZE,
GENOME_LENGTH,
SELECT_PARENT_MODE,
mutation_rate_values.first().unwrap_or(&0.0),
mutation_rate_values.last().unwrap_or(&0.0),
mutation_rate_values.len(),
crossover_rate_values.first().unwrap_or(&0.0),
crossover_rate_values.last().unwrap_or(&0.0),
crossover_rate_values.len(),
);
process_genetic_algorithm(&mutation_rate_values, &crossover_rate_values)
}

0 comments on commit e7df21e

Please sign in to comment.