Skip to main content

Crate tantale_core

Crate tantale_core 

Source
Expand description

Tantale core contains the foundational abstractions and building blocks used throughout the library. It defines the modeling layer (domains, variables, search spaces, solutions), the optimization layer (objectives, optimizers, stop criteria), and the execution layer (experiments, recorders, checkpointing).

§Main building blocks

Most of these items are re-exported at the crate root for convenience.

§Module map

  • domain: Domain definitions and codomains (objective outputs).
  • sampler: Sampling utilities and distributions.
  • variable: Var to relate objective and optimizer domains.
  • searchspace: Composition of variables into a Searchspace.
  • solution: Solution shapes, identifiers, solution metadata, and computed wrappers.
  • objective: Objective functions, steps, and outcomes.
  • optimizer: Optimizer traits and state metadata.
  • stop: Stop criteria and lifecycle signals.
  • experiment: Execution pipelines and orchestration.
  • recorder: Recording utilities (e.g., CSV).
  • checkpointer: Checkpointing primitives.

§Execution contexts

Tantale provides multiple execution contexts through Runable:

  • MonoExperiment for single-threaded runs.
  • ThrExperiment for multi-threaded runs.
  • [MPIExperiment] for distributed runs (requires the mpi feature).

§Feature flags

  • mpi: Enables MPI-based distributed execution and related utilities.

§Dependencies and integration

Tantale core relies on a few external crates:

  • serde: Most core types implement Serialize and Deserialize to support checkpointing.
  • rand: Domains and samplers use RNG traits to generate valid values.
  • mpi (optional): Distributed experiments are enabled behind the mpi feature flag and expose MPI-specific execution helpers.
  • rayon: Parallel evaluation and orchestration in multi-threaded runs. Used in synchrnous batched optimization, and SpPar utilities.
  • csv: CSV-backed Recorder, with CSVRecorder as the main implementation.
  • rmp-serde: MessagePack serialization used by the default checkpointer. Used by MessagePack for compact checkpointing of experiment state.
  • bincode: Compact binary serialization for checkpointing and transport. Used by MPI-distributed optimization to create messages from Uncomputed and Outcome.
  • bitvec: Efficient bit-level storage in domain and solution utilities. Used by MPI-distributed optimization within IdleWorker to track idle workers.
  • num_cpus: Detects available CPU cores to size thread pools. Used in multi-threaded execution contexts, for asynchrnous multi-threaded optimization, to determine pool size of threads.
  • indexmap: Provides a hashmap preserving iteration order. It used to managa function states, for FIFO discarding of old states to save memory.

§Examples

§Define a searchspace with objective!

The objective! macro builds a Searchspace and wraps a user-defined function into an Objective. The example below mirrors the evaluator pattern used in the test suite.

The objective macro and the function must be defined within another module than the main function, to avoid issues with the generated code. The macro generates a searchspace based on the variables defined in the function body, and an objective that wraps the user function. The searchspace and objective can then be created via generated function: my_module::get_searchspace() and my_module::get_function().

use tantale::core::{Bool, Cat, Int, Nat, Real, Bernoulli, Uniform};
use tantale::macros::{objective, Outcome, CSVWritable};
use serde::{Serialize, Deserialize};

#[derive(Outcome, CSVWritable, Debug, Serialize, Deserialize)]
struct OutExample {
    #[maximize]
    obj: f64,
    info: f64,
}

objective!(
    pub fn example() -> OutExample {
        let _a = [! a | Int(0,100, Uniform) | !];
        let _b = [! b | Nat(0,100, Uniform) | !];
        let _c = [! c | Cat(["relu", "tanh", "sigmoid"], Uniform) | !];
        let _d = [! d | Bool(Bernoulli(0.5)) | !];
        let e = [! e | Real(1000.0,2000.0, Uniform) | !];
        // ... more variables and computation ...
        OutExample{
            obj: e,
            info: [! f | Real(10.0,20.0, Uniform) | !],
        }
    }
);

// The macro expands to helpers like:
// let sp = example::get_searchspace();
// let obj = example::get_function();

§Define a searchspace with objective! for Stepped function

Stepped functions is similar to Objective except that the user-defined function can be evaluated by Step. Hence, the function must maintain an internal state that is updated at each step. To trigger the wrapping within a Stepped objective, the user must defined an Outcome containing a Step.

First, the user defines the function state by implementing the FuncState trait, which requires implementing the save and load methods for checkpointing. Then, the user defines an outcome struct that contains a field of type Step. Finally, the user can use the objective! macro to define the function, and manage the internal state and evaluation step within the function body.

use tantale::core::{FuncState, Bool, Cat, Int, Nat, Real, Bernoulli, Uniform, Step};
use tantale::macros::{objective, Outcome, CSVWritable};
use serde::{Serialize, Deserialize};

#[derive(Outcome, CSVWritable, Debug, Serialize, Deserialize)]
struct OutExample {
    #[maximize]
    obj: f64,
    info: f64,
    #[step]
    step: Step,
}

#[derive(Serialize, Deserialize)]
pub struct FnState {
    pub something: isize,
}

// The user must implement the saving and loading logic of the function state,
// to enable checkpointing of the internal state of the function.
// The input is a folder path where the checkpoint is stored, and the user can decide how to serialize the state (e.g., using rmp_serde or bincode).
impl FuncState for FnState {
   fn save(&self, path: std::path::PathBuf) -> std::io::Result<()>{
       let mut file = std::fs::File::create(path.join("fn_state.mp"))?;
       rmp_serde::encode::write(&mut file, &self).unwrap();
       Ok(())
   }
   fn load(path: std::path::PathBuf) -> std::io::Result<Self> {
       let file_path = path.join("fn_state.mp");
       let file = std::fs::File::open(file_path)?;
       let state = rmp_serde::decode::from_read(file).unwrap();
       Ok(state)
   }
}

objective!(
    pub fn example() -> (OutExample, FnState) {
        let _a = [! a | Int(0,100, Uniform) | !];
        let _b = [! b | Nat(0,100, Uniform) | !];
        let _c = [! c | Cat(["relu", "tanh", "sigmoid"], Uniform) | !];
        let _d = [! d | Bool(Bernoulli(0.5)) | !];
        let e = [! e | Real(1000.0,2000.0, Uniform) | !];
        // ... more variables and computation ...
         
        // Manage the internal state
        let mut state = if let Some(s) = [! STATE !] {
            s
        } else {
            FnState { something: 0 }
        };
        state.something += 1;
        let evalstep = if state.something == 5 {Step::Evaluated} else{Step::Partially(state.something)};
         
        (
            OutExample{
                obj: e,
                info: [! f | Real(10.0,20.0, Uniform) | !],
                step: evalstep,
            },
            state
        )
    }
);

§Batch run with checkpointing (mono)

use tantale::core::{
    CSVRecorder, FolderConfig, MessagePack, Objective, SingleCodomain,
    experiment::{Runable, mono}, stop::Calls,
};
use tantale::algos::BatchRandomSearch;

let sp = my_module::get_searchspace();
let obj = my_module::get_function();
let opt = BatchRandomSearch::new(7);

let stop = Calls::new(50);
let config = FolderConfig::new("run_batch").init();
let rec = CSVRecorder::new(config.clone(), true, true, true, true);
let check = MessagePack::new(config);

let exp = mono(sp, obj, opt, stop, (rec, check));
exp.run();

§Sequential run with threaded execution

use tantale::core::{
    CSVRecorder, FolderConfig, MessagePack, Objective,
    experiment::{Runable, threaded}, stop::Calls,
};
use tantale::algos::RandomSearch;

let sp = my_module::get_searchspace();
let obj = my_module::get_function();
let opt = RandomSearch::new();

let stop = Calls::new(50);
let config = FolderConfig::new("run_seq_threads").init();
let rec = CSVRecorder::new(config.clone(), true, true, true, true);
let check = MessagePack::new(config);

let exp = threaded(sp, obj, opt, stop, (rec, check));
exp.run();

§Multi-fidelity batch run

use tantale::core::{
    CSVRecorder, FolderConfig, MessagePack, experiment::{Runable, mono}, stop::Calls,
};
use tantale::algos::{random_searcatchRandomSearch;

let sp = my_module::get_searchspace();
let obj = my_module::get_function();
let opt = BatchRandomSearch::new(7);

let stop = Calls::new(50);
let config = FolderConfig::new("run_fidelity").init();
let rec = CSVRecorder::new(config.clone(), true, true, true, true);
let check = MessagePack::new(config);

let exp = mono(sp, obj, opt, stop, (rec, check));
exp.run();

Re-exports§

pub use config::FolderConfig;
pub use config::SaverConfig;
pub use domain::Accumulator;
pub use domain::BestAccumulator;
pub use domain::Bool;
pub use domain::Bounded;
pub use domain::Cat;
pub use domain::Codomain;
pub use domain::ConstCodomain;
pub use domain::ConstMultiCodomain;
pub use domain::Constrained;
pub use domain::Cost;
pub use domain::CostCodomain;
pub use domain::CostConstCodomain;
pub use domain::CostConstMultiCodomain;
pub use domain::CostMultiCodomain;
pub use domain::Criteria;
pub use domain::Domain;
pub use domain::Dominate;
pub use domain::FidCriteria;
pub use domain::Grid;
pub use domain::GridDom;
pub use domain::GridInt;
pub use domain::GridNat;
pub use domain::GridReal;
pub use domain::Int;
pub use domain::LinkObj;
pub use domain::LinkOpt;
pub use domain::LinkTyObj;
pub use domain::LinkTyOpt;
pub use domain::Linked;
pub use domain::Mixed;
pub use domain::MixedTypeDom;
pub use domain::Multi;
pub use domain::MultiCodomain;
pub use domain::Nat;
pub use domain::NoDomain;
pub use domain::Onto;
pub use domain::OntoDom;
pub use domain::ParetoAccumulator;
pub use domain::Real;
pub use domain::Single;
pub use domain::SingleCodomain;
pub use domain::TypeAcc;
pub use domain::TypeCodom;
pub use domain::Unit;
pub use sampler::Bernoulli;
pub use sampler::BoolDistribution;
pub use sampler::BoundedDistribution;
pub use sampler::DomainSampler;
pub use sampler::GridDomDistribution;
pub use sampler::Uniform;
pub use variable::var::Var;
pub use solution::BaseSol;
pub use solution::Batch;
pub use solution::Computed;
pub use solution::Fidelity;
pub use solution::FidelitySol;
pub use solution::Id;
pub use solution::IntoComputed;
pub use solution::IntoComputedShape;
pub use solution::Lone;
pub use solution::OutBatch;
pub use solution::Pair;
pub use solution::ParSId;
pub use solution::SId;
pub use solution::SolInfo;
pub use solution::Solution;
pub use solution::SolutionShape;
pub use solution::StepId;
pub use solution::StepSId;
pub use solution::Uncomputed;
pub use solution::Xy;
pub use solution::shape::RawObj;
pub use searchspace::CompShape;
pub use searchspace::OptionCompShape;
pub use searchspace::Searchspace;
pub use searchspace::Sp;
pub use searchspace::SpPar;
pub use crate::objective::EvalStep;
pub use crate::objective::FidOutcome;
pub use crate::objective::FuncWrapper;
pub use crate::objective::Objective;
pub use crate::objective::Outcome;
pub use crate::objective::Step;
pub use crate::objective::Stepped;
pub use crate::objective::outcome::FuncState;
pub use crate::optimizer::BatchOptimizer;
pub use crate::optimizer::BatchSampler;
pub use crate::optimizer::EmptyInfo;
pub use crate::optimizer::OptInfo;
pub use crate::optimizer::OptState;
pub use crate::optimizer::Optimizer;
pub use crate::optimizer::Sampler;
pub use crate::optimizer::SingleOptimizer;
pub use crate::optimizer::SingleSampler;
pub use crate::optimizer::opt::CompBatch;
pub use stop::Calls;
pub use stop::Evaluated;
pub use stop::Stop;
pub use experiment::CompAcc;
pub use experiment::MonoExperiment;
pub use experiment::Pool;
pub use experiment::PoolMode;
pub use experiment::Runable;
pub use experiment::ThrExperiment;
pub use experiment::mono;
pub use experiment::mono_with_pool;
pub use experiment::threaded;
pub use experiment::threaded_with_pool;
pub use recorder::BatchRecorder;
pub use recorder::CSVRecorder;
pub use recorder::CSVWritable;
pub use recorder::NoSaver;
pub use recorder::Recorder;
pub use recorder::SeqRecorder;
pub use checkpointer::Checkpointer;
pub use checkpointer::MessagePack;
pub use checkpointer::MonoCheckpointer;
pub use checkpointer::ThrCheckpointer;
pub use has_trait::HasFidelity;
pub use has_trait::HasId;
pub use has_trait::HasInfo;
pub use has_trait::HasSolInfo;
pub use has_trait::HasStep;
pub use has_trait::HasStepId;
pub use has_trait::HasUncomputed;
pub use has_trait::HasVariables;
pub use has_trait::HasX;
pub use has_trait::HasY;

Modules§

checkpointer
Checkpointing
config
Saver Configuration
domain
Domain
errors
experiment
Experiment orchestration and optimization loops.
has_trait
objective
Objective Functions and Evaluation State
optimizer
Optimizers
recorder
Recorder
sampler
Domain Samplers
searchspace
Searchspace
solution
Solution
stop
Stop criteria for an experiment.
variable
A Var ties together two related Domain types.

Macros§

load
Macro for loading experiments from checkpoints with simplified syntax.

Structs§

GlobalParameters
Serializable snapshot of global counters for checkpointing.

Statics§

OPT_ID
Global counter for optimizer identifiers.
RUN_ID
Global counter for run identifiers.
SOL_ID
Global counter for solution identifiers.