
io.jenetics.lattices.NumericalContext Maven / Gradle / Ivy
/*
* Java Lattice Library (lattices-3.0.0.ALPHA1).
* Copyright (c) 2022-2022 Franz Wilhelmstötter
*
* Licensed 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.
*
* Author:
* Franz Wilhelmstötter ([email protected])
*/
package io.jenetics.lattices;
import static java.lang.Math.abs;
import static java.util.Objects.requireNonNull;
import java.util.function.Supplier;
import java.util.random.RandomGeneratorFactory;
/**
* Encapsulates the context settings which describes certain rules for numerical
* operations. The default context is created with an epsilon of 10-9.
* The default value can be changed with defining a Java property on
* the command line.
*
* $ java -Dio.jenetics.lattices.precision=12 ...
*
* The example above will change the epsilon of the default context object to
* 10-12. It is also possible to change the numerical context only
* for specific parts of your code. This might be useful in a testing
* environment. The following example shows how to do this.
* {@code
* final DoubleMatrix2d A = ...;
* final DoubleMatrix2d B = ...;
*
* // Executes the 'solve' operation with a different epsilon.
* // Other parts of the program are not effected.
* NumericalContext.using(new NumericalContext(0.001), () -> {
* final DoubleMatrix2d X = Algebra.solve(A, B);
* });
* }
*
* @author Franz Wilhelmstötter
* @since 3.0
* @version 3.0
*/
public class NumericalContext {
/**
* The default context used for linear algebra calculations. It can be
* defined on the command line with a Java property
* {@code io.jenetics.lattices.precission}, where {@code precission} is the
* number of significant decimal digits. The epsilon values is
* calculated as follows: {@code Math.pow(10, -precission)}.
*/
private static final NumericalContext DEFAULT_EPSILON_CONTEXT =
new NumericalContext(EnvPrecission.EPSILON);
/**
* Numerical context with an {@link #epsilon()} of zero.
*/
public static final NumericalContext ZERO_EPSILON =
new NumericalContext(0.0) {
@Override
public boolean equals(final double a, final double b) {
return Double.compare(a, b) == 0;
}
};
// Holds the current context.
private static final Context CONTEXT =
new Context<>(DEFAULT_EPSILON_CONTEXT);
private final double epsilon;
/**
* Create a new numerical context with the given epsilon.
*
* @param epsilon the {@code epsilon} of this context
*/
public NumericalContext(final double epsilon) {
this.epsilon = abs(epsilon);
}
/**
* Return the epsilon value used in this numerical context.
*
* @return the epsilon value used in this numerical context
*/
public double epsilon() {
return epsilon;
}
/**
* Checks if the given two {@code double} values are equal, obeying the
* defined {@link #epsilon()}.
*
* @param a the first value to compare
* @param b the second value to compare
* @return {@code true} if the given values are equal, modulo the given
* {@link #epsilon()}, {@code false} otherwise
*/
public boolean equals(final double a, final double b) {
return Double.compare(a, b) == 0 || abs(a - b) <= epsilon();
}
/**
* Tests whether the given value {@code a} is greater than zero.
*
* @param a the value to test
* @return {@code true} if the given value is greater than zero, {@code false}
* otherwise
*/
public boolean isGreaterZero(final double a) {
return abs(a) > epsilon() && Double.compare(a, 0.0) > 0;
}
/**
* Tests whether the given value {@code a} is smaller than zero.
*
* @param a the value to test
* @return {@code true} if the given value is smaller than zero, {@code false}
* otherwise
*/
public boolean isSmallerZero(final double a) {
return abs(a) > epsilon() && Double.compare(a, 0.0) < 0;
}
/**
* Tests whether the given double value is zero, according to the defined
* {@link #epsilon()}.
*
* @param a the value to test
* @return {@code true} if the given value is (near) zero, {@code false}
* otherwise
*/
public boolean isZero(final double a) {
return equals(a, 0);
}
/**
* Tests whether the given double value is not zero, according to the defined
* {@link #epsilon()}.
*
* @param a the value to test
* @return {@code true} if the given value is not (near) zero, {@code false}
* otherwise
*/
public boolean isNotZero(final double a) {
return !isZero(a);
}
/**
* Tests whether the given double value is one, according to the defined
* {@link #epsilon()}.
*
* @param a the value to test
* @return {@code true} if the given value is (near) one, {@code false}
* otherwise
*/
public boolean isOne(final double a) {
return equals(a, 1);
}
@Override
public String toString() {
return "NumericalContext[epsilon=%f]".formatted(epsilon);
}
/* *************************************************************************
* Accessor methods.
* ************************************************************************/
/**
* Return the current numerical context.
*
* @return the current numerical context
*/
public static NumericalContext get() {
return CONTEXT.get();
}
/**
* Set a new {@link NumericalContext} for the global scope.
*
* @param context the new {@link NumericalContext} for the global
* scope
* @throws NullPointerException if the {@code context} object is {@code null}
*/
public static void set(final NumericalContext context) {
requireNonNull(context);
CONTEXT.set(context);
}
/**
* Set the context object to its default value.
*/
public static void reset() {
CONTEXT.reset();
}
/**
* Executes the given {@code task} with the new numerical {@code context}.
* The numerical context effects only the task execution. The following
* example shows how to solve a matrix equation with a different numerical
* context then the default one.
*
* {@code
* final DoubleMatrix2d A = ...;
* final DoubleMatrix2d B = ...;
* NumericalContext.using(new NumericalContext(0.001), () -> {
* final DoubleMatrix2d X = Algebra.solve(A, B);
* });
* }
*
* The example above shuffles the given integer {@code seq} using the
* given {@link RandomGeneratorFactory#getDefault()} factory.
*
* @since 7.0
*
* @param context the numerical context used in the given {@code task}
* @param task the {@code task} which is executed within the scope of
* the given numerical context
* @throws NullPointerException if one of the arguments is {@code null}
*/
public static void using(final NumericalContext context, final Runnable task) {
requireNonNull(context);
requireNonNull(task);
CONTEXT.with(context, c -> { task.run(); return null; });
}
/**
* Opens a new scope with the given numerical and executes the
* given {@code supplier}.
*
* @param context the numerical context used when executing the
* {@code supplier}
* @param supplier the supplier to execute with the new numerical context
* @return the supplier result
* @param the numerical context type
* @param the type of the supplier result
*/
public static T with(
final C context,
final Supplier extends T> supplier
) {
requireNonNull(context);
requireNonNull(supplier);
return CONTEXT.with(context, c -> supplier.get());
}
@SuppressWarnings("removal")
private static final class EnvPrecission {
private static final int DEFAULT_PRECISSION = 9;
private static final int PRECISSION =
java.security.AccessController.doPrivileged(
(java.security.PrivilegedAction)() ->
Integer.getInteger(
"io.jenetics.lattices.precision",
DEFAULT_PRECISSION
)
);
private static final double EPSILON = Math.pow(10, -PRECISSION);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy