1 package net.bmahe.genetics4j.neat;
2
3 import java.util.function.Function;
4
5 /**
6 * Utility class providing common activation functions for NEAT (NeuroEvolution of Augmenting Topologies) neural
7 * networks.
8 *
9 * <p>Activations contains a collection of mathematical functions commonly used as activation functions in neural
10 * networks. These functions transform the weighted sum of inputs at each node into the node's output value, introducing
11 * non-linearity that enables neural networks to learn complex patterns.
12 *
13 * <p>Available activation functions:
14 * <ul>
15 * <li><strong>Linear</strong>: Simple linear transformation with slope and bias parameters</li>
16 * <li><strong>Sigmoid</strong>: Logistic function providing smooth transition between 0 and 1</li>
17 * <li><strong>Hyperbolic tangent</strong>: Smooth function providing output between -1 and 1</li>
18 * <li><strong>Identity</strong>: Pass-through function for linear networks</li>
19 * <li><strong>NEAT paper</strong>: Sigmoid variant with specific parameters from original NEAT research</li>
20 * </ul>
21 *
22 * <p>Function variations:
23 * <ul>
24 * <li><strong>Float versions</strong>: Optimized for float precision networks</li>
25 * <li><strong>Double versions</strong>: Higher precision for sensitive applications</li>
26 * <li><strong>Parameterized versions</strong>: Customizable function parameters</li>
27 * <li><strong>Pre-configured versions</strong>: Common parameter combinations</li>
28 * </ul>
29 *
30 * <p>Common usage patterns:
31 *
32 * <pre>{@code
33 * // Use standard sigmoid activation
34 * FeedForwardNetwork network = new FeedForwardNetwork(inputNodes, outputNodes, connections, Activations::sigmoid);
35 *
36 * // Custom sigmoid with different steepness
37 * Function<Double, Double> customSigmoid = Activations.sigmoid(2.0);
38 * FeedForwardNetwork steeperNetwork = new FeedForwardNetwork(inputNodes, outputNodes, connections, customSigmoid);
39 *
40 * // Hyperbolic tangent for outputs in [-1, 1] range
41 * FeedForwardNetwork tanhNetwork = new FeedForwardNetwork(inputNodes, outputNodes, connections, Activations::tanh);
42 *
43 * // Linear activation for regression problems
44 * FeedForwardNetwork linearNetwork = new FeedForwardNetwork(inputNodes,
45 * outputNodes,
46 * connections,
47 * Activations::identity);
48 *
49 * // Float versions for memory efficiency
50 * Function<Float, Float> floatSigmoid = Activations.sigmoidFloat;
51 * }</pre>
52 *
53 * <p>Activation function characteristics:
54 * <ul>
55 * <li><strong>Sigmoid</strong>: Smooth, bounded [0,1], good for binary classification</li>
56 * <li><strong>Tanh</strong>: Smooth, bounded [-1,1], zero-centered, often preferred over sigmoid</li>
57 * <li><strong>Linear</strong>: Unbounded, preserves gradients, suitable for regression</li>
58 * <li><strong>Identity</strong>: No transformation, useful for pass-through connections</li>
59 * </ul>
60 *
61 * <p>Performance considerations:
62 * <ul>
63 * <li><strong>Float vs Double</strong>: Float versions use less memory and may be faster</li>
64 * <li><strong>Function references</strong>: Pre-defined functions avoid object creation</li>
65 * <li><strong>Mathematical operations</strong>: Optimized implementations for common cases</li>
66 * <li><strong>Branch prediction</strong>: Simple functions improve CPU branch prediction</li>
67 * </ul>
68 *
69 * <p>Integration with NEAT evolution:
70 * <ul>
71 * <li><strong>Network evaluation</strong>: Applied to hidden and output nodes during forward propagation</li>
72 * <li><strong>Fitness computation</strong>: Affects network behavior and resulting fitness</li>
73 * <li><strong>Gradient flow</strong>: Function choice impacts learning and evolution dynamics</li>
74 * <li><strong>Problem matching</strong>: Different problems benefit from different activation functions</li>
75 * </ul>
76 *
77 * @see FeedForwardNetwork
78 * @see RecurrentNetwork
79 * @see NeatChromosome
80 * @see Function
81 */
82 public class Activations {
83
84 private Activations() {
85 }
86
87 /**
88 * Creates a linear activation function with specified slope and bias for float values.
89 *
90 * <p>The linear function computes: f(x) = a * x + b
91 *
92 * @param a the slope parameter
93 * @param b the bias parameter
94 * @return a linear activation function
95 */
96 public static Function<Float, Float> linearFloat(final float a, final float b) {
97 return x -> a * x + b;
98 }
99
100 /**
101 * Creates a linear activation function with specified slope and bias for double values.
102 *
103 * <p>The linear function computes: f(x) = a * x + b
104 *
105 * @param a the slope parameter
106 * @param b the bias parameter
107 * @return a linear activation function
108 */
109 public static Function<Double, Double> linear(final double a, final double b) {
110 return x -> a * x + b;
111 }
112
113 /**
114 * Creates a sigmoid activation function with specified steepness for float values.
115 *
116 * <p>The sigmoid function computes: f(x) = 1 / (1 + exp(-a * x))
117 * <p>Output range: (0, 1)
118 *
119 * @param a the steepness parameter (higher values create steeper transitions)
120 * @return a sigmoid activation function
121 */
122 public static Function<Float, Float> sigmoidFloat(final float a) {
123 return x -> 1.0f / (1.0f + (float) Math.exp(-a * x));
124 }
125
126 /**
127 * Creates a sigmoid activation function with specified steepness for double values.
128 *
129 * <p>The sigmoid function computes: f(x) = 1 / (1 + exp(-a * x))
130 * <p>Output range: (0, 1)
131 *
132 * @param a the steepness parameter (higher values create steeper transitions)
133 * @return a sigmoid activation function
134 */
135 public static Function<Double, Double> sigmoid(final double a) {
136 return x -> 1.0d / (1.0d + Math.exp(-a * x));
137 }
138
139 /** Standard sigmoid activation function for float values (steepness = 1.0). */
140 public static Function<Float, Float> sigmoidFloat = sigmoidFloat(1.0f);
141
142 /** Standard sigmoid activation function for double values (steepness = 1.0). */
143 public static Function<Double, Double> sigmoid = sigmoid(1.0d);
144
145 /** Identity activation function for float values (f(x) = x). */
146 public static Function<Float, Float> identityFloat = linearFloat(1.0f, 0.0f);
147
148 /** Identity activation function for double values (f(x) = x). */
149 public static Function<Double, Double> identity = linear(1.0d, 0.0d);
150
151 /** Hyperbolic tangent activation function for float values. Output range: (-1, 1). */
152 public static Function<Float, Float> tanhFloat = x -> (float) Math.tanh(x);
153
154 /** Hyperbolic tangent activation function for double values. Output range: (-1, 1). */
155 public static Function<Double, Double> tanh = Math::tanh;
156
157 /** Sigmoid activation function with steepness 4.9 as used in the original NEAT paper (float version). */
158 public static Function<Float, Float> neatPaperFloat = sigmoidFloat(4.9f);
159
160 /** Sigmoid activation function with steepness 4.9 as used in the original NEAT paper (double version). */
161 public static Function<Double, Double> neatPaper = sigmoid(4.9f);
162 }