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