use egobox_ego::OBJECTIVE_FUNCTION_ERROR;
use numpy::{PyArray1, PyArray2};
use pyo3::exceptions::{PyTypeError, PyValueError};
use pyo3::prelude::*;
use pyo3_stub_gen::derive::{gen_stub_pyclass, gen_stub_pyclass_enum, gen_stub_pymethods};
#[gen_stub_pyclass_enum]
#[pyclass(skip_from_py_object, eq, eq_int, rename_all = "SCREAMING_SNAKE_CASE")]
#[derive(Debug, Clone, PartialEq)]
pub enum Recombination {
Hard = 0,
Smooth = 1,
}
impl<'a, 'py> FromPyObject<'a, 'py> for Recombination {
type Error = PyErr;
fn extract(obj: pyo3::Borrowed<'a, 'py, PyAny>) -> Result<Self, Self::Error> {
if let Ok(value) = obj.extract::<PyRef<'py, Self>>() {
return Ok(value.clone());
}
match obj.extract::<u8>() {
Ok(0) => Ok(Self::Hard),
Ok(1) => Ok(Self::Smooth),
Ok(v) => Err(PyValueError::new_err(format!(
"recombination integer value must be in [0, 1], got {v}"
))),
Err(_) => Err(PyTypeError::new_err(
"recombination must be a Recombination enum or an integer in [0, 1]",
)),
}
}
}
#[gen_stub_pyclass]
#[pyclass(skip_from_py_object)]
#[derive(Clone, Default, Debug)]
pub(crate) struct RegressionSpec(pub(crate) u8);
#[gen_stub_pymethods]
#[pymethods]
impl RegressionSpec {
#[classattr]
pub(crate) const ALL: u8 = egobox_moe::RegressionSpec::ALL.bits();
#[classattr]
pub(crate) const CONSTANT: u8 = egobox_moe::RegressionSpec::CONSTANT.bits();
#[classattr]
pub(crate) const LINEAR: u8 = egobox_moe::RegressionSpec::LINEAR.bits();
#[classattr]
pub(crate) const QUADRATIC: u8 = egobox_moe::RegressionSpec::QUADRATIC.bits();
}
#[gen_stub_pyclass]
#[pyclass(skip_from_py_object)]
#[derive(Clone, Default, Debug)]
pub(crate) struct CorrelationSpec(pub(crate) u8);
#[gen_stub_pymethods]
#[pymethods]
impl CorrelationSpec {
#[classattr]
pub(crate) const ALL: u8 = egobox_moe::CorrelationSpec::ALL.bits();
#[classattr]
pub(crate) const SQUARED_EXPONENTIAL: u8 =
egobox_moe::CorrelationSpec::SQUAREDEXPONENTIAL.bits();
#[classattr]
pub(crate) const ABSOLUTE_EXPONENTIAL: u8 =
egobox_moe::CorrelationSpec::ABSOLUTEEXPONENTIAL.bits();
#[classattr]
pub(crate) const MATERN32: u8 = egobox_moe::CorrelationSpec::MATERN32.bits();
#[classattr]
pub(crate) const MATERN52: u8 = egobox_moe::CorrelationSpec::MATERN52.bits();
}
#[gen_stub_pyclass_enum]
#[pyclass(skip_from_py_object, eq, eq_int, rename_all = "SCREAMING_SNAKE_CASE")]
#[derive(Debug, Clone, Copy, PartialEq)]
pub(crate) enum InfillStrategy {
Ei = 1,
Wb2 = 2,
Wb2s = 3,
LogEi = 4,
}
impl<'a, 'py> FromPyObject<'a, 'py> for InfillStrategy {
type Error = PyErr;
fn extract(obj: pyo3::Borrowed<'a, 'py, PyAny>) -> Result<Self, Self::Error> {
if let Ok(value) = obj.extract::<PyRef<'py, Self>>() {
return Ok(*value);
}
match obj.extract::<u8>() {
Ok(1) => Ok(Self::Ei),
Ok(2) => Ok(Self::Wb2),
Ok(3) => Ok(Self::Wb2s),
Ok(4) => Ok(Self::LogEi),
Ok(v) => Err(PyValueError::new_err(format!(
"infill_strategy integer value must be in [1, 4], got {v}"
))),
Err(_) => Err(PyTypeError::new_err(
"infill_strategy must be an InfillStrategy enum or an integer in [1, 4]",
)),
}
}
}
#[gen_stub_pyclass_enum]
#[pyclass(skip_from_py_object, eq, eq_int, rename_all = "SCREAMING_SNAKE_CASE")]
#[derive(Debug, Clone, Copy, PartialEq)]
pub(crate) enum ConstraintStrategy {
Mc = 1,
Utb = 2,
}
impl<'a, 'py> FromPyObject<'a, 'py> for ConstraintStrategy {
type Error = PyErr;
fn extract(obj: pyo3::Borrowed<'a, 'py, PyAny>) -> Result<Self, Self::Error> {
if let Ok(value) = obj.extract::<PyRef<'py, Self>>() {
return Ok(*value);
}
match obj.extract::<u8>() {
Ok(1) => Ok(Self::Mc),
Ok(2) => Ok(Self::Utb),
Ok(v) => Err(PyValueError::new_err(format!(
"cstr_strategy integer value must be in [1, 2], got {v}"
))),
Err(_) => Err(PyTypeError::new_err(
"cstr_strategy must be a ConstraintStrategy enum or an integer in [1, 2]",
)),
}
}
}
#[gen_stub_pyclass_enum]
#[pyclass(skip_from_py_object, eq, eq_int, rename_all = "SCREAMING_SNAKE_CASE")]
#[derive(Debug, Clone, Copy, PartialEq)]
pub(crate) enum QEiStrategy {
Kb = 1,
Kblb = 2,
Kbub = 3,
Clmin = 4,
}
impl<'a, 'py> FromPyObject<'a, 'py> for QEiStrategy {
type Error = PyErr;
fn extract(obj: pyo3::Borrowed<'a, 'py, PyAny>) -> Result<Self, Self::Error> {
if let Ok(value) = obj.extract::<PyRef<'py, Self>>() {
return Ok(*value);
}
match obj.extract::<u8>() {
Ok(1) => Ok(Self::Kb),
Ok(2) => Ok(Self::Kblb),
Ok(3) => Ok(Self::Kbub),
Ok(4) => Ok(Self::Clmin),
Ok(v) => Err(PyValueError::new_err(format!(
"qei strategy integer value must be in [1, 4], got {v}"
))),
Err(_) => Err(PyTypeError::new_err(
"qei strategy must be a QEiStrategy enum or an integer in [1, 4]",
)),
}
}
}
#[gen_stub_pyclass_enum]
#[pyclass(skip_from_py_object, eq, eq_int, rename_all = "SCREAMING_SNAKE_CASE")]
#[derive(Debug, Clone, Copy, PartialEq)]
pub(crate) enum InfillOptimizer {
Cobyla = 1,
Slsqp = 2,
}
impl<'a, 'py> FromPyObject<'a, 'py> for InfillOptimizer {
type Error = PyErr;
fn extract(obj: pyo3::Borrowed<'a, 'py, PyAny>) -> Result<Self, Self::Error> {
if let Ok(value) = obj.extract::<PyRef<'py, Self>>() {
return Ok(*value);
}
match obj.extract::<u8>() {
Ok(1) => Ok(Self::Cobyla),
Ok(2) => Ok(Self::Slsqp),
Ok(v) => Err(PyValueError::new_err(format!(
"infill_optimizer integer value must be in [1, 2], got {v}"
))),
Err(_) => Err(PyTypeError::new_err(
"infill_optimizer must be an InfillOptimizer enum or an integer in [1, 2]",
)),
}
}
}
#[gen_stub_pyclass_enum]
#[pyclass(skip_from_py_object, eq, eq_int, rename_all = "SCREAMING_SNAKE_CASE")]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd)]
pub(crate) enum FeasibleInfillStrategy {
None = 1,
EfiP = 2,
EfiFe = 3,
}
impl<'a, 'py> FromPyObject<'a, 'py> for FeasibleInfillStrategy {
type Error = PyErr;
fn extract(obj: pyo3::Borrowed<'a, 'py, PyAny>) -> Result<Self, Self::Error> {
if let Ok(value) = obj.extract::<PyRef<'py, Self>>() {
return Ok(*value);
}
match obj.extract::<u8>() {
Ok(1) => Ok(Self::None),
Ok(2) => Ok(Self::EfiP),
Ok(3) => Ok(Self::EfiFe),
Ok(v) => Err(PyValueError::new_err(format!(
"feasible_infill_strategy integer value must be in [1, 3], got {v}"
))),
Err(_) => Err(PyTypeError::new_err(
"feasible_infill_strategy must be a FeasibleInfillStrategy enum or an integer in [1, 3]",
)),
}
}
}
#[gen_stub_pyclass_enum]
#[pyclass(skip_from_py_object, eq, eq_int, rename_all = "SCREAMING_SNAKE_CASE")]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd)]
pub(crate) enum FailsafeStrategy {
Rejection = 1,
Imputation = 2,
Viability = 3,
}
impl<'a, 'py> FromPyObject<'a, 'py> for FailsafeStrategy {
type Error = PyErr;
fn extract(obj: pyo3::Borrowed<'a, 'py, PyAny>) -> Result<Self, Self::Error> {
if let Ok(value) = obj.extract::<PyRef<'py, Self>>() {
return Ok(*value);
}
match obj.extract::<u8>() {
Ok(1) => Ok(Self::Rejection),
Ok(2) => Ok(Self::Imputation),
Ok(3) => Ok(Self::Viability),
Ok(v) => Err(PyValueError::new_err(format!(
"failsafe_strategy integer value must be in [1, 3], got {v}"
))),
Err(_) => Err(PyTypeError::new_err(
"failsafe_strategy must be a FailsafeStrategy enum or an integer in [1, 3]",
)),
}
}
}
#[gen_stub_pyclass_enum]
#[pyclass(skip_from_py_object, eq, eq_int, rename_all = "SCREAMING_SNAKE_CASE")]
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
pub(crate) enum Verbose {
Error = 0,
Warning = 1,
Info = 2,
Debug = 3,
Trace = 4,
}
impl<'a, 'py> FromPyObject<'a, 'py> for Verbose {
type Error = PyErr;
fn extract(obj: pyo3::Borrowed<'a, 'py, PyAny>) -> Result<Self, PyErr> {
if let Ok(value) = obj.extract::<PyRef<'py, Self>>() {
return Ok(*value);
}
match obj.extract::<u8>() {
Ok(0) => Ok(Self::Error),
Ok(1) => Ok(Self::Warning),
Ok(2) => Ok(Self::Info),
Ok(3) => Ok(Self::Debug),
Ok(4) => Ok(Self::Trace),
Ok(v) => Err(PyValueError::new_err(format!(
"verbose integer value must be in [0, 4], got {v}"
))),
Err(_) => Err(PyTypeError::new_err(
"verbose must be a Verbose enum or an integer in [0, 4]",
)),
}
}
}
impl From<Verbose> for log::LevelFilter {
fn from(value: Verbose) -> Self {
match value {
Verbose::Error => log::LevelFilter::Error,
Verbose::Warning => log::LevelFilter::Warn,
Verbose::Info => log::LevelFilter::Info,
Verbose::Debug => log::LevelFilter::Debug,
Verbose::Trace => log::LevelFilter::Trace,
}
}
}
#[gen_stub_pyclass_enum]
#[pyclass(skip_from_py_object, eq, eq_int, rename_all = "SCREAMING_SNAKE_CASE")]
#[derive(Clone, Copy, Debug, PartialEq)]
pub(crate) enum XType {
Float = 1,
Int = 2,
Ord = 3,
Enum = 4,
}
impl<'a, 'py> FromPyObject<'a, 'py> for XType {
type Error = PyErr;
fn extract(obj: pyo3::Borrowed<'a, 'py, PyAny>) -> Result<Self, Self::Error> {
if let Ok(value) = obj.extract::<PyRef<'py, Self>>() {
return Ok(*value);
}
match obj.extract::<u8>() {
Ok(1) => Ok(Self::Float),
Ok(2) => Ok(Self::Int),
Ok(3) => Ok(Self::Ord),
Ok(4) => Ok(Self::Enum),
Ok(v) => Err(PyValueError::new_err(format!(
"xtype integer value must be in [1, 4], got {v}"
))),
Err(_) => Err(PyTypeError::new_err(
"xtype must be an XType enum or an integer in [1, 4]",
)),
}
}
}
#[gen_stub_pyclass]
#[pyclass(skip_from_py_object)]
#[derive(FromPyObject, Debug)]
pub(crate) struct XSpec {
#[pyo3(get)]
pub(crate) xtype: XType,
#[pyo3(get)]
pub(crate) xlimits: Vec<f64>,
#[pyo3(get)]
pub(crate) tags: Vec<String>,
}
#[gen_stub_pymethods]
#[pymethods]
impl XSpec {
#[new]
#[pyo3(signature = (xtype, xlimits=vec![], tags=vec![]))]
pub(crate) fn new(xtype: XType, xlimits: Vec<f64>, tags: Vec<String>) -> Self {
XSpec {
xtype,
xlimits,
tags,
}
}
}
#[pyclass(skip_from_py_object, eq, eq_int, rename_all = "SCREAMING_SNAKE_CASE")]
#[gen_stub_pyclass_enum]
#[derive(Debug, Clone, Copy, PartialEq)]
pub(crate) enum SparseMethod {
Fitc = 1,
Vfe = 2,
}
impl<'a, 'py> FromPyObject<'a, 'py> for SparseMethod {
type Error = PyErr;
fn extract(obj: pyo3::Borrowed<'a, 'py, PyAny>) -> Result<Self, Self::Error> {
if let Ok(value) = obj.extract::<PyRef<'py, Self>>() {
return Ok(*value);
}
match obj.extract::<u8>() {
Ok(1) => Ok(Self::Fitc),
Ok(2) => Ok(Self::Vfe),
Ok(v) => Err(PyValueError::new_err(format!(
"sparse method integer value must be in [1, 2], got {v}"
))),
Err(_) => Err(PyTypeError::new_err(
"method must be a SparseMethod enum or an integer in [1, 2]",
)),
}
}
}
#[gen_stub_pyclass]
#[pyclass(skip_from_py_object)]
#[derive(Debug, Clone)]
pub(crate) struct CstrSpec {
pub(crate) inner: egobox_ego::CstrSpec,
}
impl<'a, 'py> FromPyObject<'a, 'py> for CstrSpec {
type Error = PyErr;
fn extract(obj: pyo3::Borrowed<'a, 'py, PyAny>) -> Result<Self, Self::Error> {
if let Ok(spec) = obj.extract::<PyRef<'py, Self>>() {
return Ok(spec.clone());
}
let dict = obj.cast::<pyo3::types::PyDict>()?;
if dict.len() != 1 {
return Err(PyValueError::new_err(
"CstrSpec dict form must contain exactly one key among: leq, geq, eq, btw",
));
}
if let Some(value) = dict.get_item("leq")? {
return Ok(CstrSpec::leq(value.extract()?));
}
if let Some(value) = dict.get_item("geq")? {
return Ok(CstrSpec::geq(value.extract()?));
}
if let Some(value) = dict.get_item("eq")? {
return Ok(CstrSpec::eq(value.extract()?));
}
if let Some(value) = dict.get_item("btw")? {
let (lower, upper): (f64, f64) = value.extract()?;
return Ok(CstrSpec::btw(lower, upper));
}
Err(PyValueError::new_err(
"Unknown CstrSpec dict key. Expected one of: leq, geq, eq, btw",
))
}
}
#[gen_stub_pymethods]
#[pymethods]
impl CstrSpec {
#[staticmethod]
pub fn leq(bound: f64) -> Self {
CstrSpec {
inner: egobox_ego::CstrSpec::Leq(bound),
}
}
#[staticmethod]
pub fn geq(bound: f64) -> Self {
CstrSpec {
inner: egobox_ego::CstrSpec::Geq(bound),
}
}
#[staticmethod]
pub fn eq(value: f64) -> Self {
CstrSpec {
inner: egobox_ego::CstrSpec::Eq(value),
}
}
#[staticmethod]
pub fn btw(lower: f64, upper: f64) -> Self {
CstrSpec {
inner: egobox_ego::CstrSpec::Btw(lower, upper),
}
}
fn __repr__(&self) -> String {
format!("{:?}", self.inner)
}
}
#[gen_stub_pyclass]
#[pyclass(skip_from_py_object)]
#[derive(Debug, Clone)]
pub(crate) struct RunInfo {
#[pyo3(get, set)]
pub(crate) fname: String,
#[pyo3(get, set)]
pub(crate) num: usize,
}
impl<'a, 'py> FromPyObject<'a, 'py> for RunInfo {
type Error = PyErr;
fn extract(obj: pyo3::Borrowed<'a, 'py, PyAny>) -> Result<Self, Self::Error> {
if let Ok(info) = obj.extract::<PyRef<'py, Self>>() {
return Ok(info.clone());
}
let dict = obj.cast::<pyo3::types::PyDict>()?;
let mut info = RunInfo {
fname: "fobj".to_string(),
num: 1,
};
for key_any in dict.keys().iter() {
let key = key_any.extract::<String>()?;
match key.as_str() {
"fname" => info.fname = dict.get_item("fname")?.unwrap().extract()?,
"num" => info.num = dict.get_item("num")?.unwrap().extract()?,
_ => {
return Err(PyValueError::new_err(format!(
"unknown run_info key '{key}'"
)));
}
}
}
Ok(info)
}
}
#[gen_stub_pymethods]
#[pymethods]
impl RunInfo {
#[new]
#[pyo3(signature = (fname="fobj".to_string(), num = 1))]
pub fn new(fname: String, num: usize) -> Self {
RunInfo { fname, num }
}
}
#[gen_stub_pyclass_enum]
#[pyclass(skip_from_py_object, eq, eq_int, rename_all = "SCREAMING_SNAKE_CASE")]
#[derive(Debug, Clone, PartialEq)]
pub(crate) enum ExitStatus {
MaxItersReached = 1,
TargetCostReached = 2,
Interrupt = 3,
SolverConverged = 4,
Timeout = 5,
UnexpectedExit = 6,
ObjectiveFunctionError = 7,
}
impl From<argmin::core::TerminationStatus> for ExitStatus {
fn from(value: argmin::core::TerminationStatus) -> Self {
use argmin::core::{TerminationReason, TerminationStatus};
match value {
TerminationStatus::Terminated(reason) => match reason {
TerminationReason::MaxItersReached => ExitStatus::MaxItersReached,
TerminationReason::TargetCostReached => ExitStatus::TargetCostReached,
TerminationReason::SolverConverged => ExitStatus::SolverConverged,
TerminationReason::Timeout => ExitStatus::Timeout,
TerminationReason::SolverExit(val) if val == OBJECTIVE_FUNCTION_ERROR => {
ExitStatus::ObjectiveFunctionError
}
TerminationReason::SolverExit(_) => unreachable!("Unexpected solver exit reason"),
TerminationReason::Interrupt => ExitStatus::Interrupt,
},
TerminationStatus::NotTerminated => ExitStatus::UnexpectedExit,
}
}
}
#[gen_stub_pyclass]
#[pyclass(skip_from_py_object)]
#[derive(Debug, Clone)]
pub(crate) struct RunStatus {
#[pyo3(get)]
pub(crate) info: RunInfo,
#[pyo3(get)]
pub(crate) exit: ExitStatus,
#[pyo3(get)]
pub(crate) init_doe_size: usize,
#[pyo3(get)]
pub(crate) best_iter: usize,
#[pyo3(get)]
pub(crate) total_iters: usize,
#[pyo3(get)]
pub(crate) elapsed_time: f64,
}
#[gen_stub_pyclass]
#[pyclass(skip_from_py_object)]
#[derive(Debug)]
pub(crate) struct OptimResult {
#[pyo3(get)]
pub(crate) x_opt: Py<PyArray1<f64>>,
#[pyo3(get)]
pub(crate) y_opt: Py<PyArray1<f64>>,
#[pyo3(get)]
pub(crate) x_doe: Py<PyArray2<f64>>,
#[pyo3(get)]
pub(crate) y_doe: Py<PyArray2<f64>>,
}
#[gen_stub_pyclass]
#[pyclass(skip_from_py_object)]
#[derive(Debug)]
pub(crate) struct EgorOptim {
#[pyo3(get)]
pub(crate) result: Py<OptimResult>,
#[pyo3(get)]
pub(crate) status: RunStatus,
}