| 17 | |
| 18 | @default_random_state |
| 19 | def do(self, problem, pop, parents=None, *args, random_state=None, **kwargs): |
| 20 | |
| 21 | # if a parents with array with mating indices is provided -> transform the input first |
| 22 | if parents is not None: |
| 23 | pop = [pop[mating] for mating in parents] |
| 24 | |
| 25 | # get the dimensions necessary to create in and output |
| 26 | n_parents, n_offsprings = self.n_parents, self.n_offsprings |
| 27 | n_matings, n_var = len(pop), problem.n_var |
| 28 | |
| 29 | # get the actual values from each of the parents |
| 30 | X = np.swapaxes( |
| 31 | np.array([[parent.get("X") for parent in mating] for mating in pop]), 0, 1 |
| 32 | ) |
| 33 | if self.vtype is not None: |
| 34 | X = X.astype(self.vtype) |
| 35 | |
| 36 | # the array where the offsprings will be stored to |
| 37 | Xp = np.empty(shape=(n_offsprings, n_matings, n_var), dtype=X.dtype) |
| 38 | |
| 39 | # the probability of executing the crossover |
| 40 | prob = get(self.prob, size=n_matings) |
| 41 | |
| 42 | # a boolean mask when crossover is actually executed |
| 43 | cross = random_state.random(n_matings) < prob |
| 44 | |
| 45 | # the design space from the parents used for the crossover |
| 46 | if np.any(cross): |
| 47 | # we can not prefilter for cross first, because there might be other variables using the same shape as X |
| 48 | Q = self._do(problem, X, *args, random_state=random_state, **kwargs) |
| 49 | assert Q.shape == (n_offsprings, n_matings, problem.n_var), ( |
| 50 | "Shape is incorrect of crossover impl." |
| 51 | ) |
| 52 | Xp[:, cross] = Q[:, cross] |
| 53 | |
| 54 | # now set the parents whenever NO crossover has been applied |
| 55 | for k in np.flatnonzero(~cross): |
| 56 | if n_offsprings < n_parents: |
| 57 | s = random_state.choice( |
| 58 | np.arange(self.n_parents), size=n_offsprings, replace=False |
| 59 | ) |
| 60 | elif n_offsprings == n_parents: |
| 61 | s = np.arange(n_parents) |
| 62 | else: |
| 63 | s = [] |
| 64 | while len(s) < n_offsprings: |
| 65 | s.extend(random_state.permutation(n_parents)) |
| 66 | s = s[:n_offsprings] |
| 67 | |
| 68 | Xp[:, k] = np.copy(X[s, k]) |
| 69 | |
| 70 | # flatten the array to become a 2d-array |
| 71 | Xp = Xp.reshape(-1, X.shape[-1]) |
| 72 | |
| 73 | # create a population object |
| 74 | off = Population.new("X", Xp) |
| 75 | |
| 76 | return off |