use core::marker::PhantomData;
use super::spec::{Dimensionality, HasSpec, ProblemSpec, Properties, Reference};
use crate::CostFunction;
pub fn step(x: &[f64]) -> f64 {
x.iter().map(|xi| (xi + 0.5).floor().powi(2)).sum()
}
pub struct Step<P = Vec<f64>>(PhantomData<fn() -> P>);
impl<P> Step<P> {
pub const fn new() -> Self {
Self(PhantomData)
}
}
impl<P> Default for Step<P> {
fn default() -> Self {
Self::new()
}
}
pub static STEP_SPEC: ProblemSpec = ProblemSpec {
name: "Step",
dim: Dimensionality::NDimensional { min: 1 },
properties: Properties {
smooth: false,
differentiable: false,
convex: false,
unimodal: false,
separable: true,
scalable: true,
},
references: &[Reference {
citation: "Jamil & Yang (2013)",
title: "A literature survey of benchmark functions for global optimisation problems",
source: "International Journal of Mathematical Modelling and Numerical Optimisation, 4(2), 150–194",
doi: Some("10.1504/IJMMNO.2013.055204"),
url: Some("https://arxiv.org/abs/1308.4008"),
}],
description: "Discontinuous staircase f(x) = Σ ⌊xᵢ + 0.5⌋² (De Jong's step \
function). Global minimum 0 on the central cube xᵢ ∈ [−0.5, \
0.5). Piecewise-constant plateaus make it a target for \
direct-search / derivative-free solvers and a trap for the \
Nelder-Mead simplex, which degenerates on a plateau.",
};
impl<P> HasSpec for Step<P> {
const SPEC: &'static ProblemSpec = &STEP_SPEC;
}
impl CostFunction for Step<Vec<f64>> {
type Param = Vec<f64>;
type Output = f64;
type Error = std::convert::Infallible;
fn cost(&self, x: &Vec<f64>) -> Result<f64, std::convert::Infallible> {
Ok(step(x))
}
}
#[cfg(feature = "nalgebra")]
mod nalgebra_impl {
use super::{Step, step};
use crate::CostFunction;
use nalgebra::DVector;
impl CostFunction for Step<DVector<f64>> {
type Param = DVector<f64>;
type Output = f64;
type Error = std::convert::Infallible;
fn cost(&self, x: &DVector<f64>) -> Result<f64, std::convert::Infallible> {
Ok(step(x.as_slice()))
}
}
}
#[cfg(feature = "ndarray")]
mod ndarray_impl {
use super::{Step, step};
use crate::CostFunction;
use ndarray::Array1;
impl CostFunction for Step<Array1<f64>> {
type Param = Array1<f64>;
type Output = f64;
type Error = std::convert::Infallible;
fn cost(&self, x: &Array1<f64>) -> Result<f64, std::convert::Infallible> {
Ok(step(x.as_slice().expect("Array1 is contiguous")))
}
}
}
#[cfg(feature = "faer")]
mod faer_impl {
use super::Step;
use crate::CostFunction;
use faer::Col;
impl CostFunction for Step<Col<f64>> {
type Param = Col<f64>;
type Output = f64;
type Error = std::convert::Infallible;
fn cost(&self, x: &Col<f64>) -> Result<f64, std::convert::Infallible> {
Ok((0..x.nrows()).map(|i| (x[i] + 0.5).floor().powi(2)).sum())
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn minimum_is_zero_on_central_cube() {
assert_eq!(step(&[0.0, 0.0]), 0.0);
assert_eq!(step(&[-0.4, 0.49]), 0.0);
}
#[test]
fn known_values_at_simple_points() {
assert_eq!(step(&[2.0, 2.0]), 8.0);
assert_eq!(step(&[-1.6, 0.0]), 4.0);
}
#[test]
fn scalable_dimension() {
assert_eq!(step(&[3.0, 3.0, 3.0, 3.0]), 4.0 * 9.0); }
#[test]
fn spec_is_wired_up_via_has_spec_trait() {
let spec = <Step<Vec<f64>> as HasSpec>::SPEC;
assert_eq!(spec.name, "Step");
assert!(!spec.properties.smooth);
assert!(!spec.properties.differentiable);
assert!(spec.properties.separable);
assert!(matches!(spec.dim, Dimensionality::NDimensional { min: 1 }));
assert!(!spec.references.is_empty());
}
}