Crossover
hgp_lib.crossover.crossover_executor.CrossoverExecutor
Coordinates subtree crossover operations across a collection of Rule trees.
The executor randomly selects pairs of rules based on crossover_p, then exchanges
subtrees between the paired parents to produce offspring. An optional validator can
reject invalid children, with configurable retry attempts.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
crossover_p
|
float
|
Probability of selecting each rule for crossover. Default: |
0.7
|
crossover_strategy
|
str
|
Strategy for pairing rules. Must be |
'random'
|
check_valid
|
Callable[[Rule], bool] | None
|
Optional validator executed after crossover. When supplied, each child must
pass validation or the crossover is retried up to |
None
|
num_tries
|
int
|
Maximum number of crossover attempts per pair when validation fails.
Must be |
1
|
operator_p
|
float
|
Probability of selecting an operator node (vs. a literal) when choosing
a crossover point in the rule tree. Must be in [0.0, 1.0]. Default: |
0.9
|
Examples:
>>> import random
>>> import numpy as np
>>> from hgp_lib.crossover import CrossoverExecutor
>>> from hgp_lib.rules import And, Or, Literal
>>> random.seed(42); np.random.seed(42)
>>> executor = CrossoverExecutor(crossover_p=1.0)
>>> rules = [
... And([Literal(value=0), Literal(value=1)]),
... Or([Literal(value=2), Literal(value=3)])
... ]
>>> children, parent_indices = executor.apply(rules, [None, None])
>>> len(children)
2
Source code in hgp_lib\crossover\crossover_executor.py
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 | |
apply(rules, feature_mappings=None)
Applies crossover to the provided list of rules and returns children with parent tracking.
Rules are randomly selected for crossover based on crossover_p, paired
consecutively, and their subtrees are exchanged. Before crossover, feature mappings
are applied to translate rules from child populations (which may use different
feature indices) into the parent's feature space.
This method supports hierarchical GP by tracking which parent rules contributed to each child, enabling score propagation back to child populations.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
rules
|
List[Rule]
|
The collection of parent rules that will undergo crossover. May include rules from both the current population and child populations. |
required |
feature_mappings
|
List[dict | None] | None
|
A list of feature mapping dictionaries, one per rule. Each mapping translates
feature indices from a child population's space to the parent's space.
Use None for rules that don't need remapping (i.e., from the current population).
Default: |
None
|
Returns: Tuple[List[Rule], List[int]]: A tuple containing: - List[Rule]: The children produced by crossover operations. - List[int]: The indices of parent rules that contributed to each child. For each child, both parent indices are recorded (so the list length is 2 * number of children).
Examples:
>>> import random
>>> import numpy as np
>>> from hgp_lib.crossover import CrossoverExecutor
>>> from hgp_lib.rules import And, Or, Literal
>>> random.seed(42); np.random.seed(42)
>>> executor = CrossoverExecutor(crossover_p=1.0)
>>> rules = [
... And([Literal(value=0), Literal(value=1)]),
... Or([Literal(value=2), Literal(value=3)])
... ]
>>> children, parent_indices = executor.apply(rules, [None, None])
>>> len(children)
2
Source code in hgp_lib\crossover\crossover_executor.py
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 | |
crossover(parent_a, parent_b)
Performs subtree crossover between two parent rules.
A random node is selected from each parent, and the subtrees rooted at those
nodes are exchanged using deep_swap. When a validator is provided, each child
is validated individually and accepted children are collected until at least two pass
validation or num_tries attempts are exhausted.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
parent_a
|
Rule
|
First parent rule. |
required |
parent_b
|
Rule
|
Second parent rule. |
required |
Returns:
| Type | Description |
|---|---|
Sequence[Rule]
|
Sequence[Rule]: Children with exchanged subtrees. Returns two children when no validator is provided, or up to two valid children when a validator is used. |
Examples:
>>> import random
>>> from hgp_lib.crossover import CrossoverExecutor
>>> from hgp_lib.rules import And, Or, Literal
>>> random.seed(0)
>>> executor = CrossoverExecutor()
>>> parent_a = And([Literal(value=0), Literal(value=1)])
>>> parent_b = Or([Literal(value=2), Literal(value=3)])
>>> child_a, child_b = executor.crossover(parent_a, parent_b)
>>> parent_a is child_a
False
Source code in hgp_lib\crossover\crossover_executor.py
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 | |
hgp_lib.crossover.crossover_factory.CrossoverExecutorFactory
Factory for creating configured CrossoverExecutor instances.
This factory encapsulates all crossover configuration parameters except
the optional check_valid callable, which is supplied at creation time.
This is useful when the same crossover configuration is reused with
different validation strategies.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
crossover_p
|
float
|
Probability of selecting each rule for crossover. Default: |
0.7
|
crossover_strategy
|
str
|
Strategy for pairing rules. Must be |
'random'
|
num_tries
|
int
|
Maximum number of crossover attempts per pair when validation fails.
Must be |
1
|
operator_p
|
float
|
Probability of selecting an operator node (vs. a literal) when choosing
a crossover point in the rule tree. Must be in [0.0, 1.0]. Default: |
0.9
|
Examples:
>>> import random
>>> import numpy as np
>>> from hgp_lib.crossover import CrossoverExecutorFactory
>>> from hgp_lib.rules import And, Or, Literal
>>> factory = CrossoverExecutorFactory(crossover_p=1.0)
>>> def validator(rule):
... return True
>>> executor = factory.create(validator)
>>> rules = [
... And([Literal(value=0), Literal(value=1)]),
... Or([Literal(value=2), Literal(value=3)])
... ]
>>> children, parent_indices = executor.apply(rules, [None, None])
>>> len(children)
2
>>> # Without validator
>>> factory = CrossoverExecutorFactory(crossover_p=1.0, num_tries=1)
>>> executor = factory.create(None)
>>> len(executor.apply(rules, [None, None])[0])
2
Source code in hgp_lib\crossover\crossover_factory.py
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 | |
create(check_valid=None)
Create a new CrossoverExecutor using the factory configuration.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
check_valid
|
Callable[[Rule], bool] | None
|
Optional validator executed after crossover. When supplied, each
child must pass validation or the crossover is retried up to
|
None
|
Returns:
| Name | Type | Description |
|---|---|---|
CrossoverExecutor |
CrossoverExecutor
|
A configured crossover executor instance. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Examples:
>>> factory = CrossoverExecutorFactory(crossover_p=1.0)
>>> executor = factory.create(lambda r: True)
>>> isinstance(executor, CrossoverExecutor)
True
Source code in hgp_lib\crossover\crossover_factory.py
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 | |