coco_rs/
suite.rs

1//! COCO benchmark suite.
2
3use coco_sys::coco_suite_t;
4use std::{ffi::CString, ptr};
5
6use crate::{observer::Observer, problem::Problem};
7
8/// Index of a [`Problem`] in a [`Suite`].
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub struct ProblemIdx(pub usize);
11
12/// Index of a function in a [`Suite`].
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub struct FunctionIdx(pub usize);
15
16/// Index of an instance in a [`Suite`].
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub struct InstanceIdx(pub usize);
19
20/// Index of a dimension in a [`Suite`].
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22pub struct DimensionIdx(pub usize);
23
24/// Suites provided by COCO.
25#[derive(Debug, Clone, Copy, PartialEq, Eq)]
26pub enum Name {
27    /// The standard bbob test suite.
28    Bbob,
29    /// The bi-objective test suite.
30    BbobBiobj,
31    /// The extended bi-objective test suite.
32    BbobBiobjExt,
33    /// The large scale test suite.
34    BbobLargescale,
35    /// The constrained test suite.
36    BbobConstrained,
37    /// The mixed-integer test suite.
38    BbobMixint,
39    /// The bi-objective mixed-integer test suite.
40    BbobBiobjMixint,
41    /// The toy test suite.
42    Toy,
43}
44
45impl Name {
46    fn as_str(&self) -> &'static str {
47        match self {
48            Name::Bbob => "bbob",
49            Name::BbobBiobj => "bbob-biobj",
50            Name::BbobBiobjExt => "bbob-biobj-ext",
51            Name::BbobLargescale => "bbob-largescale",
52            Name::BbobConstrained => "bbob-constrained",
53            Name::BbobMixint => "bbob-mixint",
54            Name::BbobBiobjMixint => "bbob-biobj-mixint",
55            Name::Toy => "toy",
56        }
57    }
58}
59
60/// A COCO suite
61pub struct Suite {
62    pub(crate) inner: *mut coco_suite_t,
63    name: CString,
64    instance: CString,
65    options: CString,
66}
67
68impl Clone for Suite {
69    fn clone(&self) -> Self {
70        Suite::new_raw(
71            self.name.clone(),
72            self.instance.clone(),
73            self.options.clone(),
74        )
75        .unwrap()
76    }
77}
78
79unsafe impl Send for Suite {}
80
81impl Suite {
82    /// Instantiates the specified COCO suite.
83    ///
84    /// # suite_instance
85    /// A string used for defining the suite instances. Two ways are supported:
86    /// - "year: YEAR", where YEAR is the year of the BBOB workshop, includes the instances (to be) used in that
87    /// year's workshop;
88    /// - "instances: VALUES", where VALUES are instance numbers from 1 on written as a comma-separated list or a
89    /// range m-n.
90    ///
91    /// # suite_options
92    /// A string of pairs "key: value" used to filter the suite (especially useful for
93    /// parallelizing the experiments). Supported options:
94    /// - "dimensions: LIST", where LIST is the list of dimensions to keep in the suite (range-style syntax is
95    /// not allowed here),
96    /// - "dimension_indices: VALUES", where VALUES is a list or a range of dimension indices (starting from 1) to keep
97    /// in the suite, and
98    /// - "function_indices: VALUES", where VALUES is a list or a range of function indices (starting from 1) to keep
99    /// in the suite, and
100    /// - "instance_indices: VALUES", where VALUES is a list or a range of instance indices (starting from 1) to keep
101    /// in the suite.
102    pub fn new(name: Name, instance: &str, options: &str) -> Option<Suite> {
103        let name = CString::new(name.as_str()).unwrap();
104        let instance = CString::new(instance).unwrap();
105        let options = CString::new(options).unwrap();
106
107        Self::new_raw(name, instance, options)
108    }
109
110    fn new_raw(name: CString, instance: CString, options: CString) -> Option<Suite> {
111        let inner =
112            unsafe { coco_sys::coco_suite(name.as_ptr(), instance.as_ptr(), options.as_ptr()) };
113
114        if inner.is_null() {
115            None
116        } else {
117            Some(Suite {
118                inner,
119                name,
120                instance,
121                options,
122            })
123        }
124    }
125
126    /// Returns the function number in the suite in position function_idx (counting from 0).
127    pub fn function_from_function_index(&self, function_idx: FunctionIdx) -> usize {
128        unsafe { coco_sys::coco_suite_get_function_from_function_index(self.inner, function_idx.0) }
129    }
130
131    /// Returns the dimension number in the suite in position dimension_idx (counting from 0).
132    pub fn dimension_from_dimension_index(&self, dimension_idx: DimensionIdx) -> usize {
133        unsafe {
134            coco_sys::coco_suite_get_dimension_from_dimension_index(self.inner, dimension_idx.0)
135        }
136    }
137
138    /// Returns the instance number in the suite in position instance_idx (counting from 0).
139    pub fn instance_from_instance_index(&self, instance_idx: InstanceIdx) -> usize {
140        unsafe { coco_sys::coco_suite_get_instance_from_instance_index(self.inner, instance_idx.0) }
141    }
142
143    /// Returns the next problem or `None` when the suite completed.
144    pub fn next_problem<'s>(&'s mut self, observer: Option<&mut Observer>) -> Option<Problem<'s>> {
145        let observer = observer.map(|o| o.inner).unwrap_or(ptr::null_mut());
146        let inner = unsafe { coco_sys::coco_suite_get_next_problem(self.inner, observer) };
147
148        if inner.is_null() {
149            return None;
150        }
151
152        unsafe {
153            coco_sys::coco_suite_forget_current_problem(self.inner);
154        }
155
156        Some(Problem::new(inner, self))
157    }
158
159    /// Returns the problem of the suite defined by problem_idx.
160    pub fn problem(&mut self, problem_idx: ProblemIdx) -> Option<Problem> {
161        let inner = unsafe { coco_sys::coco_suite_get_problem(self.inner, problem_idx.0) };
162
163        if inner.is_null() {
164            return None;
165        }
166
167        Some(Problem::new(inner, self))
168    }
169
170    /// Returns the problem for the given function, dimension and instance.
171    ///
172    /// While a suite can contain multiple problems with equal function, dimension and instance, this
173    /// function always returns the first problem in the suite with the given function, dimension and instance
174    /// values. If the given values don't correspond to a problem, the function returns `None`.
175    pub fn problem_by_function_dimension_instance(
176        &mut self,
177        function: usize,
178        dimension: usize,
179        instance: usize,
180    ) -> Option<Problem> {
181        let inner = unsafe {
182            coco_sys::coco_suite_get_problem_by_function_dimension_instance(
183                self.inner,
184                function as usize,
185                dimension as usize,
186                instance as usize,
187            )
188        };
189
190        if inner.is_null() {
191            return None;
192        }
193
194        Some(Problem::new(inner, self))
195    }
196
197    /// Returns the total number of problems in the suite.
198    pub fn number_of_problems(&self) -> usize {
199        unsafe {
200            coco_sys::coco_suite_get_number_of_problems(self.inner)
201                .try_into()
202                .unwrap()
203        }
204    }
205}
206
207impl Drop for Suite {
208    fn drop(&mut self) {
209        unsafe {
210            coco_sys::coco_suite_free(self.inner);
211        }
212    }
213}