Source code for odatse.solver.analytical

# SPDX-License-Identifier: MPL-2.0
#
# ODAT-SE -- an open framework for data analysis
# Copyright (C) 2020- The University of Tokyo
#
# This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
# If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.

import numpy as np

import odatse
import odatse.solver.function

[docs] def quadratics(xs: np.ndarray) -> float: """ Quadratic (sphere) function. Parameters ---------- xs : np.ndarray Input array. Returns ------- float The calculated value of the quadratic function. Notes ----- It has one global minimum f(xs)=0 at xs = [0,0,...,0]. """ return np.sum(xs * xs)
[docs] def quartics(xs: np.ndarray) -> float: """ Quartic function with two global minima. Parameters ---------- xs : np.ndarray Input array. Returns ------- float The calculated value of the quartic function. Notes ----- It has two global minima f(xs)=0 at xs = [1,1,...,1] and [0,0,...,0]. It has one saddle point f(0,0,...,0) = 1.0. """ return np.mean((xs - 1.0) ** 2) * np.mean((xs + 1.0) ** 2)
[docs] def ackley(xs: np.ndarray) -> float: """ Ackley's function in arbitrary dimension Parameters ---------- xs : np.ndarray Input array. Returns ------- float The calculated value of Ackley's function. Notes ----- It has one global minimum f(xs)=0 at xs=[0,0,...,0]. It has many local minima. """ a = np.mean(xs ** 2) a = 20 * np.exp(-0.2 * np.sqrt(a)) b = np.cos(2.0 * np.pi * xs) b = np.exp(0.5 * np.sum(b)) return 20.0 + np.exp(1.0) - a - b
[docs] def rosenbrock(xs: np.ndarray) -> float: """ Rosenbrock's function. Parameters ---------- xs : np.ndarray Input array. Returns ------- float The calculated value of Rosenbrock's function. Notes ----- It has one global minimum f(xs) = 0 at xs=[1,1,...,1]. """ return np.sum(100.0 * (xs[1:] - xs[:-1] ** 2) ** 2 + (1.0 - xs[:-1]) ** 2)
[docs] def himmelblau(xs: np.ndarray) -> float: """ Himmelblau's function. Parameters ---------- xs : np.ndarray Input array of shape (2,). Returns ------- float The calculated value of Himmelblau's function. Notes ----- It has four global minima f(xs) = 0 at xs=[3,2], [-2.805118..., 3.131312...], [-3.779310..., -3.2831860], and [3.584428..., -1.848126...]. """ if xs.shape[0] != 2: raise RuntimeError( f"ERROR: himmelblau expects d=2 input, but receives d={xs.shape[0]} one" ) return (xs[0] ** 2 + xs[1] - 11.0) ** 2 + (xs[0] + xs[1] ** 2 - 7.0) ** 2
[docs] def linear_regression_test(xs: np.ndarray) -> float: """ Negative log likelihood of linear regression with Gaussian noise N(0,sigma) y = ax + b trained by xdata = [1, 2, 3, 4, 5, 6] and ydata = [1, 3, 2, 4, 3, 5]. Model parameters (a, b, sigma) are corresponding to xs as the following, a = xs[0], b = xs[1], log(sigma**2) = xs[2] It has a global minimum f(xs) = 1.005071.. at xs = [0.628571..., 0.8, -0.664976...]. Parameters ---------- xs : np.ndarray Input array of model parameters. Returns ------- float The negative log likelihood of the linear regression model. """ if xs.shape[0] != 3: raise RuntimeError( f"ERROR: regression expects d=3 input, but receives d={xs.shape[0]} one" ) xdata = np.array([1, 2, 3, 4, 5, 6]) ydata = np.array([1, 3, 2, 4, 3, 5]) n = len(ydata) return 0.5 * ( n * xs[2] + np.sum((xs[0] * xdata + xs[1] - ydata) ** 2) / np.exp(xs[2]) )
[docs] class Solver(odatse.solver.function.Solver): """Function Solver with pre-defined benchmark functions""" x: np.ndarray fx: float
[docs] def __init__(self, info: odatse.Info) -> None: """ Initialize the solver. Parameters ---------- info: Info Information object containing solver configuration. """ super().__init__(info) self._name = "analytical" function_name = info.solver.get("function_name", "quadratics") if function_name == "quadratics": self.set_function(quadratics) elif function_name == "quartics": self.set_function(quartics) elif function_name == "ackley": self.set_function(ackley) elif function_name == "rosenbrock": self.set_function(rosenbrock) elif function_name == "himmelblau": dimension = self.dimension if int(dimension) != 2: raise RuntimeError( f"ERROR: himmelblau works only with dimension=2 but input is dimension={dimension}" ) self.set_function(himmelblau) elif function_name == "linear_regression_test": dimension = self.dimension if int(dimension) != 3: raise RuntimeError( f"ERROR: regression works only with dimension=2 but input is dimension={dimension}" ) self.set_function(linear_regression_test) else: raise RuntimeError(f"ERROR: Unknown function, {function_name}")