From 9bce0fafd3eb0b46df39718068596bfe1e76be23 Mon Sep 17 00:00:00 2001 From: MatejTomes Date: Mon, 3 Feb 2025 22:56:20 +0100 Subject: [PATCH 1/6] Add Function6D framework derived from Raysect's Function3D --- cherab/core/math/__init__.py | 1 + cherab/core/math/function/__init__.pxd | 2 + cherab/core/math/function/__init__.py | 2 + cherab/core/math/function/float/__init__.pxd | 32 + cherab/core/math/function/float/__init__.py | 32 + .../function/float/function6d/__init__.pxd | 37 + .../function/float/function6d/__init__.py | 36 + .../math/function/float/function6d/arg.pxd | 27 + .../math/function/float/function6d/arg.pyx | 82 ++ .../function/float/function6d/autowrap.pxd | 26 + .../function/float/function6d/autowrap.pyx | 98 +++ .../math/function/float/function6d/base.pxd | 165 ++++ .../math/function/float/function6d/base.pyx | 737 ++++++++++++++++++ .../math/function/float/function6d/blend.pxd | 25 + .../math/function/float/function6d/blend.pyx | 65 ++ .../math/function/float/function6d/cmath.pxd | 61 ++ .../math/function/float/function6d/cmath.pyx | 168 ++++ .../function/float/function6d/constant.pxd | 25 + .../function/float/function6d/constant.pyx | 49 ++ .../float/function6d/tests/__init__.py | 5 + .../float/function6d/tests/test_arg.py | 54 ++ .../float/function6d/tests/test_autowrap.py | 37 + .../float/function6d/tests/test_base.py | 649 +++++++++++++++ .../float/function6d/tests/test_cmath.py | 155 ++++ .../float/function6d/tests/test_constant.py | 35 + 25 files changed, 2605 insertions(+) create mode 100644 cherab/core/math/function/float/__init__.pxd create mode 100644 cherab/core/math/function/float/__init__.py create mode 100644 cherab/core/math/function/float/function6d/__init__.pxd create mode 100644 cherab/core/math/function/float/function6d/__init__.py create mode 100644 cherab/core/math/function/float/function6d/arg.pxd create mode 100644 cherab/core/math/function/float/function6d/arg.pyx create mode 100644 cherab/core/math/function/float/function6d/autowrap.pxd create mode 100644 cherab/core/math/function/float/function6d/autowrap.pyx create mode 100644 cherab/core/math/function/float/function6d/base.pxd create mode 100644 cherab/core/math/function/float/function6d/base.pyx create mode 100644 cherab/core/math/function/float/function6d/blend.pxd create mode 100644 cherab/core/math/function/float/function6d/blend.pyx create mode 100644 cherab/core/math/function/float/function6d/cmath.pxd create mode 100644 cherab/core/math/function/float/function6d/cmath.pyx create mode 100644 cherab/core/math/function/float/function6d/constant.pxd create mode 100644 cherab/core/math/function/float/function6d/constant.pyx create mode 100644 cherab/core/math/function/float/function6d/tests/__init__.py create mode 100644 cherab/core/math/function/float/function6d/tests/test_arg.py create mode 100644 cherab/core/math/function/float/function6d/tests/test_autowrap.py create mode 100644 cherab/core/math/function/float/function6d/tests/test_base.py create mode 100644 cherab/core/math/function/float/function6d/tests/test_cmath.py create mode 100644 cherab/core/math/function/float/function6d/tests/test_constant.py diff --git a/cherab/core/math/__init__.py b/cherab/core/math/__init__.py index 85336afa..567f540d 100644 --- a/cherab/core/math/__init__.py +++ b/cherab/core/math/__init__.py @@ -39,3 +39,4 @@ from .transform import CylindricalTransform, VectorCylindricalTransform from .transform import PeriodicTransform1D, PeriodicTransform2D, PeriodicTransform3D from .transform import VectorPeriodicTransform1D, VectorPeriodicTransform2D, VectorPeriodicTransform3D +from .function import * \ No newline at end of file diff --git a/cherab/core/math/function/__init__.pxd b/cherab/core/math/function/__init__.pxd index 9e5d8343..c4b2bbbe 100644 --- a/cherab/core/math/function/__init__.pxd +++ b/cherab/core/math/function/__init__.pxd @@ -16,6 +16,8 @@ # See the Licence for the specific language governing permissions and limitations # under the Licence. +from cherab.core.math.function cimport float + from raysect.core.math.function.float cimport Function1D, autowrap_function1d from raysect.core.math.function.float cimport Function2D, autowrap_function2d from raysect.core.math.function.float cimport Function3D, autowrap_function3d diff --git a/cherab/core/math/function/__init__.py b/cherab/core/math/function/__init__.py index 952e204c..98241d7a 100644 --- a/cherab/core/math/function/__init__.py +++ b/cherab/core/math/function/__init__.py @@ -16,6 +16,8 @@ # See the Licence for the specific language governing permissions and limitations # under the Licence. +from . import float + from raysect.core.math.function.float import Function1D, Function2D, Function3D from raysect.core.math.function.float import Constant1D, Constant2D, Constant3D from raysect.core.math.function.float import Discrete2DMesh, Interpolator2DMesh diff --git a/cherab/core/math/function/float/__init__.pxd b/cherab/core/math/function/float/__init__.pxd new file mode 100644 index 00000000..78e33ae5 --- /dev/null +++ b/cherab/core/math/function/float/__init__.pxd @@ -0,0 +1,32 @@ +# cython: language_level=3 + +# Copyright (c) 2014-2023, Dr Alex Meakins, Raysect Project +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the Raysect Project nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +from cherab.core.math.function.float.function6d cimport * diff --git a/cherab/core/math/function/float/__init__.py b/cherab/core/math/function/float/__init__.py new file mode 100644 index 00000000..330f73c8 --- /dev/null +++ b/cherab/core/math/function/float/__init__.py @@ -0,0 +1,32 @@ +# cython: language_level=3 + +# Copyright (c) 2014-2023, Dr Alex Meakins, Raysect Project +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the Raysect Project nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +from .function6d import * diff --git a/cherab/core/math/function/float/function6d/__init__.pxd b/cherab/core/math/function/float/function6d/__init__.pxd new file mode 100644 index 00000000..2e3336e9 --- /dev/null +++ b/cherab/core/math/function/float/function6d/__init__.pxd @@ -0,0 +1,37 @@ +# cython: language_level=3 + +# Copyright (c) 2014-2023, Dr Alex Meakins, Raysect Project +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the Raysect Project nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +from cherab.core.math.function.float.function6d.base cimport Function6D +from cherab.core.math.function.float.function6d.constant cimport Constant6D +from cherab.core.math.function.float.function6d.blend cimport Blend6D +from cherab.core.math.function.float.function6d.autowrap cimport autowrap_function6d +from cherab.core.math.function.float.function6d.arg cimport Arg6D +from cherab.core.math.function.float.function6d.cmath cimport * diff --git a/cherab/core/math/function/float/function6d/__init__.py b/cherab/core/math/function/float/function6d/__init__.py new file mode 100644 index 00000000..0ababca8 --- /dev/null +++ b/cherab/core/math/function/float/function6d/__init__.py @@ -0,0 +1,36 @@ +# cython: language_level=3 + +# Copyright (c) 2014-2023, Dr Alex Meakins, Raysect Project +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the Raysect Project nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +from .base import Function6D +from .constant import Constant6D +from .blend import Blend6D +from .arg import Arg6D +from .cmath import * diff --git a/cherab/core/math/function/float/function6d/arg.pxd b/cherab/core/math/function/float/function6d/arg.pxd new file mode 100644 index 00000000..151eea65 --- /dev/null +++ b/cherab/core/math/function/float/function6d/arg.pxd @@ -0,0 +1,27 @@ +# cython: language_level=3 + +# Copyright 2016-2025 Euratom +# Copyright 2016-2025 United Kingdom Atomic Energy Authority +# Copyright 2016-2025 Centro de Investigaciones Energéticas, Medioambientales y Tecnológicas +# +# Licensed under the EUPL, Version 1.1 or – as soon they will be approved by the +# European Commission - subsequent versions of the EUPL (the "Licence"); +# You may not use this work except in compliance with the Licence. +# You may obtain a copy of the Licence at: +# +# https://joinup.ec.europa.eu/software/page/eupl5 +# +# Unless required by applicable law or agreed to in writing, software distributed +# under the Licence is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR +# CONDITIONS OF ANY KIND, either express or implied. +# +# See the Licence for the specific language governing permissions and limitations +# under the Licence. + +from cherab.core.math.function.float.function6d.base cimport Function6D + +cdef enum ArgLabel: + X, Y, Z, U, W, V + +cdef class Arg6D(Function6D): + cdef ArgLabel _argument diff --git a/cherab/core/math/function/float/function6d/arg.pyx b/cherab/core/math/function/float/function6d/arg.pyx new file mode 100644 index 00000000..c4883e0e --- /dev/null +++ b/cherab/core/math/function/float/function6d/arg.pyx @@ -0,0 +1,82 @@ +# cython: language_level=3 + +# Copyright 2016-2025 Euratom +# Copyright 2016-2025 United Kingdom Atomic Energy Authority +# Copyright 2016-2025 Centro de Investigaciones Energéticas, Medioambientales y Tecnológicas +# +# Licensed under the EUPL, Version 1.1 or – as soon they will be approved by the +# European Commission - subsequent versions of the EUPL (the "Licence"); +# You may not use this work except in compliance with the Licence. +# You may obtain a copy of the Licence at: +# +# https://joinup.ec.europa.eu/software/page/eupl5 +# +# Unless required by applicable law or agreed to in writing, software distributed +# under the Licence is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR +# CONDITIONS OF ANY KIND, either express or implied. +# +# See the Licence for the specific language governing permissions and limitations +# under the Licence. + +from cherab.core.math.function.float.function6d.base cimport Function6D + + +cdef class Arg6D(Function6D): + """ + Returns one of the arguments the function is passed, unmodified + + This is used to pass coordinates through to other functions in the + function framework which expect a Function6D object. + + Valid options for argument are "x", "y", "z", "u", "w", or "v". + + >>> argx = Arg6D("x") + >>> argx(2, 3, 5, 7, 11, 13) + 2.0 + >>> argy = Arg6D("y") + >>> argy(2, 3, 5, 7, 11, 13) + 3.0 + >>> argz = Arg6D("z") + >>> argz(2, 3, 5, 7, 11, 13) + 5.0 + >>> argu = Arg6D("u") + >>> argu(2, 3, 5, 7, 11, 13) + 7.0 + >>> argw = Arg6D("w") + >>> argw(2, 3, 5, 7, 11, 13) + 11.0 + >>> argv = Arg6D("v") + >>> argv(2, 3, 5, 7, 11, 13) + 13.0 + + :param str argument: either "x", "y", "z", "u", "w", or "v", the argument to return + """ + def __init__(self, object argument): + if argument == "x": + self._argument = X + elif argument == "y": + self._argument = Y + elif argument == "z": + self._argument = Z + elif argument == "u": + self._argument = U + elif argument == "w": + self._argument = W + elif argument == "v": + self._argument = V + else: + raise ValueError("The argument to Arg6D must be either 'x', 'y', 'z', 'u', 'w' or 'v'") + + cdef double evaluate(self, double x, double y, double z, double u, double w, double v) except? -1e999: + if self._argument == X: + return x + elif self._argument == Y: + return y + elif self._argument == Z: + return z + elif self._argument == U: + return u + elif self._argument == W: + return w + else: # V + return v diff --git a/cherab/core/math/function/float/function6d/autowrap.pxd b/cherab/core/math/function/float/function6d/autowrap.pxd new file mode 100644 index 00000000..a630393c --- /dev/null +++ b/cherab/core/math/function/float/function6d/autowrap.pxd @@ -0,0 +1,26 @@ +# cython: language_level=3 + +# Copyright 2016-2025 Euratom +# Copyright 2016-2025 United Kingdom Atomic Energy Authority +# Copyright 2016-2025 Centro de Investigaciones Energéticas, Medioambientales y Tecnológicas +# +# Licensed under the EUPL, Version 1.1 or – as soon they will be approved by the +# European Commission - subsequent versions of the EUPL (the "Licence"); +# You may not use this work except in compliance with the Licence. +# You may obtain a copy of the Licence at: +# +# https://joinup.ec.europa.eu/software/page/eupl5 +# +# Unless required by applicable law or agreed to in writing, software distributed +# under the Licence is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR +# CONDITIONS OF ANY KIND, either express or implied. +# +# See the Licence for the specific language governing permissions and limitations +# under the Licence. + +from cherab.core.math.function.float.function6d.base cimport Function6D + +cdef class PythonFunction6D(Function6D): + cdef public object function + +cdef Function6D autowrap_function6d(object obj) diff --git a/cherab/core/math/function/float/function6d/autowrap.pyx b/cherab/core/math/function/float/function6d/autowrap.pyx new file mode 100644 index 00000000..9026a0d8 --- /dev/null +++ b/cherab/core/math/function/float/function6d/autowrap.pyx @@ -0,0 +1,98 @@ +# cython: language_level=3 + +# Copyright 2016-2025 Euratom +# Copyright 2016-2025 United Kingdom Atomic Energy Authority +# Copyright 2016-2025 Centro de Investigaciones Energéticas, Medioambientales y Tecnológicas +# +# Licensed under the EUPL, Version 1.1 or – as soon they will be approved by the +# European Commission - subsequent versions of the EUPL (the "Licence"); +# You may not use this work except in compliance with the Licence. +# You may obtain a copy of the Licence at: +# +# https://joinup.ec.europa.eu/software/page/eupl5 +# +# Unless required by applicable law or agreed to in writing, software distributed +# under the Licence is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR +# CONDITIONS OF ANY KIND, either express or implied. +# +# See the Licence for the specific language governing permissions and limitations +# under the Licence. + +import numbers +from cherab.core.math.function.float.function6d.base cimport Function6D +from cherab.core.math.function.float.function6d.constant cimport Constant6D +from raysect.core.math.function.base cimport Function + + +cdef class PythonFunction6D(Function6D): + """ + Wraps a python callable object with a Function6D object. + + This class allows a python object to interact with cython code that requires + a Function6D object. The python object must implement __call__() expecting + six arguments. + + This class is intended to be used to transparently wrap python objects that + are passed via constructors or methods into cython optimised code. It is not + intended that the users should need to directly interact with these wrapping + objects. Constructors and methods expecting a Function6D object should be + designed to accept a generic python object and then test that object to + determine if it is an instance of Function6D. If the object is not a + Function6D object it should be wrapped using this class for internal use. + + See also: autowrap_function6d() + """ + + def __init__(self, object function): + self.function = function + + cdef double evaluate(self, double x, double y, double z, double u, double w, double v) except? -1e999: + return self.function(x, y, z, u, w, v) + + +cdef Function6D autowrap_function6d(object obj): + """ + Automatically wraps the supplied python object in a PythonFunction6D or Constant6D object. + + If this function is passed a valid Function6D object, then the Function6D + object is simply returned without wrapping. + + If this function is passed a numerical scalar (int or float), a Constant6D + object is returned. + + This convenience function is provided to simplify the handling of Function6D + and python callable objects in constructors, functions and setters. + """ + + if isinstance(obj, Function6D): + return obj + elif isinstance(obj, Function): + raise TypeError('A Function6D object is required.') + elif isinstance(obj, numbers.Real): + return Constant6D(obj) + else: + return PythonFunction6D(obj) + + +def _autowrap_function6d(obj): + """Expose cython function for testing.""" + return autowrap_function6d(obj) + + +cdef inline bint is_callable(object f): + """ + Tests if an object is a python callable or a Function6D object. + """ + print(f"Checking if callable:", f) + print(f"isinstance(Function6D):", isinstance(f, Function6D)) + print(f"isinstance(Function):", isinstance(f, Function)) + print(f"callable():", callable(f)) + + if isinstance(f, Function6D): + return True + + # other function classes are incompatible + if isinstance(f, Function): + return False + + return callable(f) diff --git a/cherab/core/math/function/float/function6d/base.pxd b/cherab/core/math/function/float/function6d/base.pxd new file mode 100644 index 00000000..4f482baa --- /dev/null +++ b/cherab/core/math/function/float/function6d/base.pxd @@ -0,0 +1,165 @@ +# cython: language_level=3 + +# Copyright 2016-2025 Euratom +# Copyright 2016-2025 United Kingdom Atomic Energy Authority +# Copyright 2016-2025 Centro de Investigaciones Energéticas, Medioambientales y Tecnológicas +# +# Licensed under the EUPL, Version 1.1 or – as soon they will be approved by the +# European Commission - subsequent versions of the EUPL (the "Licence"); +# You may not use this work except in compliance with the Licence. +# You may obtain a copy of the Licence at: +# +# https://joinup.ec.europa.eu/software/page/eupl5 +# +# Unless required by applicable law or agreed to in writing, software distributed +# under the Licence is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR +# CONDITIONS OF ANY KIND, either express or implied. +# +# See the Licence for the specific language governing permissions and limitations +# under the Licence. + +from raysect.core.math.function.base cimport Function +from raysect.core.math.function.float.base cimport FloatFunction + + +cdef class Function6D(FloatFunction): + cdef double evaluate(self, double x, double y, double z, double u, double w, double v) except? -1e999 + + +cdef class AddFunction6D(Function6D): + cdef Function6D _function1, _function2 + + +cdef class SubtractFunction6D(Function6D): + cdef Function6D _function1, _function2 + + +cdef class MultiplyFunction6D(Function6D): + cdef Function6D _function1, _function2 + + +cdef class DivideFunction6D(Function6D): + cdef Function6D _function1, _function2 + + +cdef class ModuloFunction6D(Function6D): + cdef Function6D _function1, _function2 + + +cdef class PowFunction6D(Function6D): + cdef Function6D _function1, _function2 + + +cdef class AbsFunction6D(Function6D): + cdef Function6D _function + + +cdef class EqualsFunction6D(Function6D): + cdef Function6D _function1, _function2 + + +cdef class NotEqualsFunction6D(Function6D): + cdef Function6D _function1, _function2 + + +cdef class LessThanFunction6D(Function6D): + cdef Function6D _function1, _function2 + + +cdef class GreaterThanFunction6D(Function6D): + cdef Function6D _function1, _function2 + + +cdef class LessEqualsFunction6D(Function6D): + cdef Function6D _function1, _function2 + + +cdef class GreaterEqualsFunction6D(Function6D): + cdef Function6D _function1, _function2 + + +cdef class AddScalar6D(Function6D): + cdef double _value + cdef Function6D _function + + +cdef class SubtractScalar6D(Function6D): + cdef double _value + cdef Function6D _function + + +cdef class MultiplyScalar6D(Function6D): + cdef double _value + cdef Function6D _function + + +cdef class DivideScalar6D(Function6D): + cdef double _value + cdef Function6D _function + + +cdef class ModuloScalarFunction6D(Function6D): + cdef double _value + cdef Function6D _function + + +cdef class ModuloFunctionScalar6D(Function6D): + cdef double _value + cdef Function6D _function + + +cdef class PowScalarFunction6D(Function6D): + cdef double _value + cdef Function6D _function + + +cdef class PowFunctionScalar6D(Function6D): + cdef double _value + cdef Function6D _function + + +cdef class EqualsScalar6D(Function6D): + cdef double _value + cdef Function6D _function + + +cdef class NotEqualsScalar6D(Function6D): + cdef double _value + cdef Function6D _function + + +cdef class LessThanScalar6D(Function6D): + cdef double _value + cdef Function6D _function + + +cdef class GreaterThanScalar6D(Function6D): + cdef double _value + cdef Function6D _function + + +cdef class LessEqualsScalar6D(Function6D): + cdef double _value + cdef Function6D _function + + +cdef class GreaterEqualsScalar6D(Function6D): + cdef double _value + cdef Function6D _function + + +cdef inline bint is_callable(object f): + """ + Tests if an object is a python callable or a Function6D object. + + :param object f: Object to test. + :return: True if callable, False otherwise. + """ + if isinstance(f, Function6D): + return True + + # other function classes are incompatible + if isinstance(f, Function): + return False + + return callable(f) \ No newline at end of file diff --git a/cherab/core/math/function/float/function6d/base.pyx b/cherab/core/math/function/float/function6d/base.pyx new file mode 100644 index 00000000..fd653cd9 --- /dev/null +++ b/cherab/core/math/function/float/function6d/base.pyx @@ -0,0 +1,737 @@ +# cython: language_level=3 + +# Copyright 2016-2025 Euratom +# Copyright 2016-2025 United Kingdom Atomic Energy Authority +# Copyright 2016-2025 Centro de Investigaciones Energéticas, Medioambientales y Tecnológicas +# +# Licensed under the EUPL, Version 1.1 or – as soon they will be approved by the +# European Commission - subsequent versions of the EUPL (the "Licence"); +# You may not use this work except in compliance with the Licence. +# You may obtain a copy of the Licence at: +# +# https://joinup.ec.europa.eu/software/page/eupl5 +# +# Unless required by applicable law or agreed to in writing, software distributed +# under the Licence is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR +# CONDITIONS OF ANY KIND, either express or implied. +# +# See the Licence for the specific language governing permissions and limitations +# under the Licence. + +import numbers +from cpython.object cimport Py_LT, Py_EQ, Py_GT, Py_LE, Py_NE, Py_GE +cimport cython +from libc.math cimport floor +from .autowrap cimport autowrap_function6d + + +cdef class Function6D(FloatFunction): + """ + Cython optimised class for representing an arbitrary 6D function returning a float. + + Using __call__() in cython is slow. This class provides an overloadable + cython cdef evaluate() method which has much less overhead than a python + function call. + + For use in cython code only, this class cannot be extended via python. + + To create a new function object, inherit this class and implement the + evaluate() method. The new function object can then be used with any code + that accepts a function object. + """ + + cdef double evaluate(self, double x, double y, double z, double u, double w, double v) except? -1e999: + raise NotImplementedError("The evaluate() method has not been implemented.") + + def __call__(self, double x, double y, double z, double u, double w, double v): + """ Evaluate the function f(x, y, z, u, w, v) + + :param float x: function parameter x + :param float y: function parameter y + :param float z: function parameter z + :param float u: function parameter u + :param float w: function parameter w + :param float v: function parameter v + :rtype: float + """ + return self.evaluate(x, y, z, u, w, v) + def __add__(self, object b): + if is_callable(b): + # a() + b() + return AddFunction6D(self, b) + elif isinstance(b, numbers.Real): + # a() + B -> B + a() + return AddScalar6D( b, self) + return NotImplemented + + def __radd__(self, object a): + return self.__add__(a) + + def __sub__(self, object b): + if is_callable(b): + # a() - b() + return SubtractFunction6D(self, b) + elif isinstance(b, numbers.Real): + # a() - B -> -B + a() + return AddScalar6D(-( b), self) + return NotImplemented + + def __rsub__(self, object a): + if is_callable(a): + # a() - b() + return SubtractFunction6D(a, self) + elif isinstance(a, numbers.Real): + # A - b() + return SubtractScalar6D( a, self) + return NotImplemented + + def __mul__(self, object b): + if is_callable(b): + # a() * b() + return MultiplyFunction6D(self, b) + elif isinstance(b, numbers.Real): + # a() * B -> B * a() + return MultiplyScalar6D( b, self) + return NotImplemented + + def __rmul__(self, object a): + return self.__mul__(a) + + @cython.cdivision(True) + def __truediv__(self, object b): + cdef double v + if is_callable(b): + # a() / b() + return DivideFunction6D(self, b) + elif isinstance(b, numbers.Real): + # a() / B -> 1/B * a() + v = b + if v == 0.0: + raise ZeroDivisionError("Scalar used as the denominator of the division is zero valued.") + return MultiplyScalar6D(1/v, self) + return NotImplemented + + @cython.cdivision(True) + def __rtruediv__(self, object a): + if is_callable(a): + # a() / b() + return DivideFunction6D(a, self) + elif isinstance(a, numbers.Real): + # A / b() + return DivideScalar6D( a, self) + return NotImplemented + + def __mod__(self, object b): + cdef double v + if is_callable(b): + # a() % b() + return ModuloFunction6D(self, b) + elif isinstance(b, numbers.Real): + # a() % B + v = b + if v == 0.0: + raise ZeroDivisionError("Scalar used as the divisor of the division is zero valued.") + return ModuloFunctionScalar6D(self, v) + return NotImplemented + + def __rmod__(self, object a): + if is_callable(a): + # a() % b() + return ModuloFunction6D(a, self) + elif isinstance(a, numbers.Real): + # A % b() + return ModuloScalarFunction6D( a, self) + return NotImplemented + + def __neg__(self): + return MultiplyScalar6D(-1, self) + + def __pow__(self, object b, object c): + if c is not None: + # Optimised implementation of pow(a, b, c) not available: fall back + # to general implementation + return PowFunction6D(self, b) % c + if is_callable(b): + # a() ** b() + return PowFunction6D(self, b) + elif isinstance(b, numbers.Real): + # a() ** b + return PowFunctionScalar6D(self, b) + return NotImplemented + + def __rpow__(self, object a, object c): + if c is not None: + # Optimised implementation of pow(a, b, c) not available: fall back + # to general implementation + return PowFunction6D(a, self) % c + if is_callable(a): + # a() ** b() + return PowFunction6D(a, self) + elif isinstance(a, numbers.Real): + # A ** b() + return PowScalarFunction6D( a, self) + return NotImplemented + + def __abs__(self): + return AbsFunction6D(self) + + def __richcmp__(self, object other, int op): + if is_callable(other): + if op == Py_EQ: + return EqualsFunction6D(self, other) + if op == Py_NE: + return NotEqualsFunction6D(self, other) + if op == Py_LT: + return LessThanFunction6D(self, other) + if op == Py_GT: + return GreaterThanFunction6D(self, other) + if op == Py_LE: + return LessEqualsFunction6D(self, other) + if op == Py_GE: + return GreaterEqualsFunction6D(self, other) + if isinstance(other, numbers.Real): + if op == Py_EQ: + return EqualsScalar6D( other, self) + if op == Py_NE: + return NotEqualsScalar6D( other, self) + if op == Py_LT: + # f() < K -> K > f + return GreaterThanScalar6D( other, self) + if op == Py_GT: + # f() > K -> K < f + return LessThanScalar6D( other, self) + if op == Py_LE: + # f() <= K -> K >= f + return GreaterEqualsScalar6D( other, self) + if op == Py_GE: + # f() >= K -> K <= f + return LessEqualsScalar6D( other, self) + return NotImplemented + + +cdef class AddFunction6D(Function6D): + """ + A Function6D class that implements the addition of the results of two Function6D objects: f1() + f2() + + This class is not intended to be used directly, but rather returned as the result of an __add__() call on a + Function6D object. + + :param function1: A Function6D object. + :param function2: A Function6D object. + """ + + def __init__(self, object function1, object function2): + self._function1 = autowrap_function6d(function1) + self._function2 = autowrap_function6d(function2) + + cdef double evaluate(self, double x, double y, double z, double u, double w, double v) except? -1e999: + return self._function1.evaluate(x, y, z, u, w, v) + self._function2.evaluate(x, y, z, u, w, v) + + +cdef class SubtractFunction6D(Function6D): + """ + A Function6D class that implements the subtraction of the results of two Function6D objects: f1() - f2() + + This class is not intended to be used directly, but rather returned as the result of a __sub__() call on a + Function6D object. + + :param function1: A Function6D object. + :param function2: A Function6D object. + """ + + def __init__(self, object function1, object function2): + self._function1 = autowrap_function6d(function1) + self._function2 = autowrap_function6d(function2) + + cdef double evaluate(self, double x, double y, double z, double u, double w, double v) except? -1e999: + return self._function1.evaluate(x, y, z, u, w, v) - self._function2.evaluate(x, y, z, u, w, v) + + +cdef class MultiplyFunction6D(Function6D): + """ + A Function6D class that implements the multiplication of the results of two Function6D objects: f1() * f2() + + This class is not intended to be used directly, but rather returned as the result of a __mul__() call on a + Function6D object. + + :param function1: A Function6D object. + :param function2: A Function6D object. + """ + + def __init__(self, function1, function2): + self._function1 = autowrap_function6d(function1) + self._function2 = autowrap_function6d(function2) + + cdef double evaluate(self, double x, double y, double z, double u, double w, double v) except? -1e999: + return self._function1.evaluate(x, y, z, u, w, v) * self._function2.evaluate(x, y, z, u, w, v) + + +cdef class DivideFunction6D(Function6D): + """ + A Function6D class that implements the division of the results of two Function6D objects: f1() / f2() + + This class is not intended to be used directly, but rather returned as the result of a __truediv__() call on a + Function6D object. + + :param function1: A Function6D object. + :param function2: A Function6D object. + """ + + def __init__(self, function1, function2): + self._function1 = autowrap_function6d(function1) + self._function2 = autowrap_function6d(function2) + + @cython.cdivision(True) + cdef double evaluate(self, double x, double y, double z, double u, double w, double v) except? -1e999: + cdef double denominator = self._function2.evaluate(x, y, z, u, w, v) + if denominator == 0.0: + raise ZeroDivisionError("Function used as the denominator of the division returned a zero value.") + return self._function1.evaluate(x, y, z, u, w, v) / denominator + + +cdef class ModuloFunction6D(Function6D): + """ + A Function6D class that implements the modulo of the results of two Function6D objects: f1() % f2() + + This class is not intended to be used directly, but rather returned as the result of a __mod__() call on a + Function6D object. + + :param object function1: A Function6D object or Python callable. + :param object function2: A Function6D object or Python callable. + """ + def __init__(self, function1, function2): + self._function1 = autowrap_function6d(function1) + self._function2 = autowrap_function6d(function2) + + @cython.cdivision(True) + cdef double evaluate(self, double x, double y, double z, double u, double w, double v) except? -1e999: + cdef double divisor = self._function2.evaluate(x, y, z, u, w, v) + if divisor == 0.0: + raise ZeroDivisionError("Function used as the divisor of the modulo returned a zero value.") + return self._function1.evaluate(x, y, z, u, w, v) % divisor + + +cdef class PowFunction6D(Function6D): + """ + A Function6D class that implements the pow() operator on two Function6D objects. + + This class is not intended to be used directly, but rather returned as the result of a __pow__() call on a + Function6D object. + + :param object function1: A Function6D object or Python callable. + :param object function2: A Function6D object or Python callable. + """ + def __init__(self, function1, function2): + self._function1 = autowrap_function6d(function1) + self._function2 = autowrap_function6d(function2) + + cdef double evaluate(self, double x, double y, double z, double u, double w, double v) except? -1e999: + cdef double base, exponent + base = self._function1.evaluate(x, y, z, u, w, v) + exponent = self._function2.evaluate(x, y, z, u, w, v) + if base < 0 and floor(exponent) != exponent: # Would return a complex value rather than double + raise ValueError("Negative base and non-integral exponent is not supported") + if base == 0 and exponent < 0: + raise ZeroDivisionError("0.0 cannot be raised to a negative power") + return base ** exponent + + +cdef class AbsFunction6D(Function6D): + """ + A Function6D class that implements the absolute value of the result of a Function6D object: abs(f()). + + This class is not intended to be used directly, but rather returned as the + result of an __abs__() call on a Function6D object. + + :param object function: A Function6D object or Python callable. + """ + def __init__(self, object function): + self._function = autowrap_function6d(function) + + cdef double evaluate(self, double x, double y, double z, double u, double w, double v) except? -1e999: + return abs(self._function.evaluate(x, y, z, u, w, v)) + + +cdef class EqualsFunction6D(Function6D): + """ + A Function6D class that tests the equality of the results of two Function6D objects: f1() == f2() + + This class is not intended to be used directly, but rather returned as the result of an __eq__() call on a + Function6D object. + + :param object function1: A Function6D object or Python callable. + :param object function2: A Function6D object or Python callable. + """ + def __init__(self, object function1, object function2): + self._function1 = autowrap_function6d(function1) + self._function2 = autowrap_function6d(function2) + + cdef double evaluate(self, double x, double y, double z, double u, double w, double v) except? -1e999: + return self._function1.evaluate(x, y, z, u, w, v) == self._function2.evaluate(x, y, z, u, w, v) + + +cdef class NotEqualsFunction6D(Function6D): + """ + A Function6D class that tests the inequality of the results of two Function6D objects: f1() != f2() + + This class is not intended to be used directly, but rather returned as the result of an __ne__() call on a + Function6D object. + + :param object function1: A Function6D object or Python callable. + :param object function2: A Function6D object or Python callable. + """ + def __init__(self, object function1, object function2): + self._function1 = autowrap_function6d(function1) + self._function2 = autowrap_function6d(function2) + + cdef double evaluate(self, double x, double y, double z, double u, double w, double v) except? -1e999: + return self._function1.evaluate(x, y, z, u, w, v) != self._function2.evaluate(x, y, z, u, w, v) + + +cdef class LessThanFunction6D(Function6D): + """ + A Function6D class that implements < of the results of two Function6D objects: f1() < f2() + + This class is not intended to be used directly, but rather returned as the result of an __lt__() call on a + Function6D object. + + :param object function1: A Function6D object or Python callable. + :param object function2: A Function6D object or Python callable. + """ + def __init__(self, object function1, object function2): + self._function1 = autowrap_function6d(function1) + self._function2 = autowrap_function6d(function2) + + cdef double evaluate(self, double x, double y, double z, double u, double w, double v) except? -1e999: + return self._function1.evaluate(x, y, z, u, w, v) < self._function2.evaluate(x, y, z, u, w, v) + + +cdef class GreaterThanFunction6D(Function6D): + """ + A Function6D class that implements > of the results of two Function6D objects: f1() > f2() + + This class is not intended to be used directly, but rather returned as the result of a __gt__() call on a + Function6D object. + + :param object function1: A Function6D object or Python callable. + :param object function2: A Function6D object or Python callable. + """ + def __init__(self, object function1, object function2): + self._function1 = autowrap_function6d(function1) + self._function2 = autowrap_function6d(function2) + + cdef double evaluate(self, double x, double y, double z, double u, double w, double v) except? -1e999: + return self._function1.evaluate(x, y, z, u, w, v) > self._function2.evaluate(x, y, z, u, w, v) + + +cdef class LessEqualsFunction6D(Function6D): + """ + A Function6D class that implements <= of the results of two Function6D objects: f1() <= f2() + + This class is not intended to be used directly, but rather returned as the result of an __le__() call on a + Function6D object. + + :param object function1: A Function6D object or Python callable. + :param object function2: A Function6D object or Python callable. + """ + def __init__(self, object function1, object function2): + self._function1 = autowrap_function6d(function1) + self._function2 = autowrap_function6d(function2) + + cdef double evaluate(self, double x, double y, double z, double u, double w, double v) except? -1e999: + return self._function1.evaluate(x, y, z, u, w, v) <= self._function2.evaluate(x, y, z, u, w, v) + + +cdef class GreaterEqualsFunction6D(Function6D): + """ + A Function6D class that implements >= of the results of two Function6D objects: f1() >= f2() + + This class is not intended to be used directly, but rather returned as the result of an __ge__() call on a + Function6D object. + + :param object function1: A Function6D object or Python callable. + :param object function2: A Function6D object or Python callable. + """ + def __init__(self, object function1, object function2): + self._function1 = autowrap_function6d(function1) + self._function2 = autowrap_function6d(function2) + + cdef double evaluate(self, double x, double y, double z, double u, double w, double v) except? -1e999: + return self._function1.evaluate(x, y, z, u, w, v) >= self._function2.evaluate(x, y, z, u, w, v) + + +cdef class AddScalar6D(Function6D): + """ + A Function6D class that implements the addition of scalar and the result of a Function6D object: K + f() + + This class is not intended to be used directly, but rather returned as the result of an __add__() call on a + Function6D object. + + :param value: A double value. + :param function: A Function6D object or Python callable. + """ + + def __init__(self, double value, object function): + self._value = value + self._function = autowrap_function6d(function) + + cdef double evaluate(self, double x, double y, double z, double u, double w, double v) except? -1e999: + return self._value + self._function.evaluate(x, y, z, u, w, v) + + +cdef class SubtractScalar6D(Function6D): + """ + A Function6D class that implements the subtraction of scalar and the result of a Function6D object: K - f() + + This class is not intended to be used directly, but rather returned as the result of an __sub__() call on a + Function6D object. + + :param value: A double value. + :param function: A Function6D object or Python callable. + """ + + def __init__(self, double value, object function): + self._value = value + self._function = autowrap_function6d(function) + + cdef double evaluate(self, double x, double y, double z, double u, double w, double v) except? -1e999: + return self._value - self._function.evaluate(x, y, z, u, w, v) + + +cdef class MultiplyScalar6D(Function6D): + """ + A Function6D class that implements the multiplication of scalar and the result of a Function6D object: K * f() + + This class is not intended to be used directly, but rather returned as the result of an __mul__() call on a + Function6D object. + + :param value: A double value. + :param function: A Function6D object or Python callable. + """ + + def __init__(self, double value, object function): + self._value = value + self._function = autowrap_function6d(function) + + cdef double evaluate(self, double x, double y, double z, double u, double w, double v) except? -1e999: + return self._value * self._function.evaluate(x, y, z, u, w, v) + + +cdef class DivideScalar6D(Function6D): + """ + A Function6D class that implements the subtraction of scalar and the result of a Function6D object: K / f() + + This class is not intended to be used directly, but rather returned as the result of an __div__() call on a + Function6D object. + + :param value: A double value. + :param function: A Function6D object or Python callable. + """ + + def __init__(self, double value, object function): + self._value = value + self._function = autowrap_function6d(function) + + @cython.cdivision(True) + cdef double evaluate(self, double x, double y, double z, double u, double w, double v) except? -1e999: + cdef double denominator = self._function.evaluate(x, y, z, u, w, v) + if denominator == 0.0: + raise ZeroDivisionError("Function used as the denominator of the division returned a zero value.") + return self._value / denominator + + +cdef class ModuloScalarFunction6D(Function6D): + """ + A Function6D class that implements the modulo of scalar and the result of a Function6D object: K % f() + + This class is not intended to be used directly, but rather returned as the result of a __mod__() call on a + Function6D object. + + :param float value: A double value. + :param object function: A Function6D object or Python callable. + """ + def __init__(self, double value, object function): + self._value = value + self._function = autowrap_function6d(function) + + @cython.cdivision(True) + cdef double evaluate(self, double x, double y, double z, double u, double w, double v) except? -1e999: + cdef double divisor = self._function.evaluate(x, y, z, u, w, v) + if divisor == 0.0: + raise ZeroDivisionError("Function used as the divisor of the modulo returned a zero value.") + return self._value % divisor + + +cdef class ModuloFunctionScalar6D(Function6D): + """ + A Function6D class that implements the modulo of the result of a Function6D object and a scalar: f() % K + + This class is not intended to be used directly, but rather returned as the result of a __mod__() call on a + Function6D object. + + :param object function: A Function6D object or Python callable. + :param float value: A double value. + """ + def __init__(self, object function, double value): + if value == 0: + raise ValueError("Divisor cannot be zero") + self._value = value + self._function = autowrap_function6d(function) + + @cython.cdivision(True) + cdef double evaluate(self, double x, double y, double z, double u, double w, double v) except? -1e999: + return self._function.evaluate(x, y, z, u, w, v) % self._value + + +cdef class PowScalarFunction6D(Function6D): + """ + A Function6D class that implements the pow of scalar and the result of a Function6D object: K ** f() + + This class is not intended to be used directly, but rather returned as the result of an __pow__() call on a + Function6D object. + + :param float value: A double value. + :param object function: A Function6D object or Python callable. + """ + def __init__(self, double value, object function): + self._value = value + self._function = autowrap_function6d(function) + + cdef double evaluate(self, double x, double y, double z, double u, double w, double v) except? -1e999: + cdef double exponent = self._function.evaluate(x, y, z, u, w, v) + if self._value < 0 and floor(exponent) != exponent: + raise ValueError("Negative base and non-integral exponent is not supported") + if self._value == 0 and exponent < 0: + raise ZeroDivisionError("0.0 cannot be raised to a negative power") + return self._value ** exponent + + +cdef class PowFunctionScalar6D(Function6D): + """ + A Function6D class that implements the pow of the result of a Function6D object and a scalar: f() ** K + + This class is not intended to be used directly, but rather returned as the result of an __pow__() call on a + Function6D object. + + :param object function: A Function6D object or Python callable. + :param float value: A double value. + """ + def __init__(self, object function, double value): + self._value = value + self._function = autowrap_function6d(function) + + cdef double evaluate(self, double x, double y, double z, double u, double w, double v) except? -1e999: + cdef double base = self._function.evaluate(x, y, z, u, w, v) + if base < 0 and floor(self._value) != self._value: + raise ValueError("Negative base and non-integral exponent is not supported") + if base == 0 and self._value < 0: + raise ZeroDivisionError("0.0 cannot be raised to a negative power") + return base ** self._value + + +cdef class EqualsScalar6D(Function6D): + """ + A Function6D class that tests the equality of a scalar and the result of a Function6D object: K == f2() + + This class is not intended to be used directly, but rather returned as the result of an __eq__() call on a + Function6D object. + + :param value: A double value. + :param object function: A Function6D object or Python callable. + """ + def __init__(self, double value, object function): + self._value = value + self._function = autowrap_function6d(function) + + cdef double evaluate(self, double x, double y, double z, double u, double w, double v) except? -1e999: + return self._value == self._function.evaluate(x, y, z, u, w, v) + + +cdef class NotEqualsScalar6D(Function6D): + """ + A Function6D class that tests the inequality of a scalar and the result of a Function6D object: K != f2() + + This class is not intended to be used directly, but rather returned as the result of an __ne__() call on a + Function6D object. + + :param value: A double value. + :param object function: A Function6D object or Python callable. + """ + def __init__(self, double value, object function): + self._value = value + self._function = autowrap_function6d(function) + + cdef double evaluate(self, double x, double y, double z, double u, double w, double v) except? -1e999: + return self._value != self._function.evaluate(x, y, z, u, w, v) + + +cdef class LessThanScalar6D(Function6D): + """ + A Function6D class that implements < of a scalar and the result of a Function6D object: K < f2() + + This class is not intended to be used directly, but rather returned as the result of an __lt__() call on a + Function6D object. + + :param value: A double value. + :param object function: A Function6D object or Python callable. + """ + def __init__(self, double value, object function): + self._value = value + self._function = autowrap_function6d(function) + + cdef double evaluate(self, double x, double y, double z, double u, double w, double v) except? -1e999: + return self._value < self._function.evaluate(x, y, z, u, w, v) + + +cdef class GreaterThanScalar6D(Function6D): + """ + A Function6D class that implements > of a scalar and the result of a Function6D object: K > f2() + + This class is not intended to be used directly, but rather returned as the result of a __gt__() call on a + Function6D object. + + :param value: A double value. + :param object function: A Function6D object or Python callable. + """ + def __init__(self, double value, object function): + self._value = value + self._function = autowrap_function6d(function) + + cdef double evaluate(self, double x, double y, double z, double u, double w, double v) except? -1e999: + return self._value > self._function.evaluate(x, y, z, u, w, v) + + +cdef class LessEqualsScalar6D(Function6D): + """ + A Function6D class that implements <= of a scalar and the result of a Function6D object: K <= f2() + + This class is not intended to be used directly, but rather returned as the result of an __le__() call on a + Function6D object. + + :param value: A double value. + :param object function: A Function6D object or Python callable. + """ + def __init__(self, double value, object function): + self._value = value + self._function = autowrap_function6d(function) + + cdef double evaluate(self, double x, double y, double z, double u, double w, double v) except? -1e999: + return self._value <= self._function.evaluate(x, y, z, u, w, v) + + +cdef class GreaterEqualsScalar6D(Function6D): + """ + A Function6D class that implements >= of a scalar and the result of a Function6D object: K >= f2() + + This class is not intended to be used directly, but rather returned as the result of an __ge__() call on a + Function6D object. + + :param value: A double value. + :param object function: A Function6D object or Python callable. + """ + def __init__(self, double value, object function): + self._value = value + self._function = autowrap_function6d(function) + + cdef double evaluate(self, double x, double y, double z, double u, double w, double v) except? -1e999: + return self._value >= self._function.evaluate(x, y, z, u, w, v) diff --git a/cherab/core/math/function/float/function6d/blend.pxd b/cherab/core/math/function/float/function6d/blend.pxd new file mode 100644 index 00000000..3a634883 --- /dev/null +++ b/cherab/core/math/function/float/function6d/blend.pxd @@ -0,0 +1,25 @@ +# cython: language_level=3 + +# Copyright 2016-2025 Euratom +# Copyright 2016-2025 United Kingdom Atomic Energy Authority +# Copyright 2016-2025 Centro de Investigaciones Energéticas, Medioambientales y Tecnológicas +# +# Licensed under the EUPL, Version 1.1 or – as soon they will be approved by the +# European Commission - subsequent versions of the EUPL (the "Licence"); +# You may not use this work except in compliance with the Licence. +# You may obtain a copy of the Licence at: +# +# https://joinup.ec.europa.eu/software/page/eupl5 +# +# Unless required by applicable law or agreed to in writing, software distributed +# under the Licence is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR +# CONDITIONS OF ANY KIND, either express or implied. +# +# See the Licence for the specific language governing permissions and limitations +# under the Licence. + +from cherab.core.math.function.float.function6d.base cimport Function6D + + +cdef class Blend6D(Function6D): + cdef Function6D _f1, _f2, _mask \ No newline at end of file diff --git a/cherab/core/math/function/float/function6d/blend.pyx b/cherab/core/math/function/float/function6d/blend.pyx new file mode 100644 index 00000000..6630bbb2 --- /dev/null +++ b/cherab/core/math/function/float/function6d/blend.pyx @@ -0,0 +1,65 @@ +# cython: language_level=3 + +# Copyright 2016-2025 Euratom +# Copyright 2016-2025 United Kingdom Atomic Energy Authority +# Copyright 2016-2025 Centro de Investigaciones Energéticas, Medioambientales y Tecnológicas +# +# Licensed under the EUPL, Version 1.1 or – as soon they will be approved by the +# European Commission - subsequent versions of the EUPL (the "Licence"); +# You may not use this work except in compliance with the Licence. +# You may obtain a copy of the Licence at: +# +# https://joinup.ec.europa.eu/software/page/eupl5 +# +# Unless required by applicable law or agreed to in writing, software distributed +# under the Licence is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR +# CONDITIONS OF ANY KIND, either express or implied. +# +# See the Licence for the specific language governing permissions and limitations +# under the Licence. + +from cherab.core.math.function.float.function6d.autowrap cimport autowrap_function6d +from raysect.core.math.cython cimport clamp + + +cdef class Blend6D(Function6D): + """ + Performs a linear interpolation between two scalar functions, modulated by a 3rd scalar function. + + The value of the scalar mask function is used to interpolated between the + values returned by the two functions. Mathematically the value returned by + this function is as follows: + + .. math:: + v = (1 - f_m(x)) f_1(x) + f_m(x) f_2(x) + + The value of the mask function is clamped to the range [0, 1] if the sampled + value exceeds the required range. + """ + + def __init__(self, object f1, object f2, object mask): + """ + :param float.Function6D f1: First scalar function. + :param float.Function6D f2: Second scalar function. + :param float.Function6D mask: Scalar function returning a value in the range [0, 1]. + """ + + self._f1 = autowrap_function6d(f1) + self._f2 = autowrap_function6d(f2) + self._mask = autowrap_function6d(mask) + + cdef double evaluate(self, double x, double y, double z, double u, double w, double v) except? -1e999: + + cdef double t = clamp(self._mask.evaluate(x, y, z, u, w, v), 0.0, 1.0) + + # sample endpoints directly + if t == 0: + return self._f1.evaluate(x, y, z, u, w, v) + + if t == 1: + return self._f2.evaluate(x, y, z, u, w, v) + + # lerp between function values + cdef double f1 = self._f1.evaluate(x, y, z, u, w, v) + cdef double f2 = self._f2.evaluate(x, y, z, u, w, v) + return (1 - t) * f1 + t * f2 diff --git a/cherab/core/math/function/float/function6d/cmath.pxd b/cherab/core/math/function/float/function6d/cmath.pxd new file mode 100644 index 00000000..bf1043a7 --- /dev/null +++ b/cherab/core/math/function/float/function6d/cmath.pxd @@ -0,0 +1,61 @@ +# cython: language_level=3 + +# Copyright 2016-2025 Euratom +# Copyright 2016-2025 United Kingdom Atomic Energy Authority +# Copyright 2016-2025 Centro de Investigaciones Energéticas, Medioambientales y Tecnológicas +# +# Licensed under the EUPL, Version 1.1 or – as soon they will be approved by the +# European Commission - subsequent versions of the EUPL (the "Licence"); +# You may not use this work except in compliance with the Licence. +# You may obtain a copy of the Licence at: +# +# https://joinup.ec.europa.eu/software/page/eupl5 +# +# Unless required by applicable law or agreed to in writing, software distributed +# under the Licence is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR +# CONDITIONS OF ANY KIND, either express or implied. +# +# See the Licence for the specific language governing permissions and limitations +# under the Licence. + +from cherab.core.math.function.float.function6d.base cimport Function6D + + +cdef class Exp6D(Function6D): + cdef Function6D _function + + +cdef class Sin6D(Function6D): + cdef Function6D _function + + +cdef class Cos6D(Function6D): + cdef Function6D _function + + +cdef class Tan6D(Function6D): + cdef Function6D _function + + +cdef class Asin6D(Function6D): + cdef Function6D _function + + +cdef class Acos6D(Function6D): + cdef Function6D _function + + +cdef class Atan6D(Function6D): + cdef Function6D _function + + +cdef class Atan4Q6D(Function6D): + cdef Function6D _numerator, _denominator + + +cdef class Sqrt6D(Function6D): + cdef Function6D _function + + +cdef class Erf6D(Function6D): + cdef Function6D _function diff --git a/cherab/core/math/function/float/function6d/cmath.pyx b/cherab/core/math/function/float/function6d/cmath.pyx new file mode 100644 index 00000000..ccc5d9b6 --- /dev/null +++ b/cherab/core/math/function/float/function6d/cmath.pyx @@ -0,0 +1,168 @@ +# cython: language_level=3 + +# Copyright 2016-2025 Euratom +# Copyright 2016-2025 United Kingdom Atomic Energy Authority +# Copyright 2016-2025 Centro de Investigaciones Energéticas, Medioambientales y Tecnológicas +# +# Licensed under the EUPL, Version 1.1 or – as soon they will be approved by the +# European Commission - subsequent versions of the EUPL (the "Licence"); +# You may not use this work except in compliance with the Licence. +# You may obtain a copy of the Licence at: +# +# https://joinup.ec.europa.eu/software/page/eupl5 +# +# Unless required by applicable law or agreed to in writing, software distributed +# under the Licence is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR +# CONDITIONS OF ANY KIND, either express or implied. +# +# See the Licence for the specific language governing permissions and limitations +# under the Licence. + +cimport libc.math as cmath +from cherab.core.math.function.float.function6d.base cimport Function6D +from cherab.core.math.function.float.function6d.autowrap cimport autowrap_function6d + + +cdef class Exp6D(Function6D): + """ + A Function6D class that implements the exponential of the result of a Function6D object: exp(f()) + + :param Function6D function: A Function6D object. + """ + def __init__(self, object function): + self._function = autowrap_function6d(function) + + cdef double evaluate(self, double x, double y, double z, double u, double w, double v) except? -1e999: + return cmath.exp(self._function.evaluate(x, y, z, u, w, v)) + + +cdef class Sin6D(Function6D): + """ + A Function6D class that implements the sine of the result of a Function6D object: sin(f()) + + :param Function6D function: A Function6D object. + """ + def __init__(self, object function): + self._function = autowrap_function6d(function) + + cdef double evaluate(self, double x, double y, double z, double u, double w, double v) except? -1e999: + return cmath.sin(self._function.evaluate(x, y, z, u, w, v)) + + +cdef class Cos6D(Function6D): + """ + A Function6D class that implements the cosine of the result of a Function6D object: cos(f()) + + :param Function6D function: A Function6D object. + """ + def __init__(self, object function): + self._function = autowrap_function6d(function) + + cdef double evaluate(self, double x, double y, double z, double u, double w, double v) except? -1e999: + return cmath.cos(self._function.evaluate(x, y, z, u, w, v)) + + +cdef class Tan6D(Function6D): + """ + A Function6D class that implements the tangent of the result of a Function6D object: tan(f()) + + :param Function6D function: A Function6D object. + """ + def __init__(self, object function): + self._function = autowrap_function6d(function) + + cdef double evaluate(self, double x, double y, double z, double u, double w, double v) except? -1e999: + return cmath.tan(self._function.evaluate(x, y, z, u, w, v)) + + +cdef class Asin6D(Function6D): + """ + A Function6D class that implements the arcsine of the result of a Function6D object: asin(f()) + + :param Function6D function: A Function6D object. + """ + def __init__(self, object function): + self._function = autowrap_function6d(function) + + cdef double evaluate(self, double x, double y, double z, double u, double w, double v) except? -1e999: + cdef double val = self._function.evaluate(x, y, z, u, w, v) + if -1.0 <= val <= 1.0: + return cmath.asin(val) + raise ValueError("The function returned a value outside of the arcsine domain of [-1, 1].") + + +cdef class Acos6D(Function6D): + """ + A Function6D class that implements the arccosine of the result of a Function6D object: acos(f()) + + :param Function6D function: A Function6D object. + """ + def __init__(self, object function): + self._function = autowrap_function6d(function) + + cdef double evaluate(self, double x, double y, double z, double u, double w, double v) except? -1e999: + cdef double val = self._function.evaluate(x, y, z, u, w, v) + if -1.0 <= val <= 1.0: + return cmath.acos(val) + raise ValueError("The function returned a value outside of the arccosine domain of [-1, 1].") + + +cdef class Atan6D(Function6D): + """ + A Function6D class that implements the arctangent of the result of a Function6D object: atan(f()) + + :param Function6D function: A Function6D object. + """ + def __init__(self, object function): + self._function = autowrap_function6d(function) + + cdef double evaluate(self, double x, double y, double z, double u, double w, double v) except? -1e999: + return cmath.atan(self._function.evaluate(x, y, z, u, w, v)) + + +cdef class Atan4Q6D(Function6D): + """ + A Function6D class that implements the arctangent of the result of 2 Function6D objects: atan2(f1(), f2()) + + This differs from Atan6D in that it takes separate functions for the + numerator and denominator, in order to get the quadrant correct. + + :param Function6D numerator: A Function6D object representing the numerator + :param Function6D denominator: A Function6D object representing the denominator + """ + def __init__(self, object numerator, object denominator): + self._numerator = autowrap_function6d(numerator) + self._denominator = autowrap_function6d(denominator) + + cdef double evaluate(self, double x, double y, double z, double u, double w, double v) except? -1e999: + return cmath.atan2(self._numerator.evaluate(x, y, z, u, w, v), + self._denominator.evaluate(x, y, z, u, w, v)) + + +cdef class Sqrt6D(Function6D): + """ + A Function6D class that implements the square root of the result of a Function6D object: sqrt(f()) + + :param Function6D function: A Function6D object. + """ + def __init__(self, object function): + self._function = autowrap_function6d(function) + + cdef double evaluate(self, double x, double y, double z, double u, double w, double v) except? -1e999: + cdef double f = self._function.evaluate(x, y, z, u, w, v) + if f < 0: # complex values are not supported + raise ValueError("Math domain error in sqrt({0}). Sqrt of a negative value is not supported.".format(f)) + return cmath.sqrt(f) + + +cdef class Erf6D(Function6D): + """ + A Function6D class that implements the error function of the result of a Function6D object: erf(f()) + + :param Function6D function: A Function6D object. + """ + def __init__(self, object function): + self._function = autowrap_function6d(function) + + cdef double evaluate(self, double x, double y, double z, double u, double w, double v) except? -1e999: + return cmath.erf(self._function.evaluate(x, y, z, u, w, v)) \ No newline at end of file diff --git a/cherab/core/math/function/float/function6d/constant.pxd b/cherab/core/math/function/float/function6d/constant.pxd new file mode 100644 index 00000000..2ff17e61 --- /dev/null +++ b/cherab/core/math/function/float/function6d/constant.pxd @@ -0,0 +1,25 @@ +# cython: language_level=3 + +# Copyright 2016-2025 Euratom +# Copyright 2016-2025 United Kingdom Atomic Energy Authority +# Copyright 2016-2025 Centro de Investigaciones Energéticas, Medioambientales y Tecnológicas +# +# Licensed under the EUPL, Version 1.1 or – as soon they will be approved by the +# European Commission - subsequent versions of the EUPL (the "Licence"); +# You may not use this work except in compliance with the Licence. +# You may obtain a copy of the Licence at: +# +# https://joinup.ec.europa.eu/software/page/eupl5 +# +# Unless required by applicable law or agreed to in writing, software distributed +# under the Licence is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR +# CONDITIONS OF ANY KIND, either express or implied. +# +# See the Licence for the specific language governing permissions and limitations +# under the Licence. + +from cherab.core.math.function.float.function6d.base cimport Function6D + + +cdef class Constant6D(Function6D): + cdef double _value diff --git a/cherab/core/math/function/float/function6d/constant.pyx b/cherab/core/math/function/float/function6d/constant.pyx new file mode 100644 index 00000000..1464f01c --- /dev/null +++ b/cherab/core/math/function/float/function6d/constant.pyx @@ -0,0 +1,49 @@ +# cython: language_level=3 + +# Copyright 2016-2025 Euratom +# Copyright 2016-2025 United Kingdom Atomic Energy Authority +# Copyright 2016-2025 Centro de Investigaciones Energéticas, Medioambientales y Tecnológicas +# +# Licensed under the EUPL, Version 1.1 or – as soon they will be approved by the +# European Commission - subsequent versions of the EUPL (the "Licence"); +# You may not use this work except in compliance with the Licence. +# You may obtain a copy of the Licence at: +# +# https://joinup.ec.europa.eu/software/page/eupl5 +# +# Unless required by applicable law or agreed to in writing, software distributed +# under the Licence is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR +# CONDITIONS OF ANY KIND, either express or implied. +# +# See the Licence for the specific language governing permissions and limitations +# under the Licence. + +from cherab.core.math.function.float.function6d.base cimport Function6D + + +cdef class Constant6D(Function6D): + """ + Wraps a scalar constant with a Function6D object. + + This class allows a numeric Python scalar, such as a float or an integer, to + interact with cython code that requires a Function6D object. The scalar must + be convertible to double. The value of the scalar constant will be returned + independent of the arguments the function is called with. + + This class is intended to be used to transparently wrap python objects that + are passed via constructors or methods into cython optimised code. It is not + intended that the users should need to directly interact with these wrapping + objects. Constructors and methods expecting a Function6D object should be + designed to accept a generic python object and then test that object to + determine if it is an instance of Function6D. If the object is not a + Function6D object it should be wrapped using this class for internal use. + + See also: autowrap_function6d() + + :param float value: the constant value to return when called + """ + def __init__(self, double value): + self._value = value + + cdef double evaluate(self, double x, double y, double z, double u, double w, double v) except? -1e999: + return self._value diff --git a/cherab/core/math/function/float/function6d/tests/__init__.py b/cherab/core/math/function/float/function6d/tests/__init__.py new file mode 100644 index 00000000..dcc4669c --- /dev/null +++ b/cherab/core/math/function/float/function6d/tests/__init__.py @@ -0,0 +1,5 @@ +from .test_base import * +from .test_autowrap import * +from .test_constant import * +from .test_arg import * +from .test_cmath import * diff --git a/cherab/core/math/function/float/function6d/tests/test_arg.py b/cherab/core/math/function/float/function6d/tests/test_arg.py new file mode 100644 index 00000000..c5f42cae --- /dev/null +++ b/cherab/core/math/function/float/function6d/tests/test_arg.py @@ -0,0 +1,54 @@ +# cython: language_level=3 + +# Copyright 2016-2025 Euratom +# Copyright 2016-2025 United Kingdom Atomic Energy Authority +# Copyright 2016-2025 Centro de Investigaciones Energéticas, Medioambientales y Tecnológicas +# +# Licensed under the EUPL, Version 1.1 or – as soon they will be approved by the +# European Commission - subsequent versions of the EUPL (the "Licence"); +# You may not use this work except in compliance with the Licence. +# You may obtain a copy of the Licence at: +# +# https://joinup.ec.europa.eu/software/page/eupl5 +# +# Unless required by applicable law or agreed to in writing, software distributed +# under the Licence is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR +# CONDITIONS OF ANY KIND, either express or implied. +# +# See the Licence for the specific language governing permissions and limitations +# under the Licence. + +""" +Unit tests for the Arg6D class. +""" + +import unittest +from cherab.core.math.function.float.function6d.arg import Arg6D + +# TODO: expand tests to cover the cython interface +class TestArg6D(unittest.TestCase): + + def test_arg(self): + testvals = [-1e10, -7, -0.001, 0.0, 0.00003, 10, 2.3e49] + for x in testvals: + for y in testvals: + for z in testvals: + for u in testvals: + for w in testvals: + for v in testvals: + argx = Arg6D("x") + argy = Arg6D("y") + argz = Arg6D("z") + argu = Arg6D("u") + argw = Arg6D("w") + argv = Arg6D("v") + self.assertEqual(argx(x, y, z, u, w, v), x, "Arg6D('x') call did not match reference value.") + self.assertEqual(argy(x, y, z, u, w, v), y, "Arg6D('y') call did not match reference value.") + self.assertEqual(argz(x, y, z, u, w, v), z, "Arg6D('z') call did not match reference value.") + self.assertEqual(argu(x, y, z, u, w, v), u, "Arg6D('u') call did not match reference value.") + self.assertEqual(argw(x, y, z, u, w, v), w, "Arg6D('w') call did not match reference value.") + self.assertEqual(argv(x, y, z, u, w, v), v, "Arg6D('v') call did not match reference value.") + + def test_invalid_inputs(self): + with self.assertRaises(ValueError, msg="Arg6D did not raise ValueError with incorrect string."): + Arg6D("q") diff --git a/cherab/core/math/function/float/function6d/tests/test_autowrap.py b/cherab/core/math/function/float/function6d/tests/test_autowrap.py new file mode 100644 index 00000000..2ebe0463 --- /dev/null +++ b/cherab/core/math/function/float/function6d/tests/test_autowrap.py @@ -0,0 +1,37 @@ +# cython: language_level=3 + +# Copyright 2016-2025 Euratom +# Copyright 2016-2025 United Kingdom Atomic Energy Authority +# Copyright 2016-2025 Centro de Investigaciones Energéticas, Medioambientales y Tecnológicas +# +# Licensed under the EUPL, Version 1.1 or – as soon they will be approved by the +# European Commission - subsequent versions of the EUPL (the "Licence"); +# You may not use this work except in compliance with the Licence. +# You may obtain a copy of the Licence at: +# +# https://joinup.ec.europa.eu/software/page/eupl5 +# +# Unless required by applicable law or agreed to in writing, software distributed +# under the Licence is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR +# CONDITIONS OF ANY KIND, either express or implied. +# +# See the Licence for the specific language governing permissions and limitations +# under the Licence. + +""" +Unit tests for the autowrap_6d function +""" + +import unittest +from cherab.core.math.function.float.function6d.autowrap import _autowrap_function6d, PythonFunction6D +from cherab.core.math.function.float.function6d.constant import Constant6D + +class TestAutowrap6D(unittest.TestCase): + + def test_constant(self): + function = _autowrap_function6d(5.0) + self.assertIsInstance(function, Constant6D, "Autowrapped scalar float is not a Constant6D.") + + def test_python_function(self): + function = _autowrap_function6d(lambda x, y, z, u, w, v: 10*x + 5*y + 2*z + u + 3*w + 4*v) + self.assertIsInstance(function, PythonFunction6D, "Autowrapped function is not a PythonFunction6D.") diff --git a/cherab/core/math/function/float/function6d/tests/test_base.py b/cherab/core/math/function/float/function6d/tests/test_base.py new file mode 100644 index 00000000..66a5e2a1 --- /dev/null +++ b/cherab/core/math/function/float/function6d/tests/test_base.py @@ -0,0 +1,649 @@ +# cython: language_level=3 + +# Copyright 2016-2025 Euratom +# Copyright 2016-2025 United Kingdom Atomic Energy Authority +# Copyright 2016-2025 Centro de Investigaciones Energéticas, Medioambientales y Tecnológicas +# +# Licensed under the EUPL, Version 1.1 or – as soon they will be approved by the +# European Commission - subsequent versions of the EUPL (the "Licence"); +# You may not use this work except in compliance with the Licence. +# You may obtain a copy of the Licence at: +# +# https://joinup.ec.europa.eu/software/page/eupl5 +# +# Unless required by applicable law or agreed to in writing, software distributed +# under the Licence is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR +# CONDITIONS OF ANY KIND, either express or implied. +# +# See the Licence for the specific language governing permissions and limitations +# under the Licence. + +""" +Unit tests for the Function6D class. +""" + +import math +import unittest +from cherab.core.math.function.float.function6d.autowrap import PythonFunction6D + +# TODO: expand tests to cover the cython interface +class TestFunction6D(unittest.TestCase): + + def setUp(self): + self.ref1 = lambda x, y, z, u, w, v: 10 * x + 5 * y + 2 * z + u + 3 * w + 4 * v + self.ref2 = lambda x, y, z, u, w, v: abs(x + y + z + u + w + v) + + self.f1 = PythonFunction6D(self.ref1) + self.f2 = PythonFunction6D(self.ref2) + + def test_call(self): + testvals = [-1e10, -7, -0.001, 0.0, 0.00003, 10, 2.3e49] + for x in testvals: + for y in testvals: + for z in testvals: + for u in testvals: + for w in testvals: + for v in testvals: + self.assertEqual(self.f1(x, y, z, u, w, v), self.ref1(x, y, z, u, w, v), + "Function6D call did not match reference function value.") + + def test_negate(self): + testvals = [-1e10, -7, -0.001, 0.0, 0.00003, 10, 2.3e49] + r = -self.f1 + for x in testvals: + for y in testvals: + for z in testvals: + for u in testvals: + for w in testvals: + for v in testvals: + self.assertEqual(r(x, y, z, u, w, v), -self.ref1(x, y, z, u, w, v), + "Function6D negate did not match reference function value.") + + def test_add_scalar(self): + testvals = [-1e10, -7, -0.001, 0.0, 0.00003, 10, 2.3e49] + r1 = 8 + self.f1 + r2 = self.f1 + 65 + for x in testvals: + for y in testvals: + for z in testvals: + for u in testvals: + for w in testvals: + for v in testvals: + self.assertEqual(r1(x, y, z, u, w, v), 8 + self.ref1(x, y, z, u, w, v), + "Function6D add scalar (K + f()) did not match reference function value.") + self.assertEqual(r2(x, y, z, u, w, v), self.ref1(x, y, z, u, w, v) + 65, + "Function6D add scalar (f() + K) did not match reference function value.") + + def test_sub_scalar(self): + testvals = [-1e10, -7, -0.001, 0.0, 0.00003, 10, 2.3e49] + r1 = 8 - self.f1 + r2 = self.f1 - 65 + for x in testvals: + for y in testvals: + for z in testvals: + for u in testvals: + for w in testvals: + for v in testvals: + self.assertEqual(r1(x, y, z, u, w, v), 8 - self.ref1(x, y, z, u, w, v), + "Function6D subtract scalar (K - f()) did not match reference function value.") + self.assertEqual(r2(x, y, z, u, w, v), self.ref1(x, y, z, u, w, v) - 65, + "Function6D subtract scalar (f() - K) did not match reference function value.") + + def test_mul_scalar(self): + testvals = [-1e10, -7, -0.001, 0.0, 0.00003, 10, 2.3e49] + r1 = 5 * self.f1 + r2 = self.f1 * -7.8 + for x in testvals: + for y in testvals: + for z in testvals: + for u in testvals: + for w in testvals: + for v in testvals: + self.assertEqual(r1(x, y, z, u, w, v), 5 * self.ref1(x, y, z, u, w, v), + "Function6D multiply scalar (K * f()) did not match reference function value.") + self.assertEqual(r2(x, y, z, u, w, v), self.ref1(x, y, z, u, w, v) * -7.8, + "Function6D multiply scalar (f() * K) did not match reference function value.") + + def test_div_scalar(self): + testvals = [-1e10, -7, -0.001, 0.000031, 10.3, 2.3e49] + r1 = 5.451 / self.f1 + r2 = self.f1 / -7.8 + for x in testvals: + for y in testvals: + for z in testvals: + for u in testvals: + for w in testvals: + for v in testvals: + self.assertEqual(r1(x, y, z, u, w, v), 5.451 / self.ref1(x, y, z, u, w, v), + "Function6D divide scalar (K / f()) did not match reference function value.") + self.assertAlmostEqual(r2(x, y, z, u, w, v), self.ref1(x, y, z, u, w, v) / -7.8, + delta=abs(r2(x, y, z, u, w, v)) * 1e-12, + msg="Function6D divide scalar (f() / K) did not match reference function value.") + + r = 5 / self.f1 + with self.assertRaises(ZeroDivisionError, msg="ZeroDivisionError not raised when function returns zero."): + r(0, 0, 0, 0, 0, 0) + + def test_mod_function6d_scalar(self): + # Note that Function6D objects work with doubles, so the floating modulo + # operator is used rather than the integer one. For accurate testing we + # therefore need to use the math.fmod operator rather than % in Python. + testvals = [-10, -7, -0.001, 0.00003, 10, 12.3] + r1 = 5 % self.f1 + r2 = self.f1 % -7.8 + for x in testvals: + for y in testvals: + for z in testvals: + for u in testvals: + for w in testvals: + for v in testvals: + if self.ref1(x, y, z, u, w, v) == 0: + with self.assertRaises(ZeroDivisionError, msg="ZeroDivisionError not raised when function returns 0."): + r1(x, y, z, u, w, v) + else: + self.assertAlmostEqual(r1(x, y, z, u, w, v), math.fmod(5, self.ref1(x, y, z, u, w, v)), 15, "Function6D modulo scalar (K % f()) did not match reference function value.") + self.assertAlmostEqual(r2(x, y, z, u, w, v), math.fmod(self.ref1(x, y, z, u, w, v), -7.8), 15, "Function6D modulo scalar (f() % K) did not match reference function value.") + with self.assertRaises(ZeroDivisionError, msg="ZeroDivisionError not raised when function returns 0."): + r1(0, 0, 0, 0, 0, 0) + with self.assertRaises(ZeroDivisionError, msg="ZeroDivisionError not raised when modulo scalar is 0."): + self.f1 % 0 + + def test_pow_function6d_scalar(self): + testvals = [-10, -7, -0.001, 0.00003, 10, 12.3] + r1 = 5. ** self.f1 + r2 = self.f1 ** -7.8 + r3 = (-5.) ** self.f1 + for x in testvals: + for y in testvals: + for z in testvals: + for u in testvals: + for w in testvals: + for v in testvals: + self.assertAlmostEqual(r1(x, y, z, u, w, v), 5. ** self.ref1(x, y, z, u, w, v), 15, "Function6D power scalar (K ** f()) did not match reference function value.") + if self.ref1(x, y, z, u, w, v) < 0: + with self.assertRaises(ValueError, msg="ValueError not raised when base is negative and exponent non-integral."): + r2(x, y, z, u, w, v) + elif not float(self.ref1(x, y, z, u, w, v)).is_integer(): + with self.assertRaises(ValueError, msg="ValueError not raised when base is negative and exponent non-integral."): + r3(x, y, z, u, w, v) + else: + if self.ref1(x, y, z, u, w, v) == 0: + with self.assertRaises(ZeroDivisionError, msg="ZeroDivisionError not raised when base is 0 and exponent negative."): + r2(x, y, z, u, w, v) + else: + self.assertAlmostEqual(r2(x, y, z, u, w, v), self.ref1(x, y, z, u, w, v) ** -7.8, 15, "Function6D power scalar (f() ** K) did not match reference function value.") + with self.assertRaises(ZeroDivisionError, msg="ZeroDivisionError not raised when base is 0 and exponent negative."): + r2(0, 0, 0, 0, 0, 0) + with self.assertRaises(ZeroDivisionError, msg="ZeroDivisionError not raised when base is zero and exponent negative."): + r4 = 0 ** self.f1 + r4(-1, 0, 0, 0, 0, 0) + + def test_richcmp_scalar(self): + testvals = [-1e10, -7, -0.001, 0.0, 0.00003, 10, 2.3e49] + for x in testvals: + for y in testvals: + for z in testvals: + for u in testvals: + for w in testvals: + for v in testvals: + ref_value = self.ref1(x, y, z, u, w, v) + higher_value = ref_value + abs(ref_value) + 1 + lower_value = ref_value - abs(ref_value) - 1 + self.assertEqual( + (self.f1 == ref_value)(x, y, z, u, w, v), 1.0, + msg="Function6D equals scalar (f() == K) did not return true when it should." + ) + self.assertEqual( + (ref_value == self.f1)(x, y, z, u, w, v), 1.0, + msg="Scalar equals Function6D (K == f()) did not return true when it should." + ) + self.assertEqual( + (self.f1 == higher_value)(x, y, z, u, w, v), 0.0, + msg="Function6D equals scalar (f() == K) did not return false when it should." + ) + self.assertEqual( + (higher_value == self.f1)(x, y, z, u, w, v), 0.0, + msg="Scalar equals Function6D (K == f()) did not return false when it should." + ) + self.assertEqual( + (self.f1 != higher_value)(x, y, z, u, w, v), 1.0, + msg="Function6D not equals scalar (f() != K) did not return true when it should." + ) + self.assertEqual( + (higher_value != self.f1)(x, y, z, u, w, v), 1.0, + msg="Scalar not equals Function6D (K != f()) did not return true when it should." + ) + self.assertEqual( + (self.f1 != ref_value)(x, y, z, u, w, v), 0.0, + msg="Function6D not equals scalar (f() != K) did not return false when it should." + ) + self.assertEqual( + (ref_value != self.f1)(x, y, z, u, w, v), 0.0, + msg="Scalar not equals Function6D (K != f()) did not return false when it should." + ) + self.assertEqual( + (self.f1 < higher_value)(x, y, z, u, w, v), 1.0, + msg="Function6D less than scalar (f() < K) did not return true when it should." + ) + self.assertEqual( + (lower_value < self.f1)(x, y, z, u, w, v), 1.0, + msg="Scalar less than Function6D (K < f()) did not return true when it should." + ) + self.assertEqual( + (self.f1 < lower_value)(x, y, z, u, w, v), 0.0, + msg="Function6D less than scalar (f() < K) did not return false when it should." + ) + self.assertEqual( + (higher_value < self.f1)(x, y, z, u, w, v), 0.0, + msg="Scalar less than Function6D (K < f()) did not return false when it should." + ) + self.assertEqual( + (self.f1 > lower_value)(x, y, z, u, w, v), 1.0, + msg="Function6D greater than scalar (f() > K) did not return true when it should." + ) + self.assertEqual( + (higher_value > self.f1)(x, y, z, u, w, v), 1.0, + msg="Scalar greater than Function6D (K > f()) did not return true when it should." + ) + self.assertEqual( + (self.f1 > higher_value)(x, y, z, u, w, v), 0.0, + msg="Function6D greater than scalar (f() > K) did not return false when it should." + ) + self.assertEqual( + (lower_value > self.f1)(x, y, z, u, w, v), 0.0, + msg="Scalar greater than Function6D (K > f()) did not return false when it should." + ) + self.assertEqual( + (self.f1 <= higher_value)(x, y, z, u, w, v), 1.0, + msg="Function6D less equals scalar (f() <= K) did not return true when it should." + ) + self.assertEqual( + (lower_value <= self.f1)(x, y, z, u, w, v), 1.0, + msg="Scalar less equals Function6D (K <= f()) did not return true when it should." + ) + self.assertEqual( + (self.f1 <= ref_value)(x, y, z, u, w, v), 1.0, + msg="Function6D less equals scalar (f() <= K) did not return true when it should." + ) + self.assertEqual( + (ref_value <= self.f1)(x, y, z, u, w, v), 1.0, + msg="Scalar less equals Function6D (K <= f()) did not return true when it should." + ) + self.assertEqual( + (self.f1 <= lower_value)(x, y, z, u, w, v), 0.0, + msg="Function6D less equals scalar (f() <= K) did not return false when it should." + ) + self.assertEqual( + (higher_value <= self.f1)(x, y, z, u, w, v), 0.0, + msg="Scalar less equals Function6D (K <= f()) did not return false when it should." + ) + self.assertEqual( + (self.f1 >= lower_value)(x, y, z, u, w, v), 1.0, + msg="Function6D greater equals scalar (f() >= K) did not return true when it should." + ) + self.assertEqual( + (higher_value >= self.f1)(x, y, z, u, w, v), 1.0, + msg="Scalar greater equals Function6D (K >= f()) did not return true when it should." + ) + self.assertEqual( + (self.f1 >= ref_value)(x, y, z, u, w, v), 1.0, + msg="Function6D greater equals scalar (f() >= K) did not return true when it should." + ) + self.assertEqual( + (ref_value >= self.f1)(x, y, z, u, w, v), 1.0, + msg="Scalar greater equals Function6D (K >= f()) did not return true when it should." + ) + self.assertEqual( + (self.f1 >= higher_value)(x, y, z, u, w, v), 0.0, + msg="Function6D greater equals scalar (f() >= K) did not return false when it should." + ) + self.assertEqual( + (lower_value >= self.f1)(x, y, z, u, w, v), 0.0, + msg="Scalar greater equals Function6D (K >= f()) did not return false when it should." + ) + + def test_add_function6d(self): + testvals = [-1e10, -7, -0.001, 0.0, 0.00003, 10, 2.3e49] + r1 = self.f1 + self.f2 + r2 = self.ref1 + self.f2 + r3 = self.f1 + self.ref2 + for x in testvals: + for y in testvals: + for z in testvals: + for u in testvals: + for w in testvals: + for v in testvals: + self.assertEqual(r1(x, y, z, u, w, v), self.ref1(x, y, z, u, w, v) + self.ref2(x, y, z, u, w, v), "Function6D add function (f1() + f2()) did not match reference function value.") + self.assertEqual(r2(x, y, z, u, w, v), self.ref1(x, y, z, u, w, v) + self.ref2(x, y, z, u, w, v), "Function6D add function (p1() + f2()) did not match reference function value.") + self.assertEqual(r3(x, y, z, u, w, v), self.ref1(x, y, z, u, w, v) + self.ref2(x, y, z, u, w, v), "Function6D add function (f1() + p2()) did not match reference function value.") + + def test_sub_function6d(self): + testvals = [-1e10, -7, -0.001, 0.0, 0.00003, 10, 2.3e49] + r1 = self.f1 - self.f2 + r2 = self.ref1 - self.f2 + r3 = self.f1 - self.ref2 + for x in testvals: + for y in testvals: + for z in testvals: + for u in testvals: + for w in testvals: + for v in testvals: + self.assertEqual(r1(x, y, z, u, w, v), self.ref1(x, y, z, u, w, v) - self.ref2(x, y, z, u, w, v), "Function6D subtract function (f1() - f2()) did not match reference function value.") + self.assertEqual(r2(x, y, z, u, w, v), self.ref1(x, y, z, u, w, v) - self.ref2(x, y, z, u, w, v), "Function6D subtract function (p1() - f2()) did not match reference function value.") + self.assertEqual(r3(x, y, z, u, w, v), self.ref1(x, y, z, u, w, v) - self.ref2(x, y, z, u, w, v), "Function6D subtract function (f1() - p2()) did not match reference function value.") + + def test_mul_function6d(self): + testvals = [-1e10, -7, -0.001, 0.0, 0.00003, 10, 2.3e49] + r1 = self.f1 * self.f2 + r2 = self.ref1 * self.f2 + r3 = self.f1 * self.ref2 + for x in testvals: + for y in testvals: + for z in testvals: + for u in testvals: + for w in testvals: + for v in testvals: + self.assertEqual(r1(x, y, z, u, w, v), self.ref1(x, y, z, u, w, v) * self.ref2(x, y, z, u, w, v), "Function6D multiply function (f1() * f2()) did not match reference function value.") + self.assertEqual(r2(x, y, z, u, w, v), self.ref1(x, y, z, u, w, v) * self.ref2(x, y, z, u, w, v), "Function6D multiply function (p1() * f2()) did not match reference function value.") + self.assertEqual(r3(x, y, z, u, w, v), self.ref1(x, y, z, u, w, v) * self.ref2(x, y, z, u, w, v), "Function6D multiply function (f1() * p2()) did not match reference function value.") + + def test_div_function6d(self): + testvals = [-1e10, -7, -0.001, 0.00003, 10, 2.3e49] + r1 = self.f1 / self.f2 + r2 = self.ref1 / self.f2 + r3 = self.f1 / self.ref2 + for x in testvals: + for y in testvals: + for z in testvals: + for u in testvals: + for w in testvals: + for v in testvals: + self.assertAlmostEqual(r1(x, y, z, u, w, v), self.ref1(x, y, z, u, w, v) / self.ref2(x, y, z, u, w, v), delta=abs(r1(x, y, z, u, w, v)) * 1e-12, msg="Function6D divide function (f1() / f2()) did not match reference function value.") + self.assertAlmostEqual(r2(x, y, z, u, w, v), self.ref1(x, y, z, u, w, v) / self.ref2(x, y, z, u, w, v), delta=abs(r2(x, y, z, u, w, v)) * 1e-12, msg="Function6D divide function (p1() / f2()) did not match reference function value.") + self.assertAlmostEqual(r3(x, y, z, u, w, v), self.ref1(x, y, z, u, w, v) / self.ref2(x, y, z, u, w, v), delta=abs(r3(x, y, z, u, w, v)) * 1e-12, msg="Function6D divide function (f1() / p2()) did not match reference function value.") + + with self.assertRaises(ZeroDivisionError, msg="ZeroDivisionError not raised when function returns zero."): + r1(0, 0, 0, 0, 0, 0) + + def test_mod_function6d(self): + testvals = [-1e10, -7, -0.001, 0.00003, 10, 2.3e49] + r1 = self.f1 % self.f2 + r2 = self.ref1 % self.f2 + r3 = self.f1 % self.ref2 + for x in testvals: + for y in testvals: + for z in testvals: + for u in testvals: + for w in testvals: + for v in testvals: + self.assertAlmostEqual(r1(x, y, z, u, w, v), math.fmod(self.ref1(x, y, z, u, w, v), self.ref2(x, y, z, u, w, v)), delta=abs(r1(x, y, z, u, w, v)) * 1e-12, msg="Function6D modulo function (f1() % f2()) did not match reference function value.") + self.assertAlmostEqual(r2(x, y, z, u, w, v), math.fmod(self.ref1(x, y, z, u, w, v), self.ref2(x, y, z, u, w, v)), delta=abs(r2(x, y, z, u, w, v)) * 1e-12, msg="Function6D modulo function (p1() % f2()) did not match reference function value.") + self.assertAlmostEqual(r3(x, y, z, u, w, v), math.fmod(self.ref1(x, y, z, u, w, v), self.ref2(x, y, z, u, w, v)), delta=abs(r3(x, y, z, u, w, v)) * 1e-12, msg="Function6D modulo function (f1() % p2()) did not match reference function value.") + + with self.assertRaises(ZeroDivisionError, msg="ZeroDivisionError not raised when function returns zero."): + r1(0, 0, 0, 0, 0, 0) + + def test_pow_function6d_function6d(self): + testvals = [-3.0, -0.7, -0.001, 0.00003, 2] + r1 = self.f1 ** self.f2 + r2 = self.ref1 ** self.f2 + r3 = self.f1 ** self.ref2 + for x in testvals: + for y in testvals: + for z in testvals: + for u in testvals: + for w in testvals: + for v in testvals: + if self.ref1(x, y, z, u, w, v) < 0 and not float(self.ref2(x, y, z, u, w, v)).is_integer(): + with self.assertRaises(ValueError, msg="ValueError not raised when base is negative and exponent non-integral (1/3)."): + r1(x, y, z, u, w, v) + with self.assertRaises(ValueError, msg="ValueError not raised when base is negative and exponent non-integral (2/3)."): + r2(x, y, z, u, w, v) + with self.assertRaises(ValueError, msg="ValueError not raised when base is negative and exponent non-integral (3/3)."): + r3(x, y, z, u, w, v) + else: + self.assertAlmostEqual(r1(x, y, z, u, w, v), self.ref1(x, y, z, u, w, v) ** self.ref2(x, y, z, u, w, v), 15, "Function6D power function (f1() ** f2()) did not match reference function value.") + self.assertAlmostEqual(r2(x, y, z, u, w, v), self.ref1(x, y, z, u, w, v) ** self.ref2(x, y, z, u, w, v), 15, "Function6D power function (p1() ** f2()) did not match reference function value.") + self.assertAlmostEqual(r3(x, y, z, u, w, v), self.ref1(x, y, z, u, w, v) ** self.ref2(x, y, z, u, w, v), 15, "Function6D power function (f1() ** p2()) did not match reference function value.") + + with self.assertRaises(ZeroDivisionError, msg="ZeroDivisionError not raised when f1() == 0 and f2() is negative."): + r4 = PythonFunction6D(lambda x, y, z, u, w, v: 0) ** self.f1 + r4(-1, 0, 0, 0, 0, 0) + + def test_pow_3_arguments(self): + testvals = [-10, -7, -0.001, 0.00003, 0.8] + r1 = pow(self.f1, 5, 3) + r2 = pow(5, self.f1, 3) + r3 = pow(5, self.f1, self.f2) + r4 = pow(self.f2, self.f1, self.f2) + r5 = pow(self.f2, self.ref1, self.ref2) + r6 = pow(self.ref2, self.f1, self.f2) + # Can't use 3 argument pow() if all arguments aren't integers, so + # use fmod(a, b) % c instead + for x in testvals: + for y in testvals: + for z in testvals: + for u in testvals: + for w in testvals: + for v in testvals: + self.assertEqual(r1(x, y, z, u, w, v), math.fmod(self.ref1(x, y, z, u, w, v) ** 5, 3), "Function6D 3 argument pow(f1(), A, B) did not match reference value.") + self.assertEqual(r2(x, y, z, u, w, v), math.fmod(5 ** self.ref1(x, y, z, u, w, v), 3), "Function6D 3 argument pow(A, f1(), B) did not match reference value.") + self.assertEqual(r3(x, y, z, u, w, v), math.fmod(5 ** self.ref1(x, y, z, u, w, v), self.ref2(x, y, z, u, w, v)), "Function6D 3 argument pow(A, f1(), f2()) did not match reference value.") + self.assertEqual(r4(x, y, z, u, w, v), math.fmod(self.ref2(x, y, z, u, w, v) ** self.ref1(x, y, z, u, w, v), self.ref2(x, y, z, u, w, v)), "Function6D 3 argument pow(f2(), f1(), f2()) did not match reference value.") + self.assertEqual(r5(x, y, z, u, w, v), math.fmod(self.ref2(x, y, z, u, w, v) ** self.ref1(x, y, z, u, w, v), self.ref2(x, y, z, u, w, v)), "Function6D 3 argument pow(f2(), p1(), p2()) did not match reference value.") + self.assertEqual(r6(x, y, z, u, w, v), math.fmod(self.ref2(x, y, z, u, w, v) ** self.ref1(x, y, z, u, w, v), self.ref2(x, y, z, u, w, v)), "Function6D 3 argument pow(p2(), f1(), f2()) did not match reference value.") + + def test_abs(self): + testvals = [-1e10, -7, -0.001, 0.0, 0.0003, 10, 2.3e49] + for x in testvals: + for y in testvals: + for z in testvals: + for u in testvals: + for w in testvals: + for v in testvals: + self.assertEqual(abs(self.f1)(x, y, z, u, w, v), abs(self.ref1(x, y, z, u, w, v)), + msg="abs(Function6D) did not match reference value") + + def test_richcmp_function_callable(self): + testvals = [-1e10, -7, -0.001, 0.0, 0.00003, 10, 2.3e49] + for x in testvals: + for y in testvals: + for z in testvals: + for u in testvals: + for w in testvals: + for v in testvals: + ref_value = self.ref1 + higher_value = lambda x, y, z, u, w, v: self.ref1(x, y, z, u, w, v) + abs(self.ref1(x, y, z, u, w, v)) + 1 + lower_value = lambda x, y, z, u, w, v: self.ref1(x, y, z, u, w, v) - abs(self.ref1(x, y, z, u, w, v)) - 1 + self.assertEqual( + (self.f1 == ref_value)(x, y, z, u, w, v), 1.0, + msg="Function6D equals callable (f1() == f2()) did not return true when it should." + ) + self.assertEqual( + (self.f1 == higher_value)(x, y, z, u, w, v), 0.0, + msg="Function6D equals callable (f1() == f2()) did not return false when it should." + ) + self.assertEqual( + (self.f1 != higher_value)(x, y, z, u, w, v), 1.0, + msg="Function6D not equals callable (f1() != f2()) did not return true when it should." + ) + self.assertEqual( + (self.f1 != ref_value)(x, y, z, u, w, v), 0.0, + msg="Function6D not equals callable (f1() != f2()) did not return false when it should." + ) + self.assertEqual( + (self.f1 < higher_value)(x, y, z, u, w, v), 1.0, + msg="Function6D less than callable (f1() < f2()) did not return true when it should." + ) + self.assertEqual( + (self.f1 < lower_value)(x, y, z, u, w, v), 0.0, + msg="Function6D less than callable (f1() < f2()) did not return false when it should." + ) + self.assertEqual( + (self.f1 > lower_value)(x, y, z, u, w, v), 1.0, + msg="Function6D greater than callable (f1() > f2()) did not return true when it should." + ) + self.assertEqual( + (self.f1 > higher_value)(x, y, z, u, w, v), 0.0, + msg="Function6D greater than callable (f1() > f2()) did not return false when it should." + ) + self.assertEqual( + (self.f1 <= higher_value)(x, y, z, u, w, v), 1.0, + msg="Function6D less equals callable (f1() <= f2()) did not return true when it should." + ) + self.assertEqual( + (self.f1 <= ref_value)(x, y, z, u, w, v), 1.0, + msg="Function6D less equals callable (f1() <= f2()) did not return true when it should." + ) + self.assertEqual( + (self.f1 <= lower_value)(x, y, z, u, w, v), 0.0, + msg="Function6D less equals callable (f1() <= f2()) did not return false when it should." + ) + self.assertEqual( + (self.f1 >= lower_value)(x, y, z, u, w, v), 1.0, + msg="Function6D equals callable (f1() >= f2()) did not return true when it should." + ) + self.assertEqual( + (self.f1 >= ref_value)(x, y, z, u, w, v), 1.0, + msg="Function6D greater equals callable (f1() >= f2()) did not return true when it should." + ) + self.assertEqual( + (self.f1 >= higher_value)(x, y, z, u, w, v), 0.0, + msg="Function6D equals callable (f1() >= f2()) did not return false when it should." + ) + + def test_richcmp_callable_function(self): + testvals = [-1e10, -7, -0.001, 0.0, 0.00003, 10, 2.3e49] + for x in testvals: + for y in testvals: + for z in testvals: + for u in testvals: + for w in testvals: + for v in testvals: + ref_value = self.ref1 + higher_value = lambda x, y, z, u, w, v: self.ref1(x, y, z, u, w, v) + abs(self.ref1(x, y, z, u, w, v)) + 1 + lower_value = lambda x, y, z, u, w, v: self.ref1(x, y, z, u, w, v) - abs(self.ref1(x, y, z, u, w, v)) - 1 + self.assertEqual( + (ref_value == self.f1)(x, y, z, u, w, v), 1.0, + msg="Callable equals Function6D (f1() == f2()) did not return true when it should." + ) + self.assertEqual( + (higher_value == self.f1)(x, y, z, u, w, v), 0.0, + msg="Callable equals Function6D (f1() == f2()) did not return false when it should." + ) + self.assertEqual( + (higher_value != self.f1)(x, y, z, u, w, v), 1.0, + msg="Callable not equals Function6D (f1() != f2()) did not return true when it should." + ) + self.assertEqual( + (ref_value != self.f1)(x, y, z, u, w, v), 0.0, + msg="Callable not equals Function6D (f1() != f2()) did not return false when it should." + ) + self.assertEqual( + (lower_value < self.f1)(x, y, z, u, w, v), 1.0, + msg="Callable less than Function6D (f1() < f2()) did not return true when it should." + ) + self.assertEqual( + (higher_value < self.f1)(x, y, z, u, w, v), 0.0, + msg="Callable less than Function6D (f1() < f2()) did not return false when it should." + ) + self.assertEqual( + (higher_value > self.f1)(x, y, z, u, w, v), 1.0, + msg="Callable greater than Function6D (f1() > f2()) did not return true when it should." + ) + self.assertEqual( + (lower_value > self.f1)(x, y, z, u, w, v), 0.0, + msg="Callable greater than Function6D (f1() > f2()) did not return false when it should." + ) + self.assertEqual( + (lower_value <= self.f1)(x, y, z, u, w, v), 1.0, + msg="Callable less equals Function6D (f1() <= f2()) did not return true when it should." + ) + self.assertEqual( + (ref_value <= self.f1)(x, y, z, u, w, v), 1.0, + msg="Callable less equals Function6D (f1() <= f2()) did not return true when it should." + ) + self.assertEqual( + (higher_value <= self.f1)(x, y, z, u, w, v), 0.0, + msg="Callable less equals Function6D (f1() <= f2()) did not return false when it should." + ) + self.assertEqual( + (higher_value >= self.f1)(x, y, z, u, w, v), 1.0, + msg="Callable equals Function6D (f1() >= f2()) did not return true when it should." + ) + self.assertEqual( + (ref_value >= self.f1)(x, y, z, u, w, v), 1.0, + msg="Callable greater equals Function6D (f1() >= f2()) did not return true when it should." + ) + self.assertEqual( + (lower_value >= self.f1)(x, y, z, u, w, v), 0.0, + msg="Callable equals Function6D (f1() >= f2()) did not return false when it should." + ) + + def test_richcmp_function_function(self): + testvals = [-1e10, -7, -0.001, 0.0, 0.00003, 10, 2.3e49] + for x in testvals: + for y in testvals: + for z in testvals: + for u in testvals: + for w in testvals: + for v in testvals: + ref_value = self.f1 + higher_value = self.f1 + abs(self.f1) + 1 + lower_value = self.f1 - abs(self.f1) - 1 + self.assertEqual( + (self.f1 == ref_value)(x, y, z, u, w, v), 1.0, + msg="Function6D equals Function6D (f1() == f2()) did not return true when it should." + ) + self.assertEqual( + (self.f1 == higher_value)(x, y, z, u, w, v), 0.0, + msg="Function6D equals Function6D (f1() == f2()) did not return false when it should." + ) + self.assertEqual( + (self.f1 != higher_value)(x, y, z, u, w, v), 1.0, + msg="Function6D not equals Function6D (f1() != f2()) did not return true when it should." + ) + self.assertEqual( + (self.f1 != ref_value)(x, y, z, u, w, v), 0.0, + msg="Function6D not equals Function6D (f1() != f2()) did not return false when it should." + ) + self.assertEqual( + (self.f1 < higher_value)(x, y, z, u, w, v), 1.0, + msg="Function6D less than Function6D (f1() < f2()) did not return true when it should." + ) + self.assertEqual( + (self.f1 < lower_value)(x, y, z, u, w, v), 0.0, + msg="Function6D less than Function6D (f1() < f2()) did not return false when it should." + ) + self.assertEqual( + (self.f1 > lower_value)(x, y, z, u, w, v), 1.0, + msg="Function6D greater than Function6D (f1() > f2()) did not return true when it should." + ) + self.assertEqual( + (self.f1 > higher_value)(x, y, z, u, w, v), 0.0, + msg="Function6D greater than Function6D (f1() > f2()) did not return false when it should." + ) + self.assertEqual( + (self.f1 <= higher_value)(x, y, z, u, w, v), 1.0, + msg="Function6D less equals Function6D (f1() <= f2()) did not return true when it should." + ) + self.assertEqual( + (self.f1 <= ref_value)(x, y, z, u, w, v), 1.0, + msg="Function6D less equals Function6D (f1() <= f2()) did not return true when it should." + ) + self.assertEqual( + (self.f1 <= lower_value)(x, y, z, u, w, v), 0.0, + msg="Function6D less equals Function6D (f1() <= f2()) did not return false when it should." + ) + self.assertEqual( + (self.f1 >= lower_value)(x, y, z, u, w, v), 1.0, + msg="Function6D equals Function6D (f1() >= f2()) did not return true when it should." + ) + self.assertEqual( + (self.f1 >= ref_value)(x, y, z, u, w, v), 1.0, + msg="Function6D greater equals Function6D (f1() >= f2()) did not return true when it should." + ) + self.assertEqual( + (self.f1 >= higher_value)(x, y, z, u, w, v), 0.0, + msg="Function6D equals Function6D (f1() >= f2()) did not return false when it should." + ) diff --git a/cherab/core/math/function/float/function6d/tests/test_cmath.py b/cherab/core/math/function/float/function6d/tests/test_cmath.py new file mode 100644 index 00000000..5c9f7a81 --- /dev/null +++ b/cherab/core/math/function/float/function6d/tests/test_cmath.py @@ -0,0 +1,155 @@ +# cython: language_level=3 + +# Copyright 2016-2025 Euratom +# Copyright 2016-2025 United Kingdom Atomic Energy Authority +# Copyright 2016-2025 Centro de Investigaciones Energéticas, Medioambientales y Tecnológicas +# +# Licensed under the EUPL, Version 1.1 or – as soon they will be approved by the +# European Commission - subsequent versions of the EUPL (the "Licence"); +# You may not use this work except in compliance with the Licence. +# You may obtain a copy of the Licence at: +# +# https://joinup.ec.europa.eu/software/page/eupl5 +# +# Unless required by applicable law or agreed to in writing, software distributed +# under the Licence is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR +# CONDITIONS OF ANY KIND, either express or implied. +# +# See the Licence for the specific language governing permissions and limitations +# under the Licence. + +""" +Unit tests for the cmath wrapper classes. +""" + +import math +import unittest +import cherab.core.math.function.float.function6d.cmath as cmath6d +from cherab.core.math.function.float.function6d.autowrap import PythonFunction6D + +# TODO: expand tests to cover the cython interface +class TestCmath6D(unittest.TestCase): + + def setUp(self): + self.f1 = PythonFunction6D(lambda x, y, z, u, w, v: x / 10 + y + z + u/2 + w/3 + v/4) + self.f2 = PythonFunction6D(lambda x, y, z, u, w, v: x * x + y * y - z * z + u * u + w * w - v * v) + + def test_exp(self): + testvals = [-10.0, -7, -0.001, 0.0, 0.00003, 10, 23.4] + for x in testvals: + for y in testvals: + for z in testvals: + for u in testvals: + for w in testvals: + for v in testvals: + function = cmath6d.Exp6D(self.f1) + expected = math.exp(self.f1(x, y, z, u, w, v)) + self.assertEqual(function(x, y, z, u, w, v), expected, "Exp6D call did not match reference value.") + + def test_sin(self): + testvals = [-10.0, -7, -0.001, 0.0, 0.00003, 10, 23.4] + for x in testvals: + for y in testvals: + for z in testvals: + for u in testvals: + for w in testvals: + for v in testvals: + function = cmath6d.Sin6D(self.f1) + expected = math.sin(self.f1(x, y, z, u, w, v)) + self.assertEqual(function(x, y, z, u, w, v), expected, "Sin6D call did not match reference value.") + + def test_cos(self): + testvals = [-10.0, -7, -0.001, 0.0, 0.00003, 10, 23.4] + for x in testvals: + for y in testvals: + for z in testvals: + for u in testvals: + for w in testvals: + for v in testvals: + function = cmath6d.Cos6D(self.f1) + expected = math.cos(self.f1(x, y, z, u, w, v)) + self.assertEqual(function(x, y, z, u, w, v), expected, "Cos6D call did not match reference value.") + + def test_tan(self): + testvals = [-10.0, -7, -0.001, 0.0, 0.00003, 10, 23.4] + for x in testvals: + for y in testvals: + for z in testvals: + for u in testvals: + for w in testvals: + for v in testvals: + function = cmath6d.Tan6D(self.f1) + expected = math.tan(self.f1(x, y, z, u, w, v)) + self.assertEqual(function(x, y, z, u, w, v), expected, "Tan6D call did not match reference value.") + + def test_asin(self): + v = [-10, -6, -2, -0.001, 0, 0.001, 2, 6, 10] + function = cmath6d.Asin6D(self.f1) + for x in v: + expected = math.asin(self.f1(x, 0, 0, 0, 0, 0)) + self.assertEqual(function(x, 0, 0, 0, 0, 0), expected, "Asin3D call did not match reference value.") + + with self.assertRaises(ValueError, msg="Asin3D did not raise a ValueError with value outside domain."): + function(100, 0, 0, 0, 0, 0) + + def test_acos(self): + v = [-10, -6, -2, -0.001, 0, 0.001, 2, 6, 10] + function = cmath6d.Acos6D(self.f1) + for x in v: + expected = math.acos(self.f1(x, 0, 0, 0, 0, 0)) + self.assertEqual(function(x, 0, 0, 0, 0, 0), expected, "Acos6D call did not match reference value.") + + with self.assertRaises(ValueError, msg="Acos3D did not raise a ValueError with value outside domain."): + function(100, 0, 0, 0, 0, 0) + + + def test_atan(self): + testvals = [-10.0, -7, -0.001, 0.0, 0.00003, 10, 23.4] + for x in testvals: + for y in testvals: + for z in testvals: + for u in testvals: + for w in testvals: + for v in testvals: + function = cmath6d.Atan6D(self.f1) + expected = math.atan(self.f1(x, y, z, u, w, v)) + self.assertEqual(function(x, y, z, u, w, v), expected, "Atan6D call did not match reference value.") + + def test_atan2(self): + testvals = [-10.0, -7, -0.001, 0.0, 0.00003, 10, 23.4] + for x in testvals: + for y in testvals: + for z in testvals: + for u in testvals: + for w in testvals: + for v in testvals: + function = cmath6d.Atan4Q6D(self.f1, self.f2) + expected = math.atan2(self.f1(x, y, z, u, w, v), self.f2(x, y, z, u, w, v)) + self.assertEqual(function(x, y, z, u, w, v), expected, "Atan4Q6D call did not match reference value.") + + def test_erf(self): + testvals = [-1e5, -7, -0.001, 0.0, 0.00003, 10, 23.4, 1e5] + function = cmath6d.Erf6D(self.f1) + for x in testvals: + for y in testvals: + for z in testvals: + for u in testvals: + for w in testvals: + for v in testvals: + expected = math.erf(self.f1(x, y, z, u, w, v)) + self.assertAlmostEqual(function(x, y, z, u, w, v), expected, 10, "Erf6D call did not match reference value.") + + def test_sqrt(self): + testvals = [0.0, 0.00003, 10, 23.4, 1e5] + function = cmath6d.Sqrt6D(self.f1) + for x in testvals: + for y in testvals: + for z in testvals: + for u in testvals: + for w in testvals: + for v in testvals: + expected = math.sqrt(self.f1(x, y, z, u, w, v)) + self.assertEqual(function(x, y, z, u, w, v), expected, "Sqrt6D call did not match reference value.") + + with self.assertRaises(ValueError, msg="Sqrt6D did not raise a ValueError with value outside domain."): + function(-0.1, -0.1, -0.1, -0.1, -0.1, -0.1) \ No newline at end of file diff --git a/cherab/core/math/function/float/function6d/tests/test_constant.py b/cherab/core/math/function/float/function6d/tests/test_constant.py new file mode 100644 index 00000000..1247e8f2 --- /dev/null +++ b/cherab/core/math/function/float/function6d/tests/test_constant.py @@ -0,0 +1,35 @@ +# cython: language_level=3 + +# Copyright 2016-2025 Euratom +# Copyright 2016-2025 United Kingdom Atomic Energy Authority +# Copyright 2016-2025 Centro de Investigaciones Energéticas, Medioambientales y Tecnológicas +# +# Licensed under the EUPL, Version 1.1 or – as soon they will be approved by the +# European Commission - subsequent versions of the EUPL (the "Licence"); +# You may not use this work except in compliance with the Licence. +# You may obtain a copy of the Licence at: +# +# https://joinup.ec.europa.eu/software/page/eupl5 +# +# Unless required by applicable law or agreed to in writing, software distributed +# under the Licence is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR +# CONDITIONS OF ANY KIND, either express or implied. +# +# See the Licence for the specific language governing permissions and limitations +# under the Licence. + +""" +Unit tests for the Constant6D class. +""" + +import unittest +from cherab.core.math.function.float.function6d.constant import Constant6D + +# TODO: expand tests to cover the cython interface +class TestConstant6D(unittest.TestCase): + + def test_constant(self): + testvals = [-1e10, -7, -0.001, 0.0, 0.00003, 10, 2.3e49] + for x in testvals: + constant = Constant6D(x) + self.assertEqual(constant(500, 1.5, -3.14, 2.7, 1.8, 3.6), x, "Constant6D call did not match reference value.") From e65fcc3f3c82a51e83bac0fdfd304adbddc9c041 Mon Sep 17 00:00:00 2001 From: MatejTomes Date: Tue, 4 Feb 2025 11:41:38 +0100 Subject: [PATCH 2/6] Corrected arguments in the docstring formula --- cherab/core/math/function/float/function6d/blend.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cherab/core/math/function/float/function6d/blend.pyx b/cherab/core/math/function/float/function6d/blend.pyx index 6630bbb2..e05c0452 100644 --- a/cherab/core/math/function/float/function6d/blend.pyx +++ b/cherab/core/math/function/float/function6d/blend.pyx @@ -31,7 +31,7 @@ cdef class Blend6D(Function6D): this function is as follows: .. math:: - v = (1 - f_m(x)) f_1(x) + f_m(x) f_2(x) + v = (1 - f_m(x, y, z, u, w, v)) f_1(x, y, z, u, w, v) + f_m(x, y, z, u, w, v) f_2(x, y, z, u, w, v) The value of the mask function is clamped to the range [0, 1] if the sampled value exceeds the required range. From 68fa029a5d51f15cab23022febd261627844a68f Mon Sep 17 00:00:00 2001 From: MatejTomes Date: Tue, 4 Feb 2025 11:43:09 +0100 Subject: [PATCH 3/6] Add function6d documentation --- docs/source/math/function.rst | 50 +++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/docs/source/math/function.rst b/docs/source/math/function.rst index 6f1680e1..1d670ec4 100644 --- a/docs/source/math/function.rst +++ b/docs/source/math/function.rst @@ -10,6 +10,56 @@ documentation and the Cherab function tutorials. Cherab previously provided vector functions which were not present in Raysect. New codes should prefer the Raysect vector functions, but the old aliases are preserved for backwards compatibility. +The Function6D framework in Cherab aims to provide a framework for building six-dimensional distribution functions. +The relation of Function6D to distribution functions makes it domain specific, and so it was included in Cherab's math module. +It follows closely Raysect's function framework. + +6D Scalar Functions +------------------- + +.. autoclass:: cherab.core.math.function.float.function6d.base.Function6D + :members: + :special-members: __call__ + +.. autoclass:: cherab.core.math.function.float.function6d.constant.Constant6D + :show-inheritance: + +.. autoclass:: cherab.core.math.function.float.function6d.arg.Arg6D + :show-inheritance: + +.. autoclass:: cherab.core.math.function.float.function6d.cmath.Exp6D + :show-inheritance: + +.. autoclass:: cherab.core.math.function.float.function6d.cmath.Sin6D + :show-inheritance: + +.. autoclass:: cherab.core.math.function.float.function6d.cmath.Cos6D + :show-inheritance: + +.. autoclass:: cherab.core.math.function.float.function6d.cmath.Tan6D + :show-inheritance: + +.. autoclass:: cherab.core.math.function.float.function6d.cmath.Asin6D + :show-inheritance: + +.. autoclass:: cherab.core.math.function.float.function6d.cmath.Acos6D + :show-inheritance: + +.. autoclass:: cherab.core.math.function.float.function6d.cmath.Atan6D + :show-inheritance: + +.. autoclass:: cherab.core.math.function.float.function6d.cmath.Atan4Q6D + :show-inheritance: + +.. autoclass:: cherab.core.math.function.float.function6d.cmath.Sqrt6D + :show-inheritance: + +.. autoclass:: cherab.core.math.function.float.function6d.cmath.Erf6D + :show-inheritance: + +.. autoclass:: cherab.core.math.function.float.function6d.blend.Blend6D + :show-inheritance: + 2D Vector Functions ------------------- From 246f8aca798284ad28e86d29019217b1544555b8 Mon Sep 17 00:00:00 2001 From: MatejTomes Date: Tue, 4 Feb 2025 12:01:28 +0100 Subject: [PATCH 4/6] Add CHANGELOG record --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5fe4cc06..49f91145 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,12 @@ Project Changelog ================= +Release 1.6.0 (TBD) +------------------- + +New: +* Add Function6D framework. (#478) + Release 1.5.0 (27 Aug 2024) ------------------- From c673241d90d601cf147eeca16e6b6def467fb532 Mon Sep 17 00:00:00 2001 From: MatejTomes Date: Tue, 25 Mar 2025 12:20:57 +0100 Subject: [PATCH 5/6] Update docummentation Reason for having Function6D in Cherab was removed --- docs/source/math/function.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/source/math/function.rst b/docs/source/math/function.rst index 1d670ec4..8b29a66e 100644 --- a/docs/source/math/function.rst +++ b/docs/source/math/function.rst @@ -11,7 +11,6 @@ Cherab previously provided vector functions which were not present in Raysect. New codes should prefer the Raysect vector functions, but the old aliases are preserved for backwards compatibility. The Function6D framework in Cherab aims to provide a framework for building six-dimensional distribution functions. -The relation of Function6D to distribution functions makes it domain specific, and so it was included in Cherab's math module. It follows closely Raysect's function framework. 6D Scalar Functions From 9f15c4fba46fac83bae074065cf1254c08f75050 Mon Sep 17 00:00:00 2001 From: MatejTomes Date: Tue, 25 Mar 2025 13:13:56 +0100 Subject: [PATCH 6/6] Replace nested forloops with itertools --- .../float/function6d/tests/test_arg.py | 32 +- .../float/function6d/tests/test_base.py | 873 ++++++++---------- .../float/function6d/tests/test_cmath.py | 102 +- .../float/function6d/tests/test_constant.py | 1 + 4 files changed, 433 insertions(+), 575 deletions(-) diff --git a/cherab/core/math/function/float/function6d/tests/test_arg.py b/cherab/core/math/function/float/function6d/tests/test_arg.py index c5f42cae..a594b472 100644 --- a/cherab/core/math/function/float/function6d/tests/test_arg.py +++ b/cherab/core/math/function/float/function6d/tests/test_arg.py @@ -23,6 +23,7 @@ """ import unittest +import itertools from cherab.core.math.function.float.function6d.arg import Arg6D # TODO: expand tests to cover the cython interface @@ -30,24 +31,19 @@ class TestArg6D(unittest.TestCase): def test_arg(self): testvals = [-1e10, -7, -0.001, 0.0, 0.00003, 10, 2.3e49] - for x in testvals: - for y in testvals: - for z in testvals: - for u in testvals: - for w in testvals: - for v in testvals: - argx = Arg6D("x") - argy = Arg6D("y") - argz = Arg6D("z") - argu = Arg6D("u") - argw = Arg6D("w") - argv = Arg6D("v") - self.assertEqual(argx(x, y, z, u, w, v), x, "Arg6D('x') call did not match reference value.") - self.assertEqual(argy(x, y, z, u, w, v), y, "Arg6D('y') call did not match reference value.") - self.assertEqual(argz(x, y, z, u, w, v), z, "Arg6D('z') call did not match reference value.") - self.assertEqual(argu(x, y, z, u, w, v), u, "Arg6D('u') call did not match reference value.") - self.assertEqual(argw(x, y, z, u, w, v), w, "Arg6D('w') call did not match reference value.") - self.assertEqual(argv(x, y, z, u, w, v), v, "Arg6D('v') call did not match reference value.") + for (x, y, z, u, w, v) in itertools.product(testvals, repeat=6): + argx = Arg6D("x") + argy = Arg6D("y") + argz = Arg6D("z") + argu = Arg6D("u") + argw = Arg6D("w") + argv = Arg6D("v") + self.assertEqual(argx(x, y, z, u, w, v), x, "Arg6D('x') call did not match reference value.") + self.assertEqual(argy(x, y, z, u, w, v), y, "Arg6D('y') call did not match reference value.") + self.assertEqual(argz(x, y, z, u, w, v), z, "Arg6D('z') call did not match reference value.") + self.assertEqual(argu(x, y, z, u, w, v), u, "Arg6D('u') call did not match reference value.") + self.assertEqual(argw(x, y, z, u, w, v), w, "Arg6D('w') call did not match reference value.") + self.assertEqual(argv(x, y, z, u, w, v), v, "Arg6D('v') call did not match reference value.") def test_invalid_inputs(self): with self.assertRaises(ValueError, msg="Arg6D did not raise ValueError with incorrect string."): diff --git a/cherab/core/math/function/float/function6d/tests/test_base.py b/cherab/core/math/function/float/function6d/tests/test_base.py index 66a5e2a1..d9ec4b13 100644 --- a/cherab/core/math/function/float/function6d/tests/test_base.py +++ b/cherab/core/math/function/float/function6d/tests/test_base.py @@ -24,6 +24,7 @@ import math import unittest +import itertools from cherab.core.math.function.float.function6d.autowrap import PythonFunction6D # TODO: expand tests to cover the cython interface @@ -38,87 +39,57 @@ def setUp(self): def test_call(self): testvals = [-1e10, -7, -0.001, 0.0, 0.00003, 10, 2.3e49] - for x in testvals: - for y in testvals: - for z in testvals: - for u in testvals: - for w in testvals: - for v in testvals: - self.assertEqual(self.f1(x, y, z, u, w, v), self.ref1(x, y, z, u, w, v), - "Function6D call did not match reference function value.") + for (x, y, z, u, w, v) in itertools.product(testvals, repeat=6): + self.assertEqual(self.f1(x, y, z, u, w, v), self.ref1(x, y, z, u, w, v), + "Function6D call did not match reference function value.") def test_negate(self): testvals = [-1e10, -7, -0.001, 0.0, 0.00003, 10, 2.3e49] r = -self.f1 - for x in testvals: - for y in testvals: - for z in testvals: - for u in testvals: - for w in testvals: - for v in testvals: - self.assertEqual(r(x, y, z, u, w, v), -self.ref1(x, y, z, u, w, v), - "Function6D negate did not match reference function value.") + for (x, y, z, u, w, v) in itertools.product(testvals, repeat=6): + self.assertEqual(r(x, y, z, u, w, v), -self.ref1(x, y, z, u, w, v), + "Function6D negate did not match reference function value.") def test_add_scalar(self): testvals = [-1e10, -7, -0.001, 0.0, 0.00003, 10, 2.3e49] r1 = 8 + self.f1 r2 = self.f1 + 65 - for x in testvals: - for y in testvals: - for z in testvals: - for u in testvals: - for w in testvals: - for v in testvals: - self.assertEqual(r1(x, y, z, u, w, v), 8 + self.ref1(x, y, z, u, w, v), - "Function6D add scalar (K + f()) did not match reference function value.") - self.assertEqual(r2(x, y, z, u, w, v), self.ref1(x, y, z, u, w, v) + 65, - "Function6D add scalar (f() + K) did not match reference function value.") + for (x, y, z, u, w, v) in itertools.product(testvals, repeat=6): + self.assertEqual(r1(x, y, z, u, w, v), 8 + self.ref1(x, y, z, u, w, v), + "Function6D add scalar (K + f()) did not match reference function value.") + self.assertEqual(r2(x, y, z, u, w, v), self.ref1(x, y, z, u, w, v) + 65, + "Function6D add scalar (f() + K) did not match reference function value.") def test_sub_scalar(self): testvals = [-1e10, -7, -0.001, 0.0, 0.00003, 10, 2.3e49] r1 = 8 - self.f1 r2 = self.f1 - 65 - for x in testvals: - for y in testvals: - for z in testvals: - for u in testvals: - for w in testvals: - for v in testvals: - self.assertEqual(r1(x, y, z, u, w, v), 8 - self.ref1(x, y, z, u, w, v), - "Function6D subtract scalar (K - f()) did not match reference function value.") - self.assertEqual(r2(x, y, z, u, w, v), self.ref1(x, y, z, u, w, v) - 65, - "Function6D subtract scalar (f() - K) did not match reference function value.") + for (x, y, z, u, w, v) in itertools.product(testvals, repeat=6): + self.assertEqual(r1(x, y, z, u, w, v), 8 - self.ref1(x, y, z, u, w, v), + "Function6D subtract scalar (K - f()) did not match reference function value.") + self.assertEqual(r2(x, y, z, u, w, v), self.ref1(x, y, z, u, w, v) - 65, + "Function6D subtract scalar (f() - K) did not match reference function value.") def test_mul_scalar(self): testvals = [-1e10, -7, -0.001, 0.0, 0.00003, 10, 2.3e49] r1 = 5 * self.f1 r2 = self.f1 * -7.8 - for x in testvals: - for y in testvals: - for z in testvals: - for u in testvals: - for w in testvals: - for v in testvals: - self.assertEqual(r1(x, y, z, u, w, v), 5 * self.ref1(x, y, z, u, w, v), - "Function6D multiply scalar (K * f()) did not match reference function value.") - self.assertEqual(r2(x, y, z, u, w, v), self.ref1(x, y, z, u, w, v) * -7.8, - "Function6D multiply scalar (f() * K) did not match reference function value.") + for (x, y, z, u, w, v) in itertools.product(testvals, repeat=6): + self.assertEqual(r1(x, y, z, u, w, v), 5 * self.ref1(x, y, z, u, w, v), + "Function6D multiply scalar (K * f()) did not match reference function value.") + self.assertEqual(r2(x, y, z, u, w, v), self.ref1(x, y, z, u, w, v) * -7.8, + "Function6D multiply scalar (f() * K) did not match reference function value.") def test_div_scalar(self): testvals = [-1e10, -7, -0.001, 0.000031, 10.3, 2.3e49] r1 = 5.451 / self.f1 r2 = self.f1 / -7.8 - for x in testvals: - for y in testvals: - for z in testvals: - for u in testvals: - for w in testvals: - for v in testvals: - self.assertEqual(r1(x, y, z, u, w, v), 5.451 / self.ref1(x, y, z, u, w, v), - "Function6D divide scalar (K / f()) did not match reference function value.") - self.assertAlmostEqual(r2(x, y, z, u, w, v), self.ref1(x, y, z, u, w, v) / -7.8, - delta=abs(r2(x, y, z, u, w, v)) * 1e-12, - msg="Function6D divide scalar (f() / K) did not match reference function value.") + for (x, y, z, u, w, v) in itertools.product(testvals, repeat=6): + self.assertEqual(r1(x, y, z, u, w, v), 5.451 / self.ref1(x, y, z, u, w, v), + "Function6D divide scalar (K / f()) did not match reference function value.") + self.assertAlmostEqual(r2(x, y, z, u, w, v), self.ref1(x, y, z, u, w, v) / -7.8, + delta=abs(r2(x, y, z, u, w, v)) * 1e-12, + msg="Function6D divide scalar (f() / K) did not match reference function value.") r = 5 / self.f1 with self.assertRaises(ZeroDivisionError, msg="ZeroDivisionError not raised when function returns zero."): @@ -131,18 +102,13 @@ def test_mod_function6d_scalar(self): testvals = [-10, -7, -0.001, 0.00003, 10, 12.3] r1 = 5 % self.f1 r2 = self.f1 % -7.8 - for x in testvals: - for y in testvals: - for z in testvals: - for u in testvals: - for w in testvals: - for v in testvals: - if self.ref1(x, y, z, u, w, v) == 0: - with self.assertRaises(ZeroDivisionError, msg="ZeroDivisionError not raised when function returns 0."): - r1(x, y, z, u, w, v) - else: - self.assertAlmostEqual(r1(x, y, z, u, w, v), math.fmod(5, self.ref1(x, y, z, u, w, v)), 15, "Function6D modulo scalar (K % f()) did not match reference function value.") - self.assertAlmostEqual(r2(x, y, z, u, w, v), math.fmod(self.ref1(x, y, z, u, w, v), -7.8), 15, "Function6D modulo scalar (f() % K) did not match reference function value.") + for (x, y, z, u, w, v) in itertools.product(testvals, repeat=6): + if self.ref1(x, y, z, u, w, v) == 0: + with self.assertRaises(ZeroDivisionError, msg="ZeroDivisionError not raised when function returns 0."): + r1(x, y, z, u, w, v) + else: + self.assertAlmostEqual(r1(x, y, z, u, w, v), math.fmod(5, self.ref1(x, y, z, u, w, v)), 15, "Function6D modulo scalar (K % f()) did not match reference function value.") + self.assertAlmostEqual(r2(x, y, z, u, w, v), math.fmod(self.ref1(x, y, z, u, w, v), -7.8), 15, "Function6D modulo scalar (f() % K) did not match reference function value.") with self.assertRaises(ZeroDivisionError, msg="ZeroDivisionError not raised when function returns 0."): r1(0, 0, 0, 0, 0, 0) with self.assertRaises(ZeroDivisionError, msg="ZeroDivisionError not raised when modulo scalar is 0."): @@ -153,25 +119,20 @@ def test_pow_function6d_scalar(self): r1 = 5. ** self.f1 r2 = self.f1 ** -7.8 r3 = (-5.) ** self.f1 - for x in testvals: - for y in testvals: - for z in testvals: - for u in testvals: - for w in testvals: - for v in testvals: - self.assertAlmostEqual(r1(x, y, z, u, w, v), 5. ** self.ref1(x, y, z, u, w, v), 15, "Function6D power scalar (K ** f()) did not match reference function value.") - if self.ref1(x, y, z, u, w, v) < 0: - with self.assertRaises(ValueError, msg="ValueError not raised when base is negative and exponent non-integral."): - r2(x, y, z, u, w, v) - elif not float(self.ref1(x, y, z, u, w, v)).is_integer(): - with self.assertRaises(ValueError, msg="ValueError not raised when base is negative and exponent non-integral."): - r3(x, y, z, u, w, v) - else: - if self.ref1(x, y, z, u, w, v) == 0: - with self.assertRaises(ZeroDivisionError, msg="ZeroDivisionError not raised when base is 0 and exponent negative."): - r2(x, y, z, u, w, v) - else: - self.assertAlmostEqual(r2(x, y, z, u, w, v), self.ref1(x, y, z, u, w, v) ** -7.8, 15, "Function6D power scalar (f() ** K) did not match reference function value.") + for (x, y, z, u, w, v) in itertools.product(testvals, repeat=6): + self.assertAlmostEqual(r1(x, y, z, u, w, v), 5. ** self.ref1(x, y, z, u, w, v), 15, "Function6D power scalar (K ** f()) did not match reference function value.") + if self.ref1(x, y, z, u, w, v) < 0: + with self.assertRaises(ValueError, msg="ValueError not raised when base is negative and exponent non-integral."): + r2(x, y, z, u, w, v) + elif not float(self.ref1(x, y, z, u, w, v)).is_integer(): + with self.assertRaises(ValueError, msg="ValueError not raised when base is negative and exponent non-integral."): + r3(x, y, z, u, w, v) + else: + if self.ref1(x, y, z, u, w, v) == 0: + with self.assertRaises(ZeroDivisionError, msg="ZeroDivisionError not raised when base is 0 and exponent negative."): + r2(x, y, z, u, w, v) + else: + self.assertAlmostEqual(r2(x, y, z, u, w, v), self.ref1(x, y, z, u, w, v) ** -7.8, 15, "Function6D power scalar (f() ** K) did not match reference function value.") with self.assertRaises(ZeroDivisionError, msg="ZeroDivisionError not raised when base is 0 and exponent negative."): r2(0, 0, 0, 0, 0, 0) with self.assertRaises(ZeroDivisionError, msg="ZeroDivisionError not raised when base is zero and exponent negative."): @@ -180,187 +141,162 @@ def test_pow_function6d_scalar(self): def test_richcmp_scalar(self): testvals = [-1e10, -7, -0.001, 0.0, 0.00003, 10, 2.3e49] - for x in testvals: - for y in testvals: - for z in testvals: - for u in testvals: - for w in testvals: - for v in testvals: - ref_value = self.ref1(x, y, z, u, w, v) - higher_value = ref_value + abs(ref_value) + 1 - lower_value = ref_value - abs(ref_value) - 1 - self.assertEqual( - (self.f1 == ref_value)(x, y, z, u, w, v), 1.0, - msg="Function6D equals scalar (f() == K) did not return true when it should." - ) - self.assertEqual( - (ref_value == self.f1)(x, y, z, u, w, v), 1.0, - msg="Scalar equals Function6D (K == f()) did not return true when it should." - ) - self.assertEqual( - (self.f1 == higher_value)(x, y, z, u, w, v), 0.0, - msg="Function6D equals scalar (f() == K) did not return false when it should." - ) - self.assertEqual( - (higher_value == self.f1)(x, y, z, u, w, v), 0.0, - msg="Scalar equals Function6D (K == f()) did not return false when it should." - ) - self.assertEqual( - (self.f1 != higher_value)(x, y, z, u, w, v), 1.0, - msg="Function6D not equals scalar (f() != K) did not return true when it should." - ) - self.assertEqual( - (higher_value != self.f1)(x, y, z, u, w, v), 1.0, - msg="Scalar not equals Function6D (K != f()) did not return true when it should." - ) - self.assertEqual( - (self.f1 != ref_value)(x, y, z, u, w, v), 0.0, - msg="Function6D not equals scalar (f() != K) did not return false when it should." - ) - self.assertEqual( - (ref_value != self.f1)(x, y, z, u, w, v), 0.0, - msg="Scalar not equals Function6D (K != f()) did not return false when it should." - ) - self.assertEqual( - (self.f1 < higher_value)(x, y, z, u, w, v), 1.0, - msg="Function6D less than scalar (f() < K) did not return true when it should." - ) - self.assertEqual( - (lower_value < self.f1)(x, y, z, u, w, v), 1.0, - msg="Scalar less than Function6D (K < f()) did not return true when it should." - ) - self.assertEqual( - (self.f1 < lower_value)(x, y, z, u, w, v), 0.0, - msg="Function6D less than scalar (f() < K) did not return false when it should." - ) - self.assertEqual( - (higher_value < self.f1)(x, y, z, u, w, v), 0.0, - msg="Scalar less than Function6D (K < f()) did not return false when it should." - ) - self.assertEqual( - (self.f1 > lower_value)(x, y, z, u, w, v), 1.0, - msg="Function6D greater than scalar (f() > K) did not return true when it should." - ) - self.assertEqual( - (higher_value > self.f1)(x, y, z, u, w, v), 1.0, - msg="Scalar greater than Function6D (K > f()) did not return true when it should." - ) - self.assertEqual( - (self.f1 > higher_value)(x, y, z, u, w, v), 0.0, - msg="Function6D greater than scalar (f() > K) did not return false when it should." - ) - self.assertEqual( - (lower_value > self.f1)(x, y, z, u, w, v), 0.0, - msg="Scalar greater than Function6D (K > f()) did not return false when it should." - ) - self.assertEqual( - (self.f1 <= higher_value)(x, y, z, u, w, v), 1.0, - msg="Function6D less equals scalar (f() <= K) did not return true when it should." - ) - self.assertEqual( - (lower_value <= self.f1)(x, y, z, u, w, v), 1.0, - msg="Scalar less equals Function6D (K <= f()) did not return true when it should." - ) - self.assertEqual( - (self.f1 <= ref_value)(x, y, z, u, w, v), 1.0, - msg="Function6D less equals scalar (f() <= K) did not return true when it should." - ) - self.assertEqual( - (ref_value <= self.f1)(x, y, z, u, w, v), 1.0, - msg="Scalar less equals Function6D (K <= f()) did not return true when it should." - ) - self.assertEqual( - (self.f1 <= lower_value)(x, y, z, u, w, v), 0.0, - msg="Function6D less equals scalar (f() <= K) did not return false when it should." - ) - self.assertEqual( - (higher_value <= self.f1)(x, y, z, u, w, v), 0.0, - msg="Scalar less equals Function6D (K <= f()) did not return false when it should." - ) - self.assertEqual( - (self.f1 >= lower_value)(x, y, z, u, w, v), 1.0, - msg="Function6D greater equals scalar (f() >= K) did not return true when it should." - ) - self.assertEqual( - (higher_value >= self.f1)(x, y, z, u, w, v), 1.0, - msg="Scalar greater equals Function6D (K >= f()) did not return true when it should." - ) - self.assertEqual( - (self.f1 >= ref_value)(x, y, z, u, w, v), 1.0, - msg="Function6D greater equals scalar (f() >= K) did not return true when it should." - ) - self.assertEqual( - (ref_value >= self.f1)(x, y, z, u, w, v), 1.0, - msg="Scalar greater equals Function6D (K >= f()) did not return true when it should." - ) - self.assertEqual( - (self.f1 >= higher_value)(x, y, z, u, w, v), 0.0, - msg="Function6D greater equals scalar (f() >= K) did not return false when it should." - ) - self.assertEqual( - (lower_value >= self.f1)(x, y, z, u, w, v), 0.0, - msg="Scalar greater equals Function6D (K >= f()) did not return false when it should." - ) + for (x, y, z, u, w, v) in itertools.product(testvals, repeat=6): + ref_value = self.ref1(x, y, z, u, w, v) + higher_value = ref_value + abs(ref_value) + 1 + lower_value = ref_value - abs(ref_value) - 1 + self.assertEqual( + (self.f1 == ref_value)(x, y, z, u, w, v), 1.0, + msg="Function6D equals scalar (f() == K) did not return true when it should." + ) + self.assertEqual( + (ref_value == self.f1)(x, y, z, u, w, v), 1.0, + msg="Scalar equals Function6D (K == f()) did not return true when it should." + ) + self.assertEqual( + (self.f1 == higher_value)(x, y, z, u, w, v), 0.0, + msg="Function6D equals scalar (f() == K) did not return false when it should." + ) + self.assertEqual( + (higher_value == self.f1)(x, y, z, u, w, v), 0.0, + msg="Scalar equals Function6D (K == f()) did not return false when it should." + ) + self.assertEqual( + (self.f1 != higher_value)(x, y, z, u, w, v), 1.0, + msg="Function6D not equals scalar (f() != K) did not return true when it should." + ) + self.assertEqual( + (higher_value != self.f1)(x, y, z, u, w, v), 1.0, + msg="Scalar not equals Function6D (K != f()) did not return true when it should." + ) + self.assertEqual( + (self.f1 != ref_value)(x, y, z, u, w, v), 0.0, + msg="Function6D not equals scalar (f() != K) did not return false when it should." + ) + self.assertEqual( + (ref_value != self.f1)(x, y, z, u, w, v), 0.0, + msg="Scalar not equals Function6D (K != f()) did not return false when it should." + ) + self.assertEqual( + (self.f1 < higher_value)(x, y, z, u, w, v), 1.0, + msg="Function6D less than scalar (f() < K) did not return true when it should." + ) + self.assertEqual( + (lower_value < self.f1)(x, y, z, u, w, v), 1.0, + msg="Scalar less than Function6D (K < f()) did not return true when it should." + ) + self.assertEqual( + (self.f1 < lower_value)(x, y, z, u, w, v), 0.0, + msg="Function6D less than scalar (f() < K) did not return false when it should." + ) + self.assertEqual( + (higher_value < self.f1)(x, y, z, u, w, v), 0.0, + msg="Scalar less than Function6D (K < f()) did not return false when it should." + ) + self.assertEqual( + (self.f1 > lower_value)(x, y, z, u, w, v), 1.0, + msg="Function6D greater than scalar (f() > K) did not return true when it should." + ) + self.assertEqual( + (higher_value > self.f1)(x, y, z, u, w, v), 1.0, + msg="Scalar greater than Function6D (K > f()) did not return true when it should." + ) + self.assertEqual( + (self.f1 > higher_value)(x, y, z, u, w, v), 0.0, + msg="Function6D greater than scalar (f() > K) did not return false when it should." + ) + self.assertEqual( + (lower_value > self.f1)(x, y, z, u, w, v), 0.0, + msg="Scalar greater than Function6D (K > f()) did not return false when it should." + ) + self.assertEqual( + (self.f1 <= higher_value)(x, y, z, u, w, v), 1.0, + msg="Function6D less equals scalar (f() <= K) did not return true when it should." + ) + self.assertEqual( + (lower_value <= self.f1)(x, y, z, u, w, v), 1.0, + msg="Scalar less equals Function6D (K <= f()) did not return true when it should." + ) + self.assertEqual( + (self.f1 <= ref_value)(x, y, z, u, w, v), 1.0, + msg="Function6D less equals scalar (f() <= K) did not return true when it should." + ) + self.assertEqual( + (ref_value <= self.f1)(x, y, z, u, w, v), 1.0, + msg="Scalar less equals Function6D (K <= f()) did not return true when it should." + ) + self.assertEqual( + (self.f1 <= lower_value)(x, y, z, u, w, v), 0.0, + msg="Function6D less equals scalar (f() <= K) did not return false when it should." + ) + self.assertEqual( + (higher_value <= self.f1)(x, y, z, u, w, v), 0.0, + msg="Scalar less equals Function6D (K <= f()) did not return false when it should." + ) + self.assertEqual( + (self.f1 >= lower_value)(x, y, z, u, w, v), 1.0, + msg="Function6D greater equals scalar (f() >= K) did not return true when it should." + ) + self.assertEqual( + (higher_value >= self.f1)(x, y, z, u, w, v), 1.0, + msg="Scalar greater equals Function6D (K >= f()) did not return true when it should." + ) + self.assertEqual( + (self.f1 >= ref_value)(x, y, z, u, w, v), 1.0, + msg="Function6D greater equals scalar (f() >= K) did not return true when it should." + ) + self.assertEqual( + (ref_value >= self.f1)(x, y, z, u, w, v), 1.0, + msg="Scalar greater equals Function6D (K >= f()) did not return true when it should." + ) + self.assertEqual( + (self.f1 >= higher_value)(x, y, z, u, w, v), 0.0, + msg="Function6D greater equals scalar (f() >= K) did not return false when it should." + ) + self.assertEqual( + (lower_value >= self.f1)(x, y, z, u, w, v), 0.0, + msg="Scalar greater equals Function6D (K >= f()) did not return false when it should." + ) def test_add_function6d(self): testvals = [-1e10, -7, -0.001, 0.0, 0.00003, 10, 2.3e49] r1 = self.f1 + self.f2 r2 = self.ref1 + self.f2 r3 = self.f1 + self.ref2 - for x in testvals: - for y in testvals: - for z in testvals: - for u in testvals: - for w in testvals: - for v in testvals: - self.assertEqual(r1(x, y, z, u, w, v), self.ref1(x, y, z, u, w, v) + self.ref2(x, y, z, u, w, v), "Function6D add function (f1() + f2()) did not match reference function value.") - self.assertEqual(r2(x, y, z, u, w, v), self.ref1(x, y, z, u, w, v) + self.ref2(x, y, z, u, w, v), "Function6D add function (p1() + f2()) did not match reference function value.") - self.assertEqual(r3(x, y, z, u, w, v), self.ref1(x, y, z, u, w, v) + self.ref2(x, y, z, u, w, v), "Function6D add function (f1() + p2()) did not match reference function value.") + for (x, y, z, u, w, v) in itertools.product(testvals, repeat=6): + self.assertEqual(r1(x, y, z, u, w, v), self.ref1(x, y, z, u, w, v) + self.ref2(x, y, z, u, w, v), "Function6D add function (f1() + f2()) did not match reference function value.") + self.assertEqual(r2(x, y, z, u, w, v), self.ref1(x, y, z, u, w, v) + self.ref2(x, y, z, u, w, v), "Function6D add function (p1() + f2()) did not match reference function value.") + self.assertEqual(r3(x, y, z, u, w, v), self.ref1(x, y, z, u, w, v) + self.ref2(x, y, z, u, w, v), "Function6D add function (f1() + p2()) did not match reference function value.") def test_sub_function6d(self): testvals = [-1e10, -7, -0.001, 0.0, 0.00003, 10, 2.3e49] r1 = self.f1 - self.f2 r2 = self.ref1 - self.f2 r3 = self.f1 - self.ref2 - for x in testvals: - for y in testvals: - for z in testvals: - for u in testvals: - for w in testvals: - for v in testvals: - self.assertEqual(r1(x, y, z, u, w, v), self.ref1(x, y, z, u, w, v) - self.ref2(x, y, z, u, w, v), "Function6D subtract function (f1() - f2()) did not match reference function value.") - self.assertEqual(r2(x, y, z, u, w, v), self.ref1(x, y, z, u, w, v) - self.ref2(x, y, z, u, w, v), "Function6D subtract function (p1() - f2()) did not match reference function value.") - self.assertEqual(r3(x, y, z, u, w, v), self.ref1(x, y, z, u, w, v) - self.ref2(x, y, z, u, w, v), "Function6D subtract function (f1() - p2()) did not match reference function value.") + for (x, y, z, u, w, v) in itertools.product(testvals, repeat=6): + self.assertEqual(r1(x, y, z, u, w, v), self.ref1(x, y, z, u, w, v) - self.ref2(x, y, z, u, w, v), "Function6D subtract function (f1() - f2()) did not match reference function value.") + self.assertEqual(r2(x, y, z, u, w, v), self.ref1(x, y, z, u, w, v) - self.ref2(x, y, z, u, w, v), "Function6D subtract function (p1() - f2()) did not match reference function value.") + self.assertEqual(r3(x, y, z, u, w, v), self.ref1(x, y, z, u, w, v) - self.ref2(x, y, z, u, w, v), "Function6D subtract function (f1() - p2()) did not match reference function value.") def test_mul_function6d(self): testvals = [-1e10, -7, -0.001, 0.0, 0.00003, 10, 2.3e49] r1 = self.f1 * self.f2 r2 = self.ref1 * self.f2 r3 = self.f1 * self.ref2 - for x in testvals: - for y in testvals: - for z in testvals: - for u in testvals: - for w in testvals: - for v in testvals: - self.assertEqual(r1(x, y, z, u, w, v), self.ref1(x, y, z, u, w, v) * self.ref2(x, y, z, u, w, v), "Function6D multiply function (f1() * f2()) did not match reference function value.") - self.assertEqual(r2(x, y, z, u, w, v), self.ref1(x, y, z, u, w, v) * self.ref2(x, y, z, u, w, v), "Function6D multiply function (p1() * f2()) did not match reference function value.") - self.assertEqual(r3(x, y, z, u, w, v), self.ref1(x, y, z, u, w, v) * self.ref2(x, y, z, u, w, v), "Function6D multiply function (f1() * p2()) did not match reference function value.") + for (x, y, z, u, w, v) in itertools.product(testvals, repeat=6): + self.assertEqual(r1(x, y, z, u, w, v), self.ref1(x, y, z, u, w, v) * self.ref2(x, y, z, u, w, v), "Function6D multiply function (f1() * f2()) did not match reference function value.") + self.assertEqual(r2(x, y, z, u, w, v), self.ref1(x, y, z, u, w, v) * self.ref2(x, y, z, u, w, v), "Function6D multiply function (p1() * f2()) did not match reference function value.") + self.assertEqual(r3(x, y, z, u, w, v), self.ref1(x, y, z, u, w, v) * self.ref2(x, y, z, u, w, v), "Function6D multiply function (f1() * p2()) did not match reference function value.") def test_div_function6d(self): testvals = [-1e10, -7, -0.001, 0.00003, 10, 2.3e49] r1 = self.f1 / self.f2 r2 = self.ref1 / self.f2 r3 = self.f1 / self.ref2 - for x in testvals: - for y in testvals: - for z in testvals: - for u in testvals: - for w in testvals: - for v in testvals: - self.assertAlmostEqual(r1(x, y, z, u, w, v), self.ref1(x, y, z, u, w, v) / self.ref2(x, y, z, u, w, v), delta=abs(r1(x, y, z, u, w, v)) * 1e-12, msg="Function6D divide function (f1() / f2()) did not match reference function value.") - self.assertAlmostEqual(r2(x, y, z, u, w, v), self.ref1(x, y, z, u, w, v) / self.ref2(x, y, z, u, w, v), delta=abs(r2(x, y, z, u, w, v)) * 1e-12, msg="Function6D divide function (p1() / f2()) did not match reference function value.") - self.assertAlmostEqual(r3(x, y, z, u, w, v), self.ref1(x, y, z, u, w, v) / self.ref2(x, y, z, u, w, v), delta=abs(r3(x, y, z, u, w, v)) * 1e-12, msg="Function6D divide function (f1() / p2()) did not match reference function value.") + for (x, y, z, u, w, v) in itertools.product(testvals, repeat=6): + self.assertAlmostEqual(r1(x, y, z, u, w, v), self.ref1(x, y, z, u, w, v) / self.ref2(x, y, z, u, w, v), delta=abs(r1(x, y, z, u, w, v)) * 1e-12, msg="Function6D divide function (f1() / f2()) did not match reference function value.") + self.assertAlmostEqual(r2(x, y, z, u, w, v), self.ref1(x, y, z, u, w, v) / self.ref2(x, y, z, u, w, v), delta=abs(r2(x, y, z, u, w, v)) * 1e-12, msg="Function6D divide function (p1() / f2()) did not match reference function value.") + self.assertAlmostEqual(r3(x, y, z, u, w, v), self.ref1(x, y, z, u, w, v) / self.ref2(x, y, z, u, w, v), delta=abs(r3(x, y, z, u, w, v)) * 1e-12, msg="Function6D divide function (f1() / p2()) did not match reference function value.") with self.assertRaises(ZeroDivisionError, msg="ZeroDivisionError not raised when function returns zero."): r1(0, 0, 0, 0, 0, 0) @@ -370,15 +306,10 @@ def test_mod_function6d(self): r1 = self.f1 % self.f2 r2 = self.ref1 % self.f2 r3 = self.f1 % self.ref2 - for x in testvals: - for y in testvals: - for z in testvals: - for u in testvals: - for w in testvals: - for v in testvals: - self.assertAlmostEqual(r1(x, y, z, u, w, v), math.fmod(self.ref1(x, y, z, u, w, v), self.ref2(x, y, z, u, w, v)), delta=abs(r1(x, y, z, u, w, v)) * 1e-12, msg="Function6D modulo function (f1() % f2()) did not match reference function value.") - self.assertAlmostEqual(r2(x, y, z, u, w, v), math.fmod(self.ref1(x, y, z, u, w, v), self.ref2(x, y, z, u, w, v)), delta=abs(r2(x, y, z, u, w, v)) * 1e-12, msg="Function6D modulo function (p1() % f2()) did not match reference function value.") - self.assertAlmostEqual(r3(x, y, z, u, w, v), math.fmod(self.ref1(x, y, z, u, w, v), self.ref2(x, y, z, u, w, v)), delta=abs(r3(x, y, z, u, w, v)) * 1e-12, msg="Function6D modulo function (f1() % p2()) did not match reference function value.") + for (x, y, z, u, w, v) in itertools.product(testvals, repeat=6): + self.assertAlmostEqual(r1(x, y, z, u, w, v), math.fmod(self.ref1(x, y, z, u, w, v), self.ref2(x, y, z, u, w, v)), delta=abs(r1(x, y, z, u, w, v)) * 1e-12, msg="Function6D modulo function (f1() % f2()) did not match reference function value.") + self.assertAlmostEqual(r2(x, y, z, u, w, v), math.fmod(self.ref1(x, y, z, u, w, v), self.ref2(x, y, z, u, w, v)), delta=abs(r2(x, y, z, u, w, v)) * 1e-12, msg="Function6D modulo function (p1() % f2()) did not match reference function value.") + self.assertAlmostEqual(r3(x, y, z, u, w, v), math.fmod(self.ref1(x, y, z, u, w, v), self.ref2(x, y, z, u, w, v)), delta=abs(r3(x, y, z, u, w, v)) * 1e-12, msg="Function6D modulo function (f1() % p2()) did not match reference function value.") with self.assertRaises(ZeroDivisionError, msg="ZeroDivisionError not raised when function returns zero."): r1(0, 0, 0, 0, 0, 0) @@ -388,23 +319,18 @@ def test_pow_function6d_function6d(self): r1 = self.f1 ** self.f2 r2 = self.ref1 ** self.f2 r3 = self.f1 ** self.ref2 - for x in testvals: - for y in testvals: - for z in testvals: - for u in testvals: - for w in testvals: - for v in testvals: - if self.ref1(x, y, z, u, w, v) < 0 and not float(self.ref2(x, y, z, u, w, v)).is_integer(): - with self.assertRaises(ValueError, msg="ValueError not raised when base is negative and exponent non-integral (1/3)."): - r1(x, y, z, u, w, v) - with self.assertRaises(ValueError, msg="ValueError not raised when base is negative and exponent non-integral (2/3)."): - r2(x, y, z, u, w, v) - with self.assertRaises(ValueError, msg="ValueError not raised when base is negative and exponent non-integral (3/3)."): - r3(x, y, z, u, w, v) - else: - self.assertAlmostEqual(r1(x, y, z, u, w, v), self.ref1(x, y, z, u, w, v) ** self.ref2(x, y, z, u, w, v), 15, "Function6D power function (f1() ** f2()) did not match reference function value.") - self.assertAlmostEqual(r2(x, y, z, u, w, v), self.ref1(x, y, z, u, w, v) ** self.ref2(x, y, z, u, w, v), 15, "Function6D power function (p1() ** f2()) did not match reference function value.") - self.assertAlmostEqual(r3(x, y, z, u, w, v), self.ref1(x, y, z, u, w, v) ** self.ref2(x, y, z, u, w, v), 15, "Function6D power function (f1() ** p2()) did not match reference function value.") + for (x, y, z, u, w, v) in itertools.product(testvals, repeat=6): + if self.ref1(x, y, z, u, w, v) < 0 and not float(self.ref2(x, y, z, u, w, v)).is_integer(): + with self.assertRaises(ValueError, msg="ValueError not raised when base is negative and exponent non-integral (1/3)."): + r1(x, y, z, u, w, v) + with self.assertRaises(ValueError, msg="ValueError not raised when base is negative and exponent non-integral (2/3)."): + r2(x, y, z, u, w, v) + with self.assertRaises(ValueError, msg="ValueError not raised when base is negative and exponent non-integral (3/3)."): + r3(x, y, z, u, w, v) + else: + self.assertAlmostEqual(r1(x, y, z, u, w, v), self.ref1(x, y, z, u, w, v) ** self.ref2(x, y, z, u, w, v), 15, "Function6D power function (f1() ** f2()) did not match reference function value.") + self.assertAlmostEqual(r2(x, y, z, u, w, v), self.ref1(x, y, z, u, w, v) ** self.ref2(x, y, z, u, w, v), 15, "Function6D power function (p1() ** f2()) did not match reference function value.") + self.assertAlmostEqual(r3(x, y, z, u, w, v), self.ref1(x, y, z, u, w, v) ** self.ref2(x, y, z, u, w, v), 15, "Function6D power function (f1() ** p2()) did not match reference function value.") with self.assertRaises(ZeroDivisionError, msg="ZeroDivisionError not raised when f1() == 0 and f2() is negative."): r4 = PythonFunction6D(lambda x, y, z, u, w, v: 0) ** self.f1 @@ -420,230 +346,205 @@ def test_pow_3_arguments(self): r6 = pow(self.ref2, self.f1, self.f2) # Can't use 3 argument pow() if all arguments aren't integers, so # use fmod(a, b) % c instead - for x in testvals: - for y in testvals: - for z in testvals: - for u in testvals: - for w in testvals: - for v in testvals: - self.assertEqual(r1(x, y, z, u, w, v), math.fmod(self.ref1(x, y, z, u, w, v) ** 5, 3), "Function6D 3 argument pow(f1(), A, B) did not match reference value.") - self.assertEqual(r2(x, y, z, u, w, v), math.fmod(5 ** self.ref1(x, y, z, u, w, v), 3), "Function6D 3 argument pow(A, f1(), B) did not match reference value.") - self.assertEqual(r3(x, y, z, u, w, v), math.fmod(5 ** self.ref1(x, y, z, u, w, v), self.ref2(x, y, z, u, w, v)), "Function6D 3 argument pow(A, f1(), f2()) did not match reference value.") - self.assertEqual(r4(x, y, z, u, w, v), math.fmod(self.ref2(x, y, z, u, w, v) ** self.ref1(x, y, z, u, w, v), self.ref2(x, y, z, u, w, v)), "Function6D 3 argument pow(f2(), f1(), f2()) did not match reference value.") - self.assertEqual(r5(x, y, z, u, w, v), math.fmod(self.ref2(x, y, z, u, w, v) ** self.ref1(x, y, z, u, w, v), self.ref2(x, y, z, u, w, v)), "Function6D 3 argument pow(f2(), p1(), p2()) did not match reference value.") - self.assertEqual(r6(x, y, z, u, w, v), math.fmod(self.ref2(x, y, z, u, w, v) ** self.ref1(x, y, z, u, w, v), self.ref2(x, y, z, u, w, v)), "Function6D 3 argument pow(p2(), f1(), f2()) did not match reference value.") + for (x, y, z, u, w, v) in itertools.product(testvals, repeat=6): + self.assertEqual(r1(x, y, z, u, w, v), math.fmod(self.ref1(x, y, z, u, w, v) ** 5, 3), "Function6D 3 argument pow(f1(), A, B) did not match reference value.") + self.assertEqual(r2(x, y, z, u, w, v), math.fmod(5 ** self.ref1(x, y, z, u, w, v), 3), "Function6D 3 argument pow(A, f1(), B) did not match reference value.") + self.assertEqual(r3(x, y, z, u, w, v), math.fmod(5 ** self.ref1(x, y, z, u, w, v), self.ref2(x, y, z, u, w, v)), "Function6D 3 argument pow(A, f1(), f2()) did not match reference value.") + self.assertEqual(r4(x, y, z, u, w, v), math.fmod(self.ref2(x, y, z, u, w, v) ** self.ref1(x, y, z, u, w, v), self.ref2(x, y, z, u, w, v)), "Function6D 3 argument pow(f2(), f1(), f2()) did not match reference value.") + self.assertEqual(r5(x, y, z, u, w, v), math.fmod(self.ref2(x, y, z, u, w, v) ** self.ref1(x, y, z, u, w, v), self.ref2(x, y, z, u, w, v)), "Function6D 3 argument pow(f2(), p1(), p2()) did not match reference value.") + self.assertEqual(r6(x, y, z, u, w, v), math.fmod(self.ref2(x, y, z, u, w, v) ** self.ref1(x, y, z, u, w, v), self.ref2(x, y, z, u, w, v)), "Function6D 3 argument pow(p2(), f1(), f2()) did not match reference value.") def test_abs(self): testvals = [-1e10, -7, -0.001, 0.0, 0.0003, 10, 2.3e49] - for x in testvals: - for y in testvals: - for z in testvals: - for u in testvals: - for w in testvals: - for v in testvals: - self.assertEqual(abs(self.f1)(x, y, z, u, w, v), abs(self.ref1(x, y, z, u, w, v)), - msg="abs(Function6D) did not match reference value") + for (x, y, z, u, w, v) in itertools.product(testvals, repeat=6): + self.assertEqual(abs(self.f1)(x, y, z, u, w, v), abs(self.ref1(x, y, z, u, w, v)), + msg="abs(Function6D) did not match reference value") def test_richcmp_function_callable(self): testvals = [-1e10, -7, -0.001, 0.0, 0.00003, 10, 2.3e49] - for x in testvals: - for y in testvals: - for z in testvals: - for u in testvals: - for w in testvals: - for v in testvals: - ref_value = self.ref1 - higher_value = lambda x, y, z, u, w, v: self.ref1(x, y, z, u, w, v) + abs(self.ref1(x, y, z, u, w, v)) + 1 - lower_value = lambda x, y, z, u, w, v: self.ref1(x, y, z, u, w, v) - abs(self.ref1(x, y, z, u, w, v)) - 1 - self.assertEqual( - (self.f1 == ref_value)(x, y, z, u, w, v), 1.0, - msg="Function6D equals callable (f1() == f2()) did not return true when it should." - ) - self.assertEqual( - (self.f1 == higher_value)(x, y, z, u, w, v), 0.0, - msg="Function6D equals callable (f1() == f2()) did not return false when it should." - ) - self.assertEqual( - (self.f1 != higher_value)(x, y, z, u, w, v), 1.0, - msg="Function6D not equals callable (f1() != f2()) did not return true when it should." - ) - self.assertEqual( - (self.f1 != ref_value)(x, y, z, u, w, v), 0.0, - msg="Function6D not equals callable (f1() != f2()) did not return false when it should." - ) - self.assertEqual( - (self.f1 < higher_value)(x, y, z, u, w, v), 1.0, - msg="Function6D less than callable (f1() < f2()) did not return true when it should." - ) - self.assertEqual( - (self.f1 < lower_value)(x, y, z, u, w, v), 0.0, - msg="Function6D less than callable (f1() < f2()) did not return false when it should." - ) - self.assertEqual( - (self.f1 > lower_value)(x, y, z, u, w, v), 1.0, - msg="Function6D greater than callable (f1() > f2()) did not return true when it should." - ) - self.assertEqual( - (self.f1 > higher_value)(x, y, z, u, w, v), 0.0, - msg="Function6D greater than callable (f1() > f2()) did not return false when it should." - ) - self.assertEqual( - (self.f1 <= higher_value)(x, y, z, u, w, v), 1.0, - msg="Function6D less equals callable (f1() <= f2()) did not return true when it should." - ) - self.assertEqual( - (self.f1 <= ref_value)(x, y, z, u, w, v), 1.0, - msg="Function6D less equals callable (f1() <= f2()) did not return true when it should." - ) - self.assertEqual( - (self.f1 <= lower_value)(x, y, z, u, w, v), 0.0, - msg="Function6D less equals callable (f1() <= f2()) did not return false when it should." - ) - self.assertEqual( - (self.f1 >= lower_value)(x, y, z, u, w, v), 1.0, - msg="Function6D equals callable (f1() >= f2()) did not return true when it should." - ) - self.assertEqual( - (self.f1 >= ref_value)(x, y, z, u, w, v), 1.0, - msg="Function6D greater equals callable (f1() >= f2()) did not return true when it should." - ) - self.assertEqual( - (self.f1 >= higher_value)(x, y, z, u, w, v), 0.0, - msg="Function6D equals callable (f1() >= f2()) did not return false when it should." - ) + for (x, y, z, u, w, v) in itertools.product(testvals, repeat=6): + ref_value = self.ref1 + higher_value = lambda x, y, z, u, w, v: self.ref1(x, y, z, u, w, v) + abs(self.ref1(x, y, z, u, w, v)) + 1 + lower_value = lambda x, y, z, u, w, v: self.ref1(x, y, z, u, w, v) - abs(self.ref1(x, y, z, u, w, v)) - 1 + self.assertEqual( + (self.f1 == ref_value)(x, y, z, u, w, v), 1.0, + msg="Function6D equals callable (f1() == f2()) did not return true when it should." + ) + self.assertEqual( + (self.f1 == higher_value)(x, y, z, u, w, v), 0.0, + msg="Function6D equals callable (f1() == f2()) did not return false when it should." + ) + self.assertEqual( + (self.f1 != higher_value)(x, y, z, u, w, v), 1.0, + msg="Function6D not equals callable (f1() != f2()) did not return true when it should." + ) + self.assertEqual( + (self.f1 != ref_value)(x, y, z, u, w, v), 0.0, + msg="Function6D not equals callable (f1() != f2()) did not return false when it should." + ) + self.assertEqual( + (self.f1 < higher_value)(x, y, z, u, w, v), 1.0, + msg="Function6D less than callable (f1() < f2()) did not return true when it should." + ) + self.assertEqual( + (self.f1 < lower_value)(x, y, z, u, w, v), 0.0, + msg="Function6D less than callable (f1() < f2()) did not return false when it should." + ) + self.assertEqual( + (self.f1 > lower_value)(x, y, z, u, w, v), 1.0, + msg="Function6D greater than callable (f1() > f2()) did not return true when it should." + ) + self.assertEqual( + (self.f1 > higher_value)(x, y, z, u, w, v), 0.0, + msg="Function6D greater than callable (f1() > f2()) did not return false when it should." + ) + self.assertEqual( + (self.f1 <= higher_value)(x, y, z, u, w, v), 1.0, + msg="Function6D less equals callable (f1() <= f2()) did not return true when it should." + ) + self.assertEqual( + (self.f1 <= ref_value)(x, y, z, u, w, v), 1.0, + msg="Function6D less equals callable (f1() <= f2()) did not return true when it should." + ) + self.assertEqual( + (self.f1 <= lower_value)(x, y, z, u, w, v), 0.0, + msg="Function6D less equals callable (f1() <= f2()) did not return false when it should." + ) + self.assertEqual( + (self.f1 >= lower_value)(x, y, z, u, w, v), 1.0, + msg="Function6D equals callable (f1() >= f2()) did not return true when it should." + ) + self.assertEqual( + (self.f1 >= ref_value)(x, y, z, u, w, v), 1.0, + msg="Function6D greater equals callable (f1() >= f2()) did not return true when it should." + ) + self.assertEqual( + (self.f1 >= higher_value)(x, y, z, u, w, v), 0.0, + msg="Function6D equals callable (f1() >= f2()) did not return false when it should." + ) def test_richcmp_callable_function(self): testvals = [-1e10, -7, -0.001, 0.0, 0.00003, 10, 2.3e49] - for x in testvals: - for y in testvals: - for z in testvals: - for u in testvals: - for w in testvals: - for v in testvals: - ref_value = self.ref1 - higher_value = lambda x, y, z, u, w, v: self.ref1(x, y, z, u, w, v) + abs(self.ref1(x, y, z, u, w, v)) + 1 - lower_value = lambda x, y, z, u, w, v: self.ref1(x, y, z, u, w, v) - abs(self.ref1(x, y, z, u, w, v)) - 1 - self.assertEqual( - (ref_value == self.f1)(x, y, z, u, w, v), 1.0, - msg="Callable equals Function6D (f1() == f2()) did not return true when it should." - ) - self.assertEqual( - (higher_value == self.f1)(x, y, z, u, w, v), 0.0, - msg="Callable equals Function6D (f1() == f2()) did not return false when it should." - ) - self.assertEqual( - (higher_value != self.f1)(x, y, z, u, w, v), 1.0, - msg="Callable not equals Function6D (f1() != f2()) did not return true when it should." - ) - self.assertEqual( - (ref_value != self.f1)(x, y, z, u, w, v), 0.0, - msg="Callable not equals Function6D (f1() != f2()) did not return false when it should." - ) - self.assertEqual( - (lower_value < self.f1)(x, y, z, u, w, v), 1.0, - msg="Callable less than Function6D (f1() < f2()) did not return true when it should." - ) - self.assertEqual( - (higher_value < self.f1)(x, y, z, u, w, v), 0.0, - msg="Callable less than Function6D (f1() < f2()) did not return false when it should." - ) - self.assertEqual( - (higher_value > self.f1)(x, y, z, u, w, v), 1.0, - msg="Callable greater than Function6D (f1() > f2()) did not return true when it should." - ) - self.assertEqual( - (lower_value > self.f1)(x, y, z, u, w, v), 0.0, - msg="Callable greater than Function6D (f1() > f2()) did not return false when it should." - ) - self.assertEqual( - (lower_value <= self.f1)(x, y, z, u, w, v), 1.0, - msg="Callable less equals Function6D (f1() <= f2()) did not return true when it should." - ) - self.assertEqual( - (ref_value <= self.f1)(x, y, z, u, w, v), 1.0, - msg="Callable less equals Function6D (f1() <= f2()) did not return true when it should." - ) - self.assertEqual( - (higher_value <= self.f1)(x, y, z, u, w, v), 0.0, - msg="Callable less equals Function6D (f1() <= f2()) did not return false when it should." - ) - self.assertEqual( - (higher_value >= self.f1)(x, y, z, u, w, v), 1.0, - msg="Callable equals Function6D (f1() >= f2()) did not return true when it should." - ) - self.assertEqual( - (ref_value >= self.f1)(x, y, z, u, w, v), 1.0, - msg="Callable greater equals Function6D (f1() >= f2()) did not return true when it should." - ) - self.assertEqual( - (lower_value >= self.f1)(x, y, z, u, w, v), 0.0, - msg="Callable equals Function6D (f1() >= f2()) did not return false when it should." - ) + for (x, y, z, u, w, v) in itertools.product(testvals, repeat=6): + ref_value = self.ref1 + higher_value = lambda x, y, z, u, w, v: self.ref1(x, y, z, u, w, v) + abs(self.ref1(x, y, z, u, w, v)) + 1 + lower_value = lambda x, y, z, u, w, v: self.ref1(x, y, z, u, w, v) - abs(self.ref1(x, y, z, u, w, v)) - 1 + self.assertEqual( + (ref_value == self.f1)(x, y, z, u, w, v), 1.0, + msg="Callable equals Function6D (f1() == f2()) did not return true when it should." + ) + self.assertEqual( + (higher_value == self.f1)(x, y, z, u, w, v), 0.0, + msg="Callable equals Function6D (f1() == f2()) did not return false when it should." + ) + self.assertEqual( + (higher_value != self.f1)(x, y, z, u, w, v), 1.0, + msg="Callable not equals Function6D (f1() != f2()) did not return true when it should." + ) + self.assertEqual( + (ref_value != self.f1)(x, y, z, u, w, v), 0.0, + msg="Callable not equals Function6D (f1() != f2()) did not return false when it should." + ) + self.assertEqual( + (lower_value < self.f1)(x, y, z, u, w, v), 1.0, + msg="Callable less than Function6D (f1() < f2()) did not return true when it should." + ) + self.assertEqual( + (higher_value < self.f1)(x, y, z, u, w, v), 0.0, + msg="Callable less than Function6D (f1() < f2()) did not return false when it should." + ) + self.assertEqual( + (higher_value > self.f1)(x, y, z, u, w, v), 1.0, + msg="Callable greater than Function6D (f1() > f2()) did not return true when it should." + ) + self.assertEqual( + (lower_value > self.f1)(x, y, z, u, w, v), 0.0, + msg="Callable greater than Function6D (f1() > f2()) did not return false when it should." + ) + self.assertEqual( + (lower_value <= self.f1)(x, y, z, u, w, v), 1.0, + msg="Callable less equals Function6D (f1() <= f2()) did not return true when it should." + ) + self.assertEqual( + (ref_value <= self.f1)(x, y, z, u, w, v), 1.0, + msg="Callable less equals Function6D (f1() <= f2()) did not return true when it should." + ) + self.assertEqual( + (higher_value <= self.f1)(x, y, z, u, w, v), 0.0, + msg="Callable less equals Function6D (f1() <= f2()) did not return false when it should." + ) + self.assertEqual( + (higher_value >= self.f1)(x, y, z, u, w, v), 1.0, + msg="Callable equals Function6D (f1() >= f2()) did not return true when it should." + ) + self.assertEqual( + (ref_value >= self.f1)(x, y, z, u, w, v), 1.0, + msg="Callable greater equals Function6D (f1() >= f2()) did not return true when it should." + ) + self.assertEqual( + (lower_value >= self.f1)(x, y, z, u, w, v), 0.0, + msg="Callable equals Function6D (f1() >= f2()) did not return false when it should." + ) def test_richcmp_function_function(self): testvals = [-1e10, -7, -0.001, 0.0, 0.00003, 10, 2.3e49] - for x in testvals: - for y in testvals: - for z in testvals: - for u in testvals: - for w in testvals: - for v in testvals: - ref_value = self.f1 - higher_value = self.f1 + abs(self.f1) + 1 - lower_value = self.f1 - abs(self.f1) - 1 - self.assertEqual( - (self.f1 == ref_value)(x, y, z, u, w, v), 1.0, - msg="Function6D equals Function6D (f1() == f2()) did not return true when it should." - ) - self.assertEqual( - (self.f1 == higher_value)(x, y, z, u, w, v), 0.0, - msg="Function6D equals Function6D (f1() == f2()) did not return false when it should." - ) - self.assertEqual( - (self.f1 != higher_value)(x, y, z, u, w, v), 1.0, - msg="Function6D not equals Function6D (f1() != f2()) did not return true when it should." - ) - self.assertEqual( - (self.f1 != ref_value)(x, y, z, u, w, v), 0.0, - msg="Function6D not equals Function6D (f1() != f2()) did not return false when it should." - ) - self.assertEqual( - (self.f1 < higher_value)(x, y, z, u, w, v), 1.0, - msg="Function6D less than Function6D (f1() < f2()) did not return true when it should." - ) - self.assertEqual( - (self.f1 < lower_value)(x, y, z, u, w, v), 0.0, - msg="Function6D less than Function6D (f1() < f2()) did not return false when it should." - ) - self.assertEqual( - (self.f1 > lower_value)(x, y, z, u, w, v), 1.0, - msg="Function6D greater than Function6D (f1() > f2()) did not return true when it should." - ) - self.assertEqual( - (self.f1 > higher_value)(x, y, z, u, w, v), 0.0, - msg="Function6D greater than Function6D (f1() > f2()) did not return false when it should." - ) - self.assertEqual( - (self.f1 <= higher_value)(x, y, z, u, w, v), 1.0, - msg="Function6D less equals Function6D (f1() <= f2()) did not return true when it should." - ) - self.assertEqual( - (self.f1 <= ref_value)(x, y, z, u, w, v), 1.0, - msg="Function6D less equals Function6D (f1() <= f2()) did not return true when it should." - ) - self.assertEqual( - (self.f1 <= lower_value)(x, y, z, u, w, v), 0.0, - msg="Function6D less equals Function6D (f1() <= f2()) did not return false when it should." - ) - self.assertEqual( - (self.f1 >= lower_value)(x, y, z, u, w, v), 1.0, - msg="Function6D equals Function6D (f1() >= f2()) did not return true when it should." - ) - self.assertEqual( - (self.f1 >= ref_value)(x, y, z, u, w, v), 1.0, - msg="Function6D greater equals Function6D (f1() >= f2()) did not return true when it should." - ) - self.assertEqual( - (self.f1 >= higher_value)(x, y, z, u, w, v), 0.0, - msg="Function6D equals Function6D (f1() >= f2()) did not return false when it should." - ) + for (x, y, z, u, w, v) in itertools.product(testvals, repeat=6): + ref_value = self.f1 + higher_value = self.f1 + abs(self.f1) + 1 + lower_value = self.f1 - abs(self.f1) - 1 + self.assertEqual( + (self.f1 == ref_value)(x, y, z, u, w, v), 1.0, + msg="Function6D equals Function6D (f1() == f2()) did not return true when it should." + ) + self.assertEqual( + (self.f1 == higher_value)(x, y, z, u, w, v), 0.0, + msg="Function6D equals Function6D (f1() == f2()) did not return false when it should." + ) + self.assertEqual( + (self.f1 != higher_value)(x, y, z, u, w, v), 1.0, + msg="Function6D not equals Function6D (f1() != f2()) did not return true when it should." + ) + self.assertEqual( + (self.f1 != ref_value)(x, y, z, u, w, v), 0.0, + msg="Function6D not equals Function6D (f1() != f2()) did not return false when it should." + ) + self.assertEqual( + (self.f1 < higher_value)(x, y, z, u, w, v), 1.0, + msg="Function6D less than Function6D (f1() < f2()) did not return true when it should." + ) + self.assertEqual( + (self.f1 < lower_value)(x, y, z, u, w, v), 0.0, + msg="Function6D less than Function6D (f1() < f2()) did not return false when it should." + ) + self.assertEqual( + (self.f1 > lower_value)(x, y, z, u, w, v), 1.0, + msg="Function6D greater than Function6D (f1() > f2()) did not return true when it should." + ) + self.assertEqual( + (self.f1 > higher_value)(x, y, z, u, w, v), 0.0, + msg="Function6D greater than Function6D (f1() > f2()) did not return false when it should." + ) + self.assertEqual( + (self.f1 <= higher_value)(x, y, z, u, w, v), 1.0, + msg="Function6D less equals Function6D (f1() <= f2()) did not return true when it should." + ) + self.assertEqual( + (self.f1 <= ref_value)(x, y, z, u, w, v), 1.0, + msg="Function6D less equals Function6D (f1() <= f2()) did not return true when it should." + ) + self.assertEqual( + (self.f1 <= lower_value)(x, y, z, u, w, v), 0.0, + msg="Function6D less equals Function6D (f1() <= f2()) did not return false when it should." + ) + self.assertEqual( + (self.f1 >= lower_value)(x, y, z, u, w, v), 1.0, + msg="Function6D equals Function6D (f1() >= f2()) did not return true when it should." + ) + self.assertEqual( + (self.f1 >= ref_value)(x, y, z, u, w, v), 1.0, + msg="Function6D greater equals Function6D (f1() >= f2()) did not return true when it should." + ) + self.assertEqual( + (self.f1 >= higher_value)(x, y, z, u, w, v), 0.0, + msg="Function6D equals Function6D (f1() >= f2()) did not return false when it should." + ) diff --git a/cherab/core/math/function/float/function6d/tests/test_cmath.py b/cherab/core/math/function/float/function6d/tests/test_cmath.py index 5c9f7a81..257dd0b7 100644 --- a/cherab/core/math/function/float/function6d/tests/test_cmath.py +++ b/cherab/core/math/function/float/function6d/tests/test_cmath.py @@ -24,6 +24,7 @@ import math import unittest +import itertools import cherab.core.math.function.float.function6d.cmath as cmath6d from cherab.core.math.function.float.function6d.autowrap import PythonFunction6D @@ -36,51 +37,31 @@ def setUp(self): def test_exp(self): testvals = [-10.0, -7, -0.001, 0.0, 0.00003, 10, 23.4] - for x in testvals: - for y in testvals: - for z in testvals: - for u in testvals: - for w in testvals: - for v in testvals: - function = cmath6d.Exp6D(self.f1) - expected = math.exp(self.f1(x, y, z, u, w, v)) - self.assertEqual(function(x, y, z, u, w, v), expected, "Exp6D call did not match reference value.") + for (x, y, z, u, w, v) in itertools.product(testvals, repeat=6): + function = cmath6d.Exp6D(self.f1) + expected = math.exp(self.f1(x, y, z, u, w, v)) + self.assertEqual(function(x, y, z, u, w, v), expected, "Exp6D call did not match reference value.") def test_sin(self): testvals = [-10.0, -7, -0.001, 0.0, 0.00003, 10, 23.4] - for x in testvals: - for y in testvals: - for z in testvals: - for u in testvals: - for w in testvals: - for v in testvals: - function = cmath6d.Sin6D(self.f1) - expected = math.sin(self.f1(x, y, z, u, w, v)) - self.assertEqual(function(x, y, z, u, w, v), expected, "Sin6D call did not match reference value.") + for (x, y, z, u, w, v) in itertools.product(testvals, repeat=6): + function = cmath6d.Sin6D(self.f1) + expected = math.sin(self.f1(x, y, z, u, w, v)) + self.assertEqual(function(x, y, z, u, w, v), expected, "Sin6D call did not match reference value.") def test_cos(self): testvals = [-10.0, -7, -0.001, 0.0, 0.00003, 10, 23.4] - for x in testvals: - for y in testvals: - for z in testvals: - for u in testvals: - for w in testvals: - for v in testvals: - function = cmath6d.Cos6D(self.f1) - expected = math.cos(self.f1(x, y, z, u, w, v)) - self.assertEqual(function(x, y, z, u, w, v), expected, "Cos6D call did not match reference value.") + for (x, y, z, u, w, v) in itertools.product(testvals, repeat=6): + function = cmath6d.Cos6D(self.f1) + expected = math.cos(self.f1(x, y, z, u, w, v)) + self.assertEqual(function(x, y, z, u, w, v), expected, "Cos6D call did not match reference value.") def test_tan(self): testvals = [-10.0, -7, -0.001, 0.0, 0.00003, 10, 23.4] - for x in testvals: - for y in testvals: - for z in testvals: - for u in testvals: - for w in testvals: - for v in testvals: - function = cmath6d.Tan6D(self.f1) - expected = math.tan(self.f1(x, y, z, u, w, v)) - self.assertEqual(function(x, y, z, u, w, v), expected, "Tan6D call did not match reference value.") + for (x, y, z, u, w, v) in itertools.product(testvals, repeat=6): + function = cmath6d.Tan6D(self.f1) + expected = math.tan(self.f1(x, y, z, u, w, v)) + self.assertEqual(function(x, y, z, u, w, v), expected, "Tan6D call did not match reference value.") def test_asin(self): v = [-10, -6, -2, -0.001, 0, 0.001, 2, 6, 10] @@ -102,54 +83,33 @@ def test_acos(self): with self.assertRaises(ValueError, msg="Acos3D did not raise a ValueError with value outside domain."): function(100, 0, 0, 0, 0, 0) - def test_atan(self): testvals = [-10.0, -7, -0.001, 0.0, 0.00003, 10, 23.4] - for x in testvals: - for y in testvals: - for z in testvals: - for u in testvals: - for w in testvals: - for v in testvals: - function = cmath6d.Atan6D(self.f1) - expected = math.atan(self.f1(x, y, z, u, w, v)) - self.assertEqual(function(x, y, z, u, w, v), expected, "Atan6D call did not match reference value.") + for (x, y, z, u, w, v) in itertools.product(testvals, repeat=6): + function = cmath6d.Atan6D(self.f1) + expected = math.atan(self.f1(x, y, z, u, w, v)) + self.assertEqual(function(x, y, z, u, w, v), expected, "Atan6D call did not match reference value.") def test_atan2(self): testvals = [-10.0, -7, -0.001, 0.0, 0.00003, 10, 23.4] - for x in testvals: - for y in testvals: - for z in testvals: - for u in testvals: - for w in testvals: - for v in testvals: - function = cmath6d.Atan4Q6D(self.f1, self.f2) - expected = math.atan2(self.f1(x, y, z, u, w, v), self.f2(x, y, z, u, w, v)) - self.assertEqual(function(x, y, z, u, w, v), expected, "Atan4Q6D call did not match reference value.") + for (x, y, z, u, w, v) in itertools.product(testvals, repeat=6): + function = cmath6d.Atan4Q6D(self.f1, self.f2) + expected = math.atan2(self.f1(x, y, z, u, w, v), self.f2(x, y, z, u, w, v)) + self.assertEqual(function(x, y, z, u, w, v), expected, "Atan4Q6D call did not match reference value.") def test_erf(self): testvals = [-1e5, -7, -0.001, 0.0, 0.00003, 10, 23.4, 1e5] function = cmath6d.Erf6D(self.f1) - for x in testvals: - for y in testvals: - for z in testvals: - for u in testvals: - for w in testvals: - for v in testvals: - expected = math.erf(self.f1(x, y, z, u, w, v)) - self.assertAlmostEqual(function(x, y, z, u, w, v), expected, 10, "Erf6D call did not match reference value.") + for (x, y, z, u, w, v) in itertools.product(testvals, repeat=6): + expected = math.erf(self.f1(x, y, z, u, w, v)) + self.assertAlmostEqual(function(x, y, z, u, w, v), expected, 10, "Erf6D call did not match reference value.") def test_sqrt(self): testvals = [0.0, 0.00003, 10, 23.4, 1e5] function = cmath6d.Sqrt6D(self.f1) - for x in testvals: - for y in testvals: - for z in testvals: - for u in testvals: - for w in testvals: - for v in testvals: - expected = math.sqrt(self.f1(x, y, z, u, w, v)) - self.assertEqual(function(x, y, z, u, w, v), expected, "Sqrt6D call did not match reference value.") + for (x, y, z, u, w, v) in itertools.product(testvals, repeat=6): + expected = math.sqrt(self.f1(x, y, z, u, w, v)) + self.assertEqual(function(x, y, z, u, w, v), expected, "Sqrt6D call did not match reference value.") with self.assertRaises(ValueError, msg="Sqrt6D did not raise a ValueError with value outside domain."): function(-0.1, -0.1, -0.1, -0.1, -0.1, -0.1) \ No newline at end of file diff --git a/cherab/core/math/function/float/function6d/tests/test_constant.py b/cherab/core/math/function/float/function6d/tests/test_constant.py index 1247e8f2..bc7e8cba 100644 --- a/cherab/core/math/function/float/function6d/tests/test_constant.py +++ b/cherab/core/math/function/float/function6d/tests/test_constant.py @@ -32,4 +32,5 @@ def test_constant(self): testvals = [-1e10, -7, -0.001, 0.0, 0.00003, 10, 2.3e49] for x in testvals: constant = Constant6D(x) + # Test with a single set of values since it's a constant function self.assertEqual(constant(500, 1.5, -3.14, 2.7, 1.8, 3.6), x, "Constant6D call did not match reference value.")