1 package net.bmahe.genetics4j.neat.spec;
2
3 import java.util.List;
4
5 /**
6 * Describes the stable external node identities and hidden-node namespace of a NEAT chromosome.
7 *
8 * <p>Input and output lists preserve declaration order. That order defines the mapping between external vectors and
9 * node IDs, while the IDs themselves are opaque and may be sparse. The hidden-node range is half-open: its lower bound
10 * is inclusive and its upper bound is exclusive.
11 *
12 * <p>Implementations must provide non-empty input and output lists containing unique, non-negative, disjoint IDs. No
13 * external ID may fall inside the hidden-node range. Implementations should also provide value semantics when they are
14 * used as chromosome layouts.
15 *
16 * @see ContiguousNeatNodeLayout
17 * @see ExplicitNeatNodeLayout
18 */
19 public interface NeatNodeLayout {
20
21 /** Exclusive upper bound of the non-negative {@code int} node-ID domain. */
22 long MAX_NODE_ID_EXCLUSIVE = (long) Integer.MAX_VALUE + 1L;
23
24 /**
25 * Returns input node IDs in external vector order.
26 *
27 * @return an ordered, non-empty list of input node IDs
28 */
29 List<Integer> inputNodeIds();
30
31 /**
32 * Returns output node IDs in external vector order.
33 *
34 * @return an ordered, non-empty list of output node IDs
35 */
36 List<Integer> outputNodeIds();
37
38 /**
39 * Returns the first ID reserved for hidden nodes.
40 *
41 * @return the inclusive hidden-node range start
42 */
43 long hiddenNodeIdStartInclusive();
44
45 /**
46 * Returns the exclusive end of the hidden-node namespace.
47 *
48 * @return the exclusive hidden-node range end
49 */
50 long hiddenNodeIdEndExclusive();
51
52 /** @return the number of declared input nodes */
53 default int numInputs() {
54 return inputNodeIds().size();
55 }
56
57 /** @return the number of declared output nodes */
58 default int numOutputs() {
59 return outputNodeIds().size();
60 }
61
62 /**
63 * Tests whether an ID identifies an input node.
64 *
65 * @param nodeId node ID to test
66 * @return {@code true} when the ID is a declared input
67 */
68 default boolean isInput(final int nodeId) {
69 return inputNodeIds().contains(nodeId);
70 }
71
72 /**
73 * Tests whether an ID identifies an output node.
74 *
75 * @param nodeId node ID to test
76 * @return {@code true} when the ID is a declared output
77 */
78 default boolean isOutput(final int nodeId) {
79 return outputNodeIds().contains(nodeId);
80 }
81
82 /**
83 * Tests whether an ID identifies either an input or output node.
84 *
85 * @param nodeId node ID to test
86 * @return {@code true} when the ID is external
87 */
88 default boolean isExternal(final int nodeId) {
89 return isInput(nodeId) || isOutput(nodeId);
90 }
91
92 /**
93 * Tests whether an ID belongs to the reserved hidden-node range.
94 *
95 * @param nodeId node ID to test
96 * @return {@code true} when the ID is in the hidden-node namespace
97 */
98 default boolean isHidden(final int nodeId) {
99 return nodeId >= hiddenNodeIdStartInclusive() && nodeId < hiddenNodeIdEndExclusive();
100 }
101
102 /**
103 * Tests semantic compatibility with another layout.
104 *
105 * <p>Compatible layouts declare identical ordered input and output IDs and the same hidden-node range. Their
106 * concrete
107 * implementation classes do not need to match.
108 *
109 * @param other layout to compare
110 * @return {@code true} when chromosomes using the layouts can safely be combined
111 */
112 default boolean isCompatibleWith(final NeatNodeLayout other) {
113 return other != null && inputNodeIds().equals(other.inputNodeIds())
114 && outputNodeIds().equals(other.outputNodeIds())
115 && hiddenNodeIdStartInclusive() == other.hiddenNodeIdStartInclusive()
116 && hiddenNodeIdEndExclusive() == other.hiddenNodeIdEndExclusive();
117 }
118
119 /**
120 * Creates a traditional contiguous layout with the remainder of the non-negative {@code int} domain reserved for
121 * hidden nodes.
122 *
123 * @param numInputs number of input nodes; must be positive
124 * @param numOutputs number of output nodes; must be positive
125 * @return a validated contiguous layout
126 */
127 static NeatNodeLayout contiguous(final int numInputs, final int numOutputs) {
128 final long hiddenStart = (long) numInputs + numOutputs;
129 return contiguous(numInputs, numOutputs, hiddenStart, MAX_NODE_ID_EXCLUSIVE);
130 }
131
132 /**
133 * Creates a contiguous external layout with an explicit hidden-node range.
134 *
135 * @param numInputs number of input nodes; must be positive
136 * @param numOutputs number of output nodes; must be positive
137 * @param hiddenNodeIdStartInclusive inclusive hidden-node range start
138 * @param hiddenNodeIdEndExclusive exclusive hidden-node range end
139 * @return a validated contiguous layout
140 */
141 static NeatNodeLayout contiguous(final int numInputs, final int numOutputs, final long hiddenNodeIdStartInclusive,
142 final long hiddenNodeIdEndExclusive) {
143 return new ContiguousNeatNodeLayout(numInputs, numOutputs, hiddenNodeIdStartInclusive, hiddenNodeIdEndExclusive);
144 }
145
146 /**
147 * Creates a layout with explicitly ordered external IDs and a dedicated hidden-node range.
148 *
149 * @param inputNodeIds input IDs in vector order
150 * @param outputNodeIds output IDs in vector order
151 * @param hiddenNodeIdStartInclusive inclusive hidden-node range start
152 * @param hiddenNodeIdEndExclusive exclusive hidden-node range end
153 * @return a validated explicit layout
154 */
155 static NeatNodeLayout explicit(final List<Integer> inputNodeIds, final List<Integer> outputNodeIds,
156 final long hiddenNodeIdStartInclusive, final long hiddenNodeIdEndExclusive) {
157 return new ExplicitNeatNodeLayout(inputNodeIds,
158 outputNodeIds,
159 hiddenNodeIdStartInclusive,
160 hiddenNodeIdEndExclusive);
161 }
162 }