Repository: Hipparchus-Math/hipparchus
Pull request #247 (2023-04-05 13:59:04)

Fixed issue 244 : Add field version of Blendable interface method + field version of smoothstep function
Hi everyone,

Opening this merge request to close issue #244.

I added a ```FieldBlendable``` class (equivalent of ```Blendable```) and implemented it in ```FieldMatrix``` and ```FieldVector3D```. 

Also added the ability to create ```FieldSmoothStepFunction``` from the ```SmoothStepFactory``` class as well as the possibility to create quadratic smoothstep function (normal and field version). It should be possible to generalize the construction of even smoothstep functions but as it should not break the API in the future, I used the simplest and fastest option available.

Finally, I added associated tests for coverage.

Cheers,
Vincent


Patch:
diff --git a/hipparchus-core/src/main/java/org/hipparchus/analysis/polynomials/SmoothStepFactory.java b/hipparchus-core/src/main/java/org/hipparchus/analysis/polynomials/SmoothStepFactory.java
index 59ed6fcc0..be8384b7c 100644
--- a/hipparchus-core/src/main/java/org/hipparchus/analysis/polynomials/SmoothStepFactory.java
+++ b/hipparchus-core/src/main/java/org/hipparchus/analysis/polynomials/SmoothStepFactory.java
@@ -16,23 +16,26 @@
  */
 package org.hipparchus.analysis.polynomials;
 
+import org.hipparchus.CalculusFieldElement;
+import org.hipparchus.Field;
 import org.hipparchus.exception.LocalizedCoreFormats;
 import org.hipparchus.exception.MathIllegalArgumentException;
 import org.hipparchus.exception.NullArgumentException;
+import org.hipparchus.util.MathArrays;
 
 /**
  * Smoothstep function factory.
  * <p>
- * It allows for quick creation of common and generic smoothstep functions as defined <a
- * href="https://en.wikipedia.org/wiki/Smoothstep>here</a>.
+ * It allows for quick creation of common and generic smoothstep functions as defined
+ * <a href="https://en.wikipedia.org/wiki/Smoothstep">here</a>.
  */
 public class SmoothStepFactory {
 
     /**
      * Private constructor.
      * <p>
-     * This class is a utility class, it should neither have a public nor a default constructor. This private
-     * constructor prevents the compiler from generating one automatically.
+     * This class is a utility class, it should neither have a public nor a default constructor. This private constructor
+     * prevents the compiler from generating one automatically.
      */
     private SmoothStepFactory() {
         // Empty constructor
@@ -47,6 +50,16 @@ public class SmoothStepFactory {
         return getGeneralOrder(0);
     }
 
+    /**
+     * Get the {@link SmoothStepFunction quadratic smoothstep function}.
+     *
+     * @return clamping smoothstep function
+     */
+    public static SmoothStepFunction getQuadratic() {
+        // Use a default double array as it will not matter anyway
+        return new QuadraticSmoothStepFunction(new double[] { 0 });
+    }
+
     /**
      * Get the {@link SmoothStepFunction cubic smoothstep function}.
      *
@@ -65,13 +78,64 @@ public class SmoothStepFactory {
         return getGeneralOrder(2);
     }
 
+    /**
+     * Get the {@link SmoothStepFunction clamping smoothstep function}.
+     *
+     * @param <T> type of the field element
+     * @param field field of the element
+     *
+     * @return clamping smoothstep function
+     */
+    public static <T extends CalculusFieldElement<T>> FieldSmoothStepFunction<T> getClamp(final Field<T> field) {
+        return getFieldGeneralOrder(field, 0);
+    }
+
+    /**
+     * Get the {@link SmoothStepFunction quadratic smoothstep function}.
+     *
+     * @param <T> type of the field element
+     * @param field field of the element
+     *
+     * @return clamping smoothstep function
+     */
+    public static <T extends CalculusFieldElement<T>> FieldSmoothStepFunction<T> getQuadratic(final Field<T> field) {
+        final T[] tempArray = MathArrays.buildArray(field, 1);
+        return new FieldQuadraticSmoothStepFunction<>(tempArray);
+    }
+
+    /**
+     * Get the {@link SmoothStepFunction cubic smoothstep function}.
+     *
+     * @param <T> type of the field element
+     * @param field field of the element
+     *
+     * @return cubic smoothstep function
+     */
+    public static <T extends CalculusFieldElement<T>> FieldSmoothStepFunction<T> getCubic(final Field<T> field) {
+        return getFieldGeneralOrder(field, 1);
+    }
+
+    /**
+     * Get the {@link SmoothStepFunction quintic smoothstep function}.
+     *
+     * @param <T> type of the field element
+     * @param field field of the element
+     *
+     * @return quintic smoothstep function
+     */
+    public static <T extends CalculusFieldElement<T>> FieldSmoothStepFunction<T> getQuintic(final Field<T> field) {
+        return getFieldGeneralOrder(field, 2);
+    }
+
     /**
      * Create a {@link SmoothStepFunction smoothstep function} of order <b>2N + 1</b>.
      * <p>
      * It uses the general smoothstep equation presented <a href="https://en.wikipedia.org/wiki/Smoothstep">here</a> :
-     * $S_{N}(x) = \sum_{n=0}^{N} \begin{pmatrix} -N-1 \\ n \end{pmatrix} \begin{pmatrix} 2N+1 \\ N-n \end{pmatrix} x^{N+n+1}$
+     * $S_{N}(x) = \sum_{n=0}^{N} \begin{pmatrix} -N-1 \\ n \end{pmatrix} \begin{pmatrix} 2N+1 \\ N-n \end{pmatrix}
+     * x^{N+n+1}$
      *
      * @param N determines the order of the output smoothstep function (=2N + 1)
+     *
      * @return smoothstep function of order <b>2N + 1</b>
      */
     public static SmoothStepFunction getGeneralOrder(final int N) {
@@ -89,11 +153,42 @@ public class SmoothStepFactory {
         return new SmoothStepFunction(coefficients);
     }
 
+    /**
+     * Create a {@link SmoothStepFunction smoothstep function} of order <b>2N + 1</b>.
+     * <p>
+     * It uses the general smoothstep equation presented <a href="https://en.wikipedia.org/wiki/Smoothstep">here</a> :
+     * $S_{N}(x) = \sum_{n=0}^{N} \begin{pmatrix} -N-1 \\ n \end{pmatrix} \begin{pmatrix} 2N+1 \\ N-n \end{pmatrix}
+     * x^{N+n+1}$
+     *
+     * @param <T> type of the field element
+     * @param field field of the element
+     * @param N determines the order of the output smoothstep function (=2N + 1)
+     *
+     * @return smoothstep function of order <b>2N + 1</b>
+     */
+    public static <T extends CalculusFieldElement<T>> FieldSmoothStepFunction<T> getFieldGeneralOrder(final Field<T> field,
+                                                                                                      final int N) {
+
+        final int twoNPlusOne = 2 * N + 1;
+
+        final T[] coefficients = MathArrays.buildArray(field, twoNPlusOne + 1);
+
+        final T one = field.getOne();
+        int     n   = N;
+        for (int i = twoNPlusOne; i > N; i--) {
+            coefficients[i] = one.multiply(pascalTriangle(-N - 1, n) * pascalTriangle(2 * N + 1, N - n));
+            n--;
+        }
+
+        return new FieldSmoothStepFunction<>(coefficients);
+    }
+
     /**
      * Returns binomial coefficient without explicit use of factorials, which can't be used with negative integers
      *
      * @param k subset in set
      * @param n set
+     *
      * @return number of subset {@code k} in global set {@code n}
      */
     private static int pascalTriangle(final int k, final int n) {
@@ -110,6 +205,7 @@ public class SmoothStepFactory {
      * Check that input is between [0:1].
      *
      * @param input input to be checked
+     *
      * @throws MathIllegalArgumentException if input is not between [0:1]
      */
     public static void checkBetweenZeroAndOneIncluded(final double input) throws MathIllegalArgumentException {
@@ -120,10 +216,10 @@ public class SmoothStepFactory {
     }
 
     /**
-     * Smoothstep function as defined <a href="https://en.wikipedia.org/wiki/Smoothstep>here</a>.
+     * Smoothstep function as defined <a href="https://en.wikipedia.org/wiki/Smoothstep">here</a>.
      * <p>
-     * It is used to do a smooth transition between the "left edge" and the "right edge" with left edge assumed to be
-     * smaller than right edge.
+     * It is used to do a smooth transition between the "left edge" and the "right edge" with left edge assumed to be smaller
+     * than right edge.
      * <p>
      * By definition, for order n > 1 and input x, a smoothstep function respects at least the following properties :
      * <ul>
@@ -143,14 +239,15 @@ public class SmoothStepFactory {
         private static final long serialVersionUID = 20230113L;
 
         /**
-         * Construct a smoothstep with the given coefficients. The first element of the coefficients array is the
-         * constant term.  Higher degree coefficients follow in sequence.  The degree of the resulting polynomial is the
-         * index of the last non-null element of the array, or 0 if all elements are null.
+         * Construct a smoothstep with the given coefficients. The first element of the coefficients array is the constant
+         * term.  Higher degree coefficients follow in sequence.  The degree of the resulting polynomial is the index of the
+         * last non-null element of the array, or 0 if all elements are null.
          * <p>
          * The constructor makes a copy of the input array and assigns the copy to the coefficients property.</p>
          *
          * @param c Smoothstep polynomial coefficients.
-         * @throws NullArgumentException if {@code c} is {@code null}.
+         *
+         * @throws NullArgumentException        if {@code c} is {@code null}.
          * @throws MathIllegalArgumentException if {@code c} is empty.
          */
         private SmoothStepFunction(final double[] c) throws MathIllegalArgumentException, NullArgumentException {
@@ -160,9 +257,11 @@ public class SmoothStepFactory {
         /**
          * Compute the value of the smoothstep for the given argument normalized between edges.
          *
-         * @param xNormalized Normalized argument for which the function value should be computed. It is
-         *         expected to be between [0:1] and will throw an exception otherwise.
+         * @param xNormalized Normalized argument for which the function value should be computed. It is expected to be
+         * between [0:1] and will throw an exception otherwise.
+         *
          * @return the value of the polynomial at the given point.
+         *
          * @see org.hipparchus.analysis.UnivariateFunction#value(double)
          */
         @Override
@@ -179,7 +278,9 @@ public class SmoothStepFactory {
          * @param leftEdge left edge
          * @param rightEdge right edge
          * @param x Argument for which the function value should be computed
+         *
          * @return the value of the polynomial at the given point
+         *
          * @throws MathIllegalArgumentException if right edge is greater than left edge
          * @see org.hipparchus.analysis.UnivariateFunction#value(double)
          */
@@ -201,7 +302,7 @@ public class SmoothStepFactory {
          * @param leftEdge left edge
          * @param rightEdge right edge
          */
-        private void checkInputEdges(final double leftEdge, final double rightEdge) {
+        protected void checkInputEdges(final double leftEdge, final double rightEdge) {
             if (leftEdge > rightEdge) {
                 throw new MathIllegalArgumentException(LocalizedCoreFormats.RIGHT_EDGE_GREATER_THAN_LEFT_EDGE,
                                                        leftEdge, rightEdge);
@@ -214,9 +315,10 @@ public class SmoothStepFactory {
          * @param leftEdge left edge
          * @param rightEdge right edge
          * @param x input to clamp
+         *
          * @return clamped input
          */
-        private double clampInput(final double leftEdge, final double rightEdge, final double x) {
+        protected double clampInput(final double leftEdge, final double rightEdge, final double x) {
             if (x <= leftEdge) {
                 return leftEdge;
             }
@@ -232,11 +334,338 @@ public class SmoothStepFactory {
          * @param leftEdge left edge
          * @param rightEdge right edge
          * @param x input to normalize
+         *
          * @return normalized input
          */
-        private double normalizeInput(final double leftEdge, final double rightEdge, final double x) {
+        protected double normalizeInput(final double leftEdge, final double rightEdge, final double x) {
             return (x - leftEdge) / (rightEdge - leftEdge);
         }
     }
 
+    /**
+     * Specific smoothstep function that cannot be built using the {@link #getGeneralOrder(int)}.
+     * <p>
+     * Methods inherited from {@link PolynomialFunction} <em>should not be used</em> as they will not be true to the actual
+     * function.
+     *
+     * @see PolynomialFunction
+     */
+    public static class QuadraticSmoothStepFunction extends SmoothStepFunction {
+
+        /**
+         * Construct a smoothstep with the given coefficients. The first element of the coefficients array is the constant
+         * term. Higher degree coefficients follow in sequence.  The degree of the resulting polynomial is the index of the
+         * last non-null element of the array, or 0 if all elements are null.
+         * <p>
+         * The constructor makes a copy of the input array and assigns the copy to the coefficients property.</p>
+         *
+         * @param c Smoothstep polynomial coefficients.
+         *
+         * @throws NullArgumentException        if {@code c} is {@code null}.
+         * @throws MathIllegalArgumentException if {@code c} is empty.
+         */
+        private QuadraticSmoothStepFunction(final double[] c) throws MathIllegalArgumentException, NullArgumentException {
+            super(c);
+        }
+
+        /**
+         * Compute the value of the smoothstep function for the given edges and argument.
+         * <p>
+         * Note that right edge is expected to be greater than left edge. It will throw an exception otherwise.
+         *
+         * @param leftEdge left edge
+         * @param rightEdge right edge
+         * @param x Argument for which the function value should be computed
+         *
+         * @return the value of the polynomial at the given point
+         *
+         * @throws MathIllegalArgumentException if right edge is greater than left edge
+         * @see org.hipparchus.analysis.UnivariateFunction#value(double)
+         */
+        @Override
+        public double value(final double leftEdge, final double rightEdge, final double x)
+                throws MathIllegalArgumentException {
+
+            checkInputEdges(leftEdge, rightEdge);
+
+            final double xClamped = clampInput(leftEdge, rightEdge, x);
+
+            final double xNormalized = normalizeInput(leftEdge, rightEdge, xClamped);
+
+            return value(xNormalized);
+        }
+
+        /**
+         * Compute the value of the quadratic smoothstep for the given argument normalized between edges.
+         * <p>
+         *
+         * @param xNormalized Normalized argument for which the function value should be computed. It is expected to be
+         * between [0:1] and will throw an exception otherwise.
+         *
+         * @return the value of the polynomial at the given point.
+         *
+         * @see org.hipparchus.analysis.UnivariateFunction#value(double)
+         */
+        @Override
+        public double value(final double xNormalized) {
+            checkBetweenZeroAndOneIncluded(xNormalized);
+
+            if (xNormalized >= 0 && xNormalized <= 0.5) {
+                return 2 * xNormalized * xNormalized;
+            }
+            else {
+                return 4 * xNormalized - 2 * xNormalized * xNormalized - 1;
+            }
+        }
+    }
+
+    /**
+     * Smoothstep function as defined <a href="https://en.wikipedia.org/wiki/Smoothstep">here</a>.
+     * <p>
+     * It is used to do a smooth transition between the "left edge" and the "right edge" with left edge assumed to be smaller
+     * than right edge.
+     * <p>
+     * By definition, for order n > 1 and input x, a smoothstep function respects at least the following properties :
+     * <ul>
+     *     <li>f(x <= leftEdge) = 0 and f(x >= rightEdge) = 1</li>
+     *     <li>f'(leftEdge) = f'(rightEdge) = 0</li>
+     * </ul>
+     * If x is normalized between edges, we have at least :
+     * <ul>
+     *     <li>f(x <= 0) = 0 and f(x >= 1) = 1</li>
+     *     <li>f'(0) = f'(1) = 0</li>
+     * </ul>
+     * Smoothstep functions of higher order n will have their higher time derivatives also equal to zero at edges...
+     *
+     * @param <T> type of the field element
+     */
+    public static class FieldSmoothStepFunction<T extends CalculusFieldElement<T>> extends FieldPolynomialFunction<T> {
+
+        /**
+         * Construct a smoothstep with the given coefficients. The first element of the coefficients array is the constant
+         * term.  Higher degree coefficients follow in sequence.  The degree of the resulting polynomial is the index of the
+         * last non-null element of the array, or 0 if all elements are null.
+         * <p>
+         * The constructor makes a copy of the input array and assigns the copy to the coefficients property.</p>
+         *
+         * @param c Smoothstep polynomial coefficients.
+         *
+         * @throws NullArgumentException        if {@code c} is {@code null}.
+         * @throws MathIllegalArgumentException if {@code c} is empty.
+         */
+        private FieldSmoothStepFunction(final T[] c) throws MathIllegalArgumentException, NullArgumentException {
+            super(c);
+        }
+
+        /**
+         * Compute the value of the smoothstep for the given argument normalized between edges.
+         *
+         * @param xNormalized Normalized argument for which the function value should be computed. It is expected to be
+         * between [0:1] and will throw an exception otherwise.
+         *
+         * @return the value of the polynomial at the given point.
+         *
+         * @see org.hipparchus.analysis.UnivariateFunction#value(double)
+         */
+        @Override
+        public T value(final double xNormalized) {
+            checkBetweenZeroAndOneIncluded(xNormalized);
+            return super.value(xNormalized);
+        }
+
+        /**
+         * Compute the value of the smoothstep for the given argument normalized between edges.
+         *
+         * @param xNormalized Normalized argument for which the function value should be computed. It is expected to be
+         * between [0:1] and will throw an exception otherwise.
+         *
+         * @return the value of the polynomial at the given point.
+         *
+         * @see org.hipparchus.analysis.UnivariateFunction#value(double)
+         */
+        @Override
+        public T value(final T xNormalized) {
+            checkBetweenZeroAndOneIncluded(xNormalized.getReal());
+            return super.value(xNormalized);
+        }
+
+        /**
+         * Compute the value of the smoothstep function for the given edges and argument.
+         * <p>
+         * Note that right edge is expected to be greater than left edge. It will throw an exception otherwise.
+         *
+         * @param leftEdge left edge
+         * @param rightEdge right edge
+         * @param x Argument for which the function value should be computed
+         *
+         * @return the value of the polynomial at the given point
+         *
+         * @throws MathIllegalArgumentException if right edge is greater than left edge
+         * @see org.hipparchus.analysis.UnivariateFunction#value(double)
+         */
+        public T value(final double leftEdge, final double rightEdge, final T x)
+                throws MathIllegalArgumentException {
+
+            checkInputEdges(leftEdge, rightEdge);
+
+            final T xClamped = clampInput(leftEdge, rightEdge, x);
+
+            final T xNormalized = normalizeInput(leftEdge, rightEdge, xClamped);
+
+            return super.value(xNormalized);
+        }
+
+        /**
+         * Check that left edge is lower than right edge. Otherwise, throw an exception.
+         *
+         * @param leftEdge left edge
+         * @param rightEdge right edge
+         */
+        protected void checkInputEdges(final double leftEdge, final double rightEdge) {
+            if (leftEdge > rightEdge) {
+                throw new MathIllegalArgumentException(LocalizedCoreFormats.RIGHT_EDGE_GREATER_THAN_LEFT_EDGE,
+                                                       leftEdge, rightEdge);
+            }
+        }
+
+        /**
+         * Clamp input between edges.
+         *
+         * @param leftEdge left edge
+         * @param rightEdge right edge
+         * @param x input to clamp
+         *
+         * @return clamped input
+         */
+        protected T clampInput(final double leftEdge, final double rightEdge, final T x) {
+            if (x.getReal() <= leftEdge) {
+                return x.getField().getOne().multiply(leftEdge);
+            }
+            if (x.getReal() >= rightEdge) {
+                return x.getField().getOne().multiply(rightEdge);
+            }
+            return x;
+        }
+
+        /**
+         * Normalize input between left and right edges.
+         *
+         * @param leftEdge left edge
+         * @param rightEdge right edge
+         * @param x input to normalize
+         *
+         * @return normalized input
+         */
+        protected T normalizeInput(final double leftEdge, final double rightEdge, final T x) {
+            return x.subtract(leftEdge).divide(rightEdge - leftEdge);
+        }
+    }
+
+    /**
+     * Specific smoothstep function that cannot be built using the {@link #getGeneralOrder(int)}.
+     * <p>
+     * Methods inherited from {@link PolynomialFunction} <em>should not be used</em> as they will not be true to the actual
+     * function.
+     *
+     * @param <T> type of the field element
+     *
+     * @see PolynomialFunction
+     */
+    private static class FieldQuadraticSmoothStepFunction<T extends CalculusFieldElement<T>>
+            extends FieldSmoothStepFunction<T> {
+
+        /**
+         * Construct a smoothstep with the given coefficients. The first element of the coefficients array is the constant
+         * term. Higher degree coefficients follow in sequence.  The degree of the resulting polynomial is the index of the
+         * last non-null element of the array, or 0 if all elements are null.
+         * <p>
+         * The constructor makes a copy of the input array and assigns the copy to the coefficients property.</p>
+         *
+         * @param c Smoothstep polynomial coefficients.
+         *
+         * @throws NullArgumentException        if {@code c} is {@code null}.
+         * @throws MathIllegalArgumentException if {@code c} is empty.
+         */
+        private FieldQuadraticSmoothStepFunction(final T[] c) throws MathIllegalArgumentException, NullArgumentException {
+            super(c);
+        }
+
+        /**
+         * Compute the value of the smoothstep function for the given edges and argument.
+         * <p>
+         * Note that right edge is expected to be greater than left edge. It will throw an exception otherwise.
+         *
+         * @param leftEdge left edge
+         * @param rightEdge right edge
+         * @param x Argument for which the function value should be computed
+         *
+         * @return the value of the polynomial at the given point
+         *
+         * @throws MathIllegalArgumentException if right edge is greater than left edge
+         * @see org.hipparchus.analysis.UnivariateFunction#value(double)
+         */
+        @Override
+        public T value(final double leftEdge, final double rightEdge, final T x)
+                throws MathIllegalArgumentException {
+
+            checkInputEdges(leftEdge, rightEdge);
+
+            final T xClamped = clampInput(leftEdge, rightEdge, x);
+
+            final T xNormalized = normalizeInput(leftEdge, rightEdge, xClamped);
+
+            return value(xNormalized);
+        }
+
+        /**
+         * Compute the value of the quadratic smoothstep for the given argument normalized between edges.
+         * <p>
+         *
+         * @param xNormalized Normalized argument for which the function value should be computed. It is expected to be
+         * between [0:1] and will throw an exception otherwise.
+         *
+         * @return the value of the polynomial at the given point.
+         *
+         * @see org.hipparchus.analysis.UnivariateFunction#value(double)
+         */
+        @Override
+        public T value(final double xNormalized) {
+            checkBetweenZeroAndOneIncluded(xNormalized);
+
+            final Field<T> field = getField();
+            final T        one   = field.getOne();
+
+            if (xNormalized >= 0 && xNormalized <= 0.5) {
+                return one.multiply(2. * xNormalized * xNormalized);
+            }
+            else {
+                return one.multiply(4. * xNormalized - 2. * xNormalized * xNormalized - 1.);
+            }
+        }
+
+        /**
+         * Compute the value of the quadratic smoothstep for the given argument normalized between edges.
+         * <p>
+         *
+         * @param xNormalized Normalized argument for which the function value should be computed. It is expected to be
+         * between [0:1] and will throw an exception otherwise.
+         *
+         * @return the value of the polynomial at the given point.
+         *
+         * @see org.hipparchus.analysis.UnivariateFunction#value(double)
+         */
+        @Override
+        public T value(final T xNormalized) {
+            checkBetweenZeroAndOneIncluded(xNormalized.getReal());
+
+            if (xNormalized.getReal() >= 0 && xNormalized.getReal() <= 0.5) {
+                return xNormalized.multiply(xNormalized).multiply(2.);
+            }
+            else {
+                final T one = getField().getOne();
+                return one.linearCombination(4., xNormalized, -2., xNormalized.multiply(xNormalized)).subtract(1.);
+            }
+        }
+    }
+
 }
diff --git a/hipparchus-core/src/main/java/org/hipparchus/linear/FieldMatrix.java b/hipparchus-core/src/main/java/org/hipparchus/linear/FieldMatrix.java
index 3141547a1..23c187c8b 100644
--- a/hipparchus-core/src/main/java/org/hipparchus/linear/FieldMatrix.java
+++ b/hipparchus-core/src/main/java/org/hipparchus/linear/FieldMatrix.java
@@ -27,8 +27,10 @@ import java.util.function.Function;
 
 import org.hipparchus.Field;
 import org.hipparchus.FieldElement;
+import org.hipparchus.analysis.polynomials.SmoothStepFactory;
 import org.hipparchus.exception.MathIllegalArgumentException;
 import org.hipparchus.exception.NullArgumentException;
+import org.hipparchus.util.FieldBlendable;
 
 /**
  * Interface defining field-valued matrix with basic algebraic operations.
@@ -38,7 +40,7 @@ import org.hipparchus.exception.NullArgumentException;
  *
  * @param <T> the type of the field elements
  */
-public interface FieldMatrix<T extends FieldElement<T>> extends AnyMatrix {
+public interface FieldMatrix<T extends FieldElement<T>> extends AnyMatrix, FieldBlendable<FieldMatrix<T>, T> {
     /**
      * Get the type of field elements of the matrix.
      *
@@ -169,6 +171,14 @@ public interface FieldMatrix<T extends FieldElement<T>> extends AnyMatrix {
      */
     FieldMatrix<T> power(int p) throws MathIllegalArgumentException;
 
+    /** {@inheritDoc} */
+    @Override
+    default FieldMatrix<T> blendArithmeticallyWith(final FieldMatrix<T> other, final T blendingValue) {
+        SmoothStepFactory.checkBetweenZeroAndOneIncluded(blendingValue.getReal());
+        return this.scalarMultiply(getField().getOne().subtract(blendingValue))
+                   .add(other.scalarMultiply(blendingValue));
+    }
+
     /**
      * Returns matrix entries as a two-dimensional array.
      *
diff --git a/hipparchus-core/src/main/java/org/hipparchus/util/FieldBlendable.java b/hipparchus-core/src/main/java/org/hipparchus/util/FieldBlendable.java
new file mode 100644
index 000000000..6dd8a06b0
--- /dev/null
+++ b/hipparchus-core/src/main/java/org/hipparchus/util/FieldBlendable.java
@@ -0,0 +1,44 @@
+/* Copyright 2002-2023 CS GROUP
+ * Licensed to the Hipparchus project under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The Hipparchus project licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.hipparchus.util;
+
+import org.hipparchus.FieldElement;
+import org.hipparchus.exception.MathIllegalArgumentException;
+
+/**
+ * Interface representing classes that can blend with other instances of themselves using a given blending value.
+ * <p> The blending value is commonly given from a
+ * {@link org.hipparchus.analysis.polynomials.SmoothStepFactory.FieldSmoothStepFunction smoothstep function}.
+ *
+ * @param <B> blendable class
+ * @param <T> type of the field element
+ */
+public interface FieldBlendable<B, T extends FieldElement<T>> {
+
+    /**
+     * Blend arithmetically this instance with another one.
+     *
+     * @param other other instance to blend arithmetically with
+     * @param blendingValue value from smoothstep function B(x). It is expected to be between [0:1] and will throw an
+     * exception otherwise.
+     *
+     * @return this * (1 - B(x)) + other * B(x)
+     *
+     * @throws MathIllegalArgumentException if blending value is not within [0:1]
+     */
+    B blendArithmeticallyWith(B other, T blendingValue) throws MathIllegalArgumentException;
+}
diff --git a/hipparchus-geometry/src/main/java/org/hipparchus/geometry/Vector.java b/hipparchus-geometry/src/main/java/org/hipparchus/geometry/Vector.java
index 55be60074..d8a9eab2a 100644
--- a/hipparchus-geometry/src/main/java/org/hipparchus/geometry/Vector.java
+++ b/hipparchus-geometry/src/main/java/org/hipparchus/geometry/Vector.java
@@ -157,6 +157,7 @@ public interface Vector<S extends Space, V extends Vector<S,V>> extends Point<S>
      */
     String toString(NumberFormat format);
 
+    /** {@inheritDoc} */
     @Override
     default V blendArithmeticallyWith(Vector<S,V> other, double blendingValue)
             throws MathIllegalArgumentException {
diff --git a/hipparchus-geometry/src/main/java/org/hipparchus/geometry/euclidean/threed/FieldVector3D.java b/hipparchus-geometry/src/main/java/org/hipparchus/geometry/euclidean/threed/FieldVector3D.java
index fd4e9cc91..713218086 100644
--- a/hipparchus-geometry/src/main/java/org/hipparchus/geometry/euclidean/threed/FieldVector3D.java
+++ b/hipparchus-geometry/src/main/java/org/hipparchus/geometry/euclidean/threed/FieldVector3D.java
@@ -27,11 +27,13 @@ import java.text.NumberFormat;
 
 import org.hipparchus.CalculusFieldElement;
 import org.hipparchus.Field;
+import org.hipparchus.analysis.polynomials.SmoothStepFactory;
 import org.hipparchus.exception.LocalizedCoreFormats;
 import org.hipparchus.exception.MathIllegalArgumentException;
 import org.hipparchus.exception.MathRuntimeException;
 import org.hipparchus.geometry.LocalizedGeometryFormats;
 import org.hipparchus.util.FastMath;
+import org.hipparchus.util.FieldBlendable;
 import org.hipparchus.util.FieldSinCos;
 import org.hipparchus.util.MathArrays;
 
@@ -40,7 +42,7 @@ import org.hipparchus.util.MathArrays;
  * <p>Instance of this class are guaranteed to be immutable.</p>
  * @param <T> the type of the field elements
  */
-public class FieldVector3D<T extends CalculusFieldElement<T>> implements Serializable {
+public class FieldVector3D<T extends CalculusFieldElement<T>> implements FieldBlendable<FieldVector3D<T>, T>, Serializable {
 
     /** Serializable version identifier. */
     private static final long serialVersionUID = 20130224L;
@@ -1276,4 +1278,12 @@ public class FieldVector3D<T extends CalculusFieldElement<T>> implements Seriali
         return new Vector3DFormat(format).format(toVector3D());
     }
 
+    /** {@inheritDoc} */
+    @Override
+    public FieldVector3D<T> blendArithmeticallyWith(final FieldVector3D<T> other, final T blendingValue)
+            throws MathIllegalArgumentException {
+        SmoothStepFactory.checkBetweenZeroAndOneIncluded(blendingValue.getReal());
+        final T one = x.getField().getOne();
+        return this.scalarMultiply(one.subtract(blendingValue)).add(other.scalarMultiply(blendingValue));
+    }
 }
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 15f42198e..a9d7e984c 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -50,6 +50,10 @@ If the output is not quite correct, check for invisible trailing spaces!
   </properties>
   <body>
     <release version="3.0" date="TBD" description="This is a major release.">
+      <action dev="vincent" type="add" issue="issues/244">
+        Added field version of Blendable interface method (implemented in FieldMatrix and FieldVector3D)
+        Added field version of smoothstep function in SmoothStepFactory
+      </action>
       <action dev="luc" type="update" issue="issues/243">
         Improve management of Cardan/Euler rotations close to singularity.
       </action>


Test patch:
diff --git a/hipparchus-core/src/test/java/org/hipparchus/analysis/polynomials/SmoothStepFactoryTest.java b/hipparchus-core/src/test/java/org/hipparchus/analysis/polynomials/SmoothStepFactoryTest.java
index 53c080575..1c9ec9e9f 100644
--- a/hipparchus-core/src/test/java/org/hipparchus/analysis/polynomials/SmoothStepFactoryTest.java
+++ b/hipparchus-core/src/test/java/org/hipparchus/analysis/polynomials/SmoothStepFactoryTest.java
@@ -1,6 +1,9 @@
 package org.hipparchus.analysis.polynomials;
 
+import org.hipparchus.Field;
 import org.hipparchus.exception.MathIllegalArgumentException;
+import org.hipparchus.util.Binary64;
+import org.hipparchus.util.Binary64Field;
 import org.junit.Assert;
 import org.junit.Test;
 
@@ -9,21 +12,19 @@ public class SmoothStepFactoryTest {
     final double THRESHOLD = 1e-15;
 
     @Test(expected = MathIllegalArgumentException.class)
-    public void testExceptionLeftAndRightEdge() {
+    public void testExceptionBelowBoundary() {
         // Given
-        final double leftEdge  = 5;
-        final double rightEdge = leftEdge - 1;
-
+        final double                               x          = 2;
         final SmoothStepFactory.SmoothStepFunction smoothstep = SmoothStepFactory.getGeneralOrder(1);
 
         // When
-        smoothstep.value(leftEdge, rightEdge, 0);
+        smoothstep.value(x);
     }
 
     @Test(expected = MathIllegalArgumentException.class)
-    public void testExceptionBelowBoundary() {
+    public void testExceptionOverBoundary() {
         // Given
-        final double                               x          = 2;
+        final double                               x          = 17;
         final SmoothStepFactory.SmoothStepFunction smoothstep = SmoothStepFactory.getGeneralOrder(1);
 
         // When
@@ -31,28 +32,30 @@ public class SmoothStepFactoryTest {
     }
 
     @Test(expected = MathIllegalArgumentException.class)
-    public void testExceptionOverBoundary() {
+    public void testEdgesConsistency() {
         // Given
-        final double                               x          = 17;
+        final double                               leftEdge   = 5;
+        final double                               rightEdge  = 2;
+        final double                               x          = 3;
         final SmoothStepFactory.SmoothStepFunction smoothstep = SmoothStepFactory.getGeneralOrder(1);
 
         // When
-        smoothstep.value(x);
+        smoothstep.value(leftEdge, rightEdge, x);
     }
 
     @Test
     public void testBoundaries() {
         // Given
-        final double leftEdge     = 5;
-        final double rightEdge    = 10;
-        final double x1           = 2;
-        final double x2           = 11;
+        final double leftEdge  = 5;
+        final double rightEdge = 10;
+        final double x1        = 2;
+        final double x2        = 11;
 
         final SmoothStepFactory.SmoothStepFunction clamp = SmoothStepFactory.getClamp();
 
         // When
-        final double computedResult1           = clamp.value(leftEdge, rightEdge, x1);
-        final double computedResult2           = clamp.value(leftEdge, rightEdge, x2);
+        final double computedResult1 = clamp.value(leftEdge, rightEdge, x1);
+        final double computedResult2 = clamp.value(leftEdge, rightEdge, x2);
 
         // Then
         Assert.assertEquals(0, computedResult1, THRESHOLD);
@@ -64,10 +67,10 @@ public class SmoothStepFactoryTest {
 
         // Given
         final double                               x     = 0.4;
-        final SmoothStepFactory.SmoothStepFunction clamp = SmoothStepFactory.getCubic();
+        final SmoothStepFactory.SmoothStepFunction cubic = SmoothStepFactory.getCubic();
 
         // When
-        final double computedResult = clamp.value(x);
+        final double computedResult = cubic.value(x);
 
         // Then
         Assert.assertEquals(0.352, computedResult, THRESHOLD);
@@ -92,6 +95,42 @@ public class SmoothStepFactoryTest {
 
     }
 
+    @Test
+    public void testQuadraticFunction1() {
+
+        // Given
+        final double leftEdge  = 5;
+        final double rightEdge = 10;
+        final double x         = 7;
+
+        final SmoothStepFactory.SmoothStepFunction quadratic = SmoothStepFactory.getQuadratic();
+
+        // When
+        final double computedResult = quadratic.value(leftEdge, rightEdge, x);
+
+        // Then
+        Assert.assertEquals(0.32, computedResult, THRESHOLD);
+
+    }
+
+    @Test
+    public void testQuadraticFunction2() {
+
+        // Given
+        final double leftEdge  = 5;
+        final double rightEdge = 10;
+        final double x         = 8;
+
+        final SmoothStepFactory.SmoothStepFunction quadratic = SmoothStepFactory.getQuadratic();
+
+        // When
+        final double computedResult = quadratic.value(leftEdge, rightEdge, x);
+
+        // Then
+        Assert.assertEquals(0.68, computedResult, THRESHOLD);
+
+    }
+
     @Test
     public void testCubicFunction() {
 
@@ -128,4 +167,162 @@ public class SmoothStepFactoryTest {
 
     }
 
+    @Test(expected = MathIllegalArgumentException.class)
+    public void testFieldEdgesConsistency() {
+        // Given
+        final Field<Binary64> field = Binary64Field.getInstance();
+
+        final double   leftEdge  = 5;
+        final double   rightEdge = 2;
+        final Binary64 x         = new Binary64(3);
+        final SmoothStepFactory.FieldSmoothStepFunction<Binary64> smoothstep =
+                SmoothStepFactory.getFieldGeneralOrder(field, 1);
+
+        // When
+        smoothstep.value(leftEdge, rightEdge, x);
+    }
+
+    @Test
+    public void testFieldBoundaries() {
+        // Given
+        final Field<Binary64> field = Binary64Field.getInstance();
+
+        final double   leftEdge  = 5;
+        final double   rightEdge = 10;
+        final Binary64 x1        = new Binary64(2);
+        final Binary64 x2        = new Binary64(11);
+
+        final SmoothStepFactory.FieldSmoothStepFunction<Binary64> clamp = SmoothStepFactory.getClamp(field);
+
+        // When
+        final Binary64 computedResult1 = clamp.value(leftEdge, rightEdge, x1);
+        final Binary64 computedResult2 = clamp.value(leftEdge, rightEdge, x2);
+
+        // Then
+        Assert.assertEquals(0, computedResult1.getReal(), THRESHOLD);
+        Assert.assertEquals(1, computedResult2.getReal(), THRESHOLD);
+    }
+
+    @Test
+    public void testFieldNormalizedInput() {
+
+        // Given
+        final Field<Binary64> field = Binary64Field.getInstance();
+
+        final double                                              x     = 0.4;
+        final SmoothStepFactory.FieldSmoothStepFunction<Binary64> cubic = SmoothStepFactory.getCubic(field);
+
+        // When
+        final Binary64 computedResult = cubic.value(x);
+
+        // Then
+        Assert.assertEquals(0.352, computedResult.getReal(), THRESHOLD);
+
+    }
+
+    @Test
+    public void testFieldClampFunction() {
+
+        // Given
+        final Field<Binary64> field     = Binary64Field.getInstance();
+        final double          leftEdge  = 5;
+        final double          rightEdge = 10;
+        final Binary64        x         = new Binary64(7);
+
+        final SmoothStepFactory.FieldSmoothStepFunction<Binary64> clamp = SmoothStepFactory.getClamp(field);
+
+        // When
+        final Binary64 computedResult = clamp.value(leftEdge, rightEdge, x);
+
+        // Then
+        Assert.assertEquals(0.4, computedResult.getReal(), THRESHOLD);
+
+    }
+
+    @Test
+    public void testFieldQuadraticFunction1() {
+
+        // Given
+        final Field<Binary64> field     = Binary64Field.getInstance();
+        final double          leftEdge  = 5;
+        final double          rightEdge = 10;
+        final double          x         = 7;
+        final Binary64        xField    = new Binary64(x);
+
+        final SmoothStepFactory.FieldSmoothStepFunction<Binary64> quadratic = SmoothStepFactory.getQuadratic(field);
+
+        // When
+        final Binary64 computedResult  = quadratic.value(leftEdge, rightEdge, xField);
+        final Binary64 computedResult2 = quadratic.value((x - leftEdge) / (rightEdge - leftEdge));
+
+        // Then
+        Assert.assertEquals(0.32, computedResult.getReal(), THRESHOLD);
+        Assert.assertEquals(computedResult.getReal(), computedResult2.getReal(), THRESHOLD);
+
+    }
+
+    @Test
+    public void testFieldQuadraticFunction2() {
+
+        // Given
+        final Field<Binary64> field     = Binary64Field.getInstance();
+        final double          leftEdge  = 5;
+        final double          rightEdge = 10;
+        final double          x         = 8;
+        final Binary64        xField    = new Binary64(x);
+
+        final SmoothStepFactory.FieldSmoothStepFunction<Binary64> quadratic = SmoothStepFactory.getQuadratic(field);
+
+        final Binary64 computedResult  = quadratic.value(leftEdge, rightEdge, xField);
+        final Binary64 computedResult2 = quadratic.value((x - leftEdge) / (rightEdge - leftEdge));
+
+        // Then
+        Assert.assertEquals(0.68, computedResult.getReal(), THRESHOLD);
+        Assert.assertEquals(computedResult.getReal(), computedResult2.getReal(), THRESHOLD);
+
+    }
+
+    @Test
+    public void testFieldCubicFunction() {
+
+        // Given
+        final Field<Binary64> field     = Binary64Field.getInstance();
+        final double          leftEdge  = 5;
+        final double          rightEdge = 10;
+        final Binary64        x         = new Binary64(7);
+
+        final SmoothStepFactory.FieldSmoothStepFunction<Binary64> cubic = SmoothStepFactory.getCubic(field);
+
+        // When
+        final Binary64 computedResult = cubic.value(leftEdge, rightEdge, x);
+
+        // Then
+        Assert.assertEquals(0.352, computedResult.getReal(), THRESHOLD);
+
+    }
+
+    @Test
+    public void testFieldQuinticFunction() {
+
+        // Given
+        final Field<Binary64> field       = Binary64Field.getInstance();
+        final double          leftEdge    = 5;
+        final double          rightEdge   = 10;
+        final double          x           = 7;
+        final double          xNormalized = (x - leftEdge) / (rightEdge - leftEdge);
+        final Binary64        xField      = new Binary64(x);
+
+        final SmoothStepFactory.FieldSmoothStepFunction<Binary64> quintic = SmoothStepFactory.getQuintic(field);
+
+        // When
+        final Binary64 computedResult  = quintic.value(leftEdge, rightEdge, xField);
+        final Binary64 computedResult2 = quintic.value(xNormalized);
+        final Binary64 computedResult3 = quintic.value(new Binary64(xNormalized));
+
+        // Then
+        Assert.assertEquals(0.31744, computedResult.getReal(), THRESHOLD);
+        Assert.assertEquals(computedResult.getReal(), computedResult2.getReal(), THRESHOLD);
+        Assert.assertEquals(computedResult2.getReal(), computedResult3.getReal(), THRESHOLD);
+    }
+
 }
diff --git a/hipparchus-core/src/test/java/org/hipparchus/linear/FieldMatrixTest.java b/hipparchus-core/src/test/java/org/hipparchus/linear/FieldMatrixTest.java
index c7a289136..8a45ce847 100644
--- a/hipparchus-core/src/test/java/org/hipparchus/linear/FieldMatrixTest.java
+++ b/hipparchus-core/src/test/java/org/hipparchus/linear/FieldMatrixTest.java
@@ -16,6 +16,7 @@
  */
 package org.hipparchus.linear;
 
+import org.hipparchus.Field;
 import org.hipparchus.util.Binary64;
 import org.hipparchus.util.Binary64Field;
 import org.junit.Assert;
@@ -78,6 +79,35 @@ public class FieldMatrixTest {
         });
     }
 
+    @Test
+    public void testArithmeticalBlending() {
+        // Given
+        final Field<Binary64> field = Binary64Field.getInstance();
+
+        final FieldMatrix<Binary64> matrix1 = MatrixUtils.createFieldMatrix(field, 2, 2);
+        matrix1.setEntry(0, 0, new Binary64(1));
+        matrix1.setEntry(0, 1, new Binary64(2));
+        matrix1.setEntry(1, 0, new Binary64(3));
+        matrix1.setEntry(1, 1, new Binary64(4));
+
+        final FieldMatrix<Binary64> matrix2 = MatrixUtils.createFieldMatrix(field, 2, 2);
+        matrix2.setEntry(0, 0, new Binary64(2));
+        matrix2.setEntry(0, 1, new Binary64(4));
+        matrix2.setEntry(1, 0, new Binary64(9));
+        matrix2.setEntry(1, 1, new Binary64(16));
+
+        final Binary64 blendingValue = new Binary64(0.65);
+
+        // When
+        final FieldMatrix<Binary64> blendedMatrix = matrix1.blendArithmeticallyWith(matrix2, blendingValue);
+
+        // Then
+        Assert.assertEquals(1.65 , blendedMatrix.getEntry(0,0).getReal(), 1.0e-15);
+        Assert.assertEquals(3.3  , blendedMatrix.getEntry(0,1).getReal(), 1.0e-15);
+        Assert.assertEquals(6.9  , blendedMatrix.getEntry(1,0).getReal(), 1.0e-15);
+        Assert.assertEquals(11.8 , blendedMatrix.getEntry(1,1).getReal(), 1.0e-15);
+    }
+
     // local class that does NOT override multiplyTransposed nor transposeMultiply nor map nor mapToSelf
     // so the default methods are called
     private class DefaultMatrix extends AbstractFieldMatrix<Binary64> {
diff --git a/hipparchus-geometry/src/test/java/org/hipparchus/geometry/euclidean/threed/FieldVector3DTest.java b/hipparchus-geometry/src/test/java/org/hipparchus/geometry/euclidean/threed/FieldVector3DTest.java
index 566e30c37..df87d81bc 100644
--- a/hipparchus-geometry/src/test/java/org/hipparchus/geometry/euclidean/threed/FieldVector3DTest.java
+++ b/hipparchus-geometry/src/test/java/org/hipparchus/geometry/euclidean/threed/FieldVector3DTest.java
@@ -725,6 +725,23 @@ public class FieldVector3DTest {
         }
     }
 
+    @Test
+    public void testArithmeticalBlending(){
+        // Given
+        final FieldVector3D<Binary64> vector1 = new FieldVector3D<>(new Binary64(1), new Binary64(2), new Binary64(3));
+        final FieldVector3D<Binary64> vector2 = new FieldVector3D<>(new Binary64(2), new Binary64(4), new Binary64(9));
+
+        final Binary64 blendingValue = new Binary64(0.65);
+
+        // When
+        final FieldVector3D<Binary64> blendedVector = vector1.blendArithmeticallyWith(vector2, blendingValue);
+
+        // Then
+        Assert.assertEquals(1.65, blendedVector.getX().getReal(), 1.0e-15);
+        Assert.assertEquals(3.3 , blendedVector.getY().getReal(), 1.0e-15);
+        Assert.assertEquals(6.9 , blendedVector.getZ().getReal(), 1.0e-15);
+    }
+
     private FieldVector3D<DerivativeStructure> createVector(double x, double y, double z, int params) {
         DSFactory factory = new DSFactory(params, 1);
         return new FieldVector3D<>(factory.variable(0, x),