View Javadoc
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 }