l_srtde 0.1.3

Rust implementation of the L-SRTDE algorithm for large-scale global optimization.
Documentation

l_srtde

English | 简体中文

License Rust

A Rust implementation of the L-SRTDE (Large Scale Random Topology Differential Evolution) algorithm for large-scale numerical optimization.

This crate focuses on large-scale global optimization problems and uses rayon to parallelize population evaluation.

Reference

This crate implements the algorithm proposed by V. Stanovov and E. Semenkin. If you use the algorithm or this code in research, cite the original paper:

V. Stanovov and E. Semenkin, "Success Rate-based Adaptive Differential Evolution L-SRTDE for CEC 2024 Competition," 2024 IEEE Congress on Evolutionary Computation (CEC), Yokohama, Japan, 2024, pp. 1-8, doi: 10.1109/CEC60901.2024.10611907.

Features

  • Parallel population evaluation with rayon
  • Success-rate based adaptation of the scaling factor F
  • Linear population size reduction (LPSR)
  • Random-topology strategy for large search spaces
  • Pure Rust implementation
  • C ABI dynamic library for non-Rust callers

Installation

[dependencies]

l_srtde = "0.1.3"

Quick Start

use l_srtde::{Lsrtde, Problem};

struct SphereProblem {
    dim: usize,
}

impl Problem for SphereProblem {
    fn dimension(&self) -> usize {
        self.dim
    }

    fn get_bounds(&self, _index: usize) -> (f64, f64) {
        (-100.0, 100.0)
    }

    fn evaluate(&self, genome: &[f64]) -> f64 {
        genome.iter().map(|x| x * x).sum()
    }
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let problem = SphereProblem { dim: 100 };

    let solver = Lsrtde::new(&problem)
        .with_max_evaluations(500_000)
        .with_seed(42);

    let solution = solver.run()?;

    println!("Best Fitness: {:.6e}", solution.fitness);
    println!("Best Genome: {:.2?}", solution.genome);
    Ok(())
}

Advanced Configuration

let solver = Lsrtde::new(&problem)
    .with_max_evaluations(1_000_000)
    .with_pop_size_multiplier(18)
    .with_memory_size(5)
    .with_seed(12345);

You can also use a callback to monitor progress or stop the search early:

let mut generation = 0;
let solution = solver.run_with_callback(move |solution, evaluations| {
    generation += 1;

    if generation % 10 == 0 {
        println!("Eval: {}, Current Best: {}", evaluations, solution.fitness);
    }

    true
})?;

C ABI / Dynamic Library

The crate can also be built as a dynamic library for C, C++, Python ctypes, and other languages that can load a C ABI library.

Build the release library:

cargo build --release

The dynamic library is written to target/release:

  • Windows: l_srtde.dll
  • Linux: libl_srtde.so
  • macOS: libl_srtde.dylib

Use include/l_srtde.h from C:

#include "l_srtde.h"

static int32_t sphere_batch(
    const double *points,
    size_t point_count,
    size_t dim,
    double *fitness_out,
    void *user_data
) {
    (void)user_data;
    for (size_t i = 0; i < point_count; ++i) {
        double sum = 0.0;
        for (size_t j = 0; j < dim; ++j) {
            double x = points[i * dim + j];
            sum += x * x;
        }
        fitness_out[i] = sum;
    }
    return LSRTDE_OK;
}

The callback receives a row-major batch of point_count * dim doubles and must write point_count fitness values. Return 0 on success and a non-zero value to stop the optimization with LSRTDE_CALLBACK_ERROR.

Example link commands:

# Linux/macOS
cc main.c -Iinclude -Ltarget/release -ll_srtde -o main

# Windows MSVC
cl main.c /I include target\release\l_srtde.dll.lib

Minimal Python ctypes loading:

import ctypes

lib = ctypes.CDLL("./l_srtde.dll")  # use ./libl_srtde.so or ./libl_srtde.dylib on Unix
lib.lsrtde_minimize.restype = ctypes.c_int32

CALLBACK = ctypes.CFUNCTYPE(
    ctypes.c_int32,
    ctypes.POINTER(ctypes.c_double),
    ctypes.c_size_t,
    ctypes.c_size_t,
    ctypes.POINTER(ctypes.c_double),
    ctypes.c_void_p,
)

@CALLBACK
def sphere(points, point_count, dim, fitness_out, user_data):
    for i in range(point_count):
        total = 0.0
        for j in range(dim):
            x = points[i * dim + j]
            total += x * x
        fitness_out[i] = total
    return 0

When a C ABI config field is set to zero, max_evaluations, memory_size, and pop_size_multiplier use the Rust defaults. Set use_seed to 0 for a random seed or non-zero to use seed.

Validation And Budget Semantics

run() and run_with_callback() now return Result<_, LsrtdeError>. The solver rejects invalid configurations before any parallel evaluation starts.

The current validation rules are:

  • dimension() > 0
  • memory_size > 0
  • dimension * pop_size_multiplier must not overflow
  • initial population size must be at least 3
  • every (lower, upper) bound pair must be finite and satisfy lower < upper

with_max_evaluations() is a soft budget, not a hard cap:

  • the initial population is always evaluated in full
  • each generation evaluates a full batch of trial vectors in parallel
  • total objective evaluations can exceed the configured budget
  • the overrun is bounded by at most one current-generation population size

License

This project is licensed under the MIT license. See LICENSE.