ContiguousNeatNodeLayout.java
package net.bmahe.genetics4j.neat.spec;
import java.util.List;
import java.util.Objects;
import java.util.stream.IntStream;
import org.apache.commons.lang3.Validate;
/**
* Node layout using the traditional contiguous NEAT external IDs.
*
* <p>Inputs occupy {@code [0, numInputs)} and outputs occupy {@code [numInputs, numInputs + numOutputs)}. The
* configured
* hidden-node range must not overlap those external IDs.
*/
public class ContiguousNeatNodeLayout implements NeatNodeLayout {
private final int numInputs;
private final int numOutputs;
private final long hiddenNodeIdStartInclusive;
private final long hiddenNodeIdEndExclusive;
/**
* Creates a contiguous node layout.
*
* @param _numInputs number of input nodes; must be positive
* @param _numOutputs number of output nodes; must be positive
* @param _hiddenNodeIdStartInclusive inclusive hidden-node range start
* @param _hiddenNodeIdEndExclusive exclusive hidden-node range end
* @throws IllegalArgumentException if the counts or hidden-node range are invalid, or if the ranges overlap
*/
public ContiguousNeatNodeLayout(final int _numInputs,
final int _numOutputs,
final long _hiddenNodeIdStartInclusive,
final long _hiddenNodeIdEndExclusive) {
Validate.isTrue(_numInputs > 0, "Number of inputs must be positive");
Validate.isTrue(_numOutputs > 0, "Number of outputs must be positive");
final long externalEnd = (long) _numInputs + _numOutputs;
Validate.isTrue(externalEnd <= Integer.MAX_VALUE, "External node IDs exceed the int domain");
NeatNodeLayoutValidation.validate(
IntStream.range(0, _numInputs).boxed().toList(),
IntStream.range(_numInputs, _numInputs + _numOutputs).boxed().toList(),
_hiddenNodeIdStartInclusive,
_hiddenNodeIdEndExclusive);
numInputs = _numInputs;
numOutputs = _numOutputs;
hiddenNodeIdStartInclusive = _hiddenNodeIdStartInclusive;
hiddenNodeIdEndExclusive = _hiddenNodeIdEndExclusive;
}
@Override
public int numInputs() {
return numInputs;
}
@Override
public int numOutputs() {
return numOutputs;
}
@Override
public long hiddenNodeIdStartInclusive() {
return hiddenNodeIdStartInclusive;
}
@Override
public long hiddenNodeIdEndExclusive() {
return hiddenNodeIdEndExclusive;
}
@Override
public List<Integer> inputNodeIds() {
return IntStream.range(0, numInputs).boxed().toList();
}
@Override
public List<Integer> outputNodeIds() {
return IntStream.range(numInputs, numInputs + numOutputs).boxed().toList();
}
@Override
public boolean isInput(final int nodeId) {
return nodeId >= 0 && nodeId < numInputs;
}
@Override
public boolean isOutput(final int nodeId) {
return nodeId >= numInputs && nodeId < numInputs + numOutputs;
}
@Override
public int hashCode() {
return Objects.hash(hiddenNodeIdEndExclusive, hiddenNodeIdStartInclusive, numInputs, numOutputs);
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof ContiguousNeatNodeLayout other) {
return hiddenNodeIdEndExclusive == other.hiddenNodeIdEndExclusive
&& hiddenNodeIdStartInclusive == other.hiddenNodeIdStartInclusive && numInputs == other.numInputs
&& numOutputs == other.numOutputs;
}
return false;
}
@Override
public String toString() {
return "ContiguousNeatNodeLayout [numInputs=" + numInputs + ", numOutputs=" + numOutputs
+ ", hiddenNodeIdStartInclusive=" + hiddenNodeIdStartInclusive + ", hiddenNodeIdEndExclusive="
+ hiddenNodeIdEndExclusive + "]";
}
}