def rand_trans(nst: int, npl: int = 1, sparsity: float = 1., rng: np.random.Generator = RNG, **kwds) -> la.lnarray: """ Make a random transition matrix (continuous time). Parameters ---------- n : int total number of states npl : int number of matrices sparsity : float, optional sparsity, by default 1 Returns ------- mat : la.lnarray transition matrix """ params = rng.random((npl, num_param(nst, **kwds))) if sparsity < 1.: ind = rng.random(params.shape) params[ind > sparsity] = 0. return params_to_mat(params, **kwds)
def generate_problem(n: int, rg: np.random.Generator): route = list(range(n)) rg.shuffle(route) dm = rg.random((n, n)) * 1000 tm = rg.random((n, n)) * 1000 tw = rg.random(n) * 1000 tw = np.vstack((tw, tw + 1000)).T t = rg.random() * 100 return [route, dm, tm, tw, t, {}]
def get_random_point( self, boundary_distance: float = 0, cartesian: bool = True, avoid_center: bool = False, rng: np.random.Generator = None, ) -> np.ndarray: """return a random point within the grid Note that these points will be uniformly distributed on the radial axis, which implies that they are not uniformly distributed in the volume. Args: boundary_distance (float): The minimal distance this point needs to have from all boundaries. cartesian (bool): Determines whether the point is returned in Cartesian coordinates or grid coordinates. avoid_center (bool): Determines whether the boundary distance should also be kept from the center, i.e., whether points close to the center are returned. rng (:class:`~numpy.random.Generator`): Random number generator (default: :func:`~numpy.random.default_rng()`) Returns: :class:`~numpy.ndarray`: The coordinates of the point """ if rng is None: rng = np.random.default_rng() # handle the boundary distance r_min = boundary_distance if avoid_center else 0 r_mag = self.radius - boundary_distance - r_min z_min, z_max = self.axes_bounds[1] if boundary_distance != 0: z_min += boundary_distance z_max -= boundary_distance if r_mag <= 0 or z_max <= z_min: raise RuntimeError( "Random points would be too close to boundary") # create random point r = r_mag * rng.random() + r_min z = z_min + (z_max - z_min) * rng.random() point = np.array([r, z]) if cartesian: return self.point_to_cartesian(point) else: return point
def sample_generator(rg: np.random.Generator, locs): lat, lon = get_sample(locs, rg, cd, sample_df, CHC_df, CHC_sub, CHC_sub_dict, save=False) time_windows = np.zeros((len(lat), 2)) for i in range(len(lat)): if rg.random() > 0.5: time_windows[i, 0] = 0 time_windows[i, 1] = 14400 else: time_windows[i, 0] = 14400 time_windows[i, 1] = 28800 customers = [ Customer( lat[i], lon[i], 0.8, 0.8, rg=rg, presence=[1 for _ in range(28800)], presence_interval=1.6, ) for i in range(len(lat)) ] return customers, time_windows
def sample_generator(rg: np.random.Generator): lat, lon = get_sample( arrival_rate, # rg.poisson(arrival_rate), rg, cd, sample_df, CHC_df, CHC_sub, CHC_sub_dict, save=False, ) time_windows = np.zeros((len(lat), 2)) for i in range(len(lat)): interval = (day_end - day_start) / num_time_windows for j in range(num_time_windows): if rg.random() > (num_time_windows - (j + 1)) / num_time_windows: time_windows[i, 0] = interval * j time_windows[i, 1] = interval * (j + 1) break customers = [ Customer(lat[i], lon[i], 0.5, 0.5, rg=rg) for i in range(len(lat)) ] return customers, time_windows
def rand_trans_d(nst: int, npl: int = 1, sparsity: float = 1., rng: np.random.Generator = RNG, **kwds) -> la.lnarray: """ Make a random transition matrix (discrete time). Parameters ---------- n : int total number of states npl : int number of matrices sparsity : float, optional sparsity, by default 1 Returns ------- mat : la.lnarray transition matrix """ if any(kwds.get(opt, False) for opt in ('uniform', 'serial', 'ring')): trans = rand_trans(nst, npl, sparsity, rng, **kwds) stochastify_pd(trans) return trans trans = rng.random((npl, nst, nst)).squeeze() stochastify_d(trans) return trans
def get_random_point( self, boundary_distance: float = 0, cartesian: bool = True, rng: np.random.Generator = None, ) -> np.ndarray: """return a random point within the grid Args: boundary_distance (float): The minimal distance this point needs to have from all boundaries. cartesian (bool): Determines whether the point is returned in Cartesian coordinates or grid coordinates. This does not have any effect for Cartesian coordinate systems, but the argument is retained to have a unified interface for all grids. rng (:class:`~numpy.random.Generator`): Random number generator (default: :func:`~numpy.random.default_rng()`) Returns: :class:`~numpy.ndarray`: The coordinates of the point """ if rng is None: rng = np.random.default_rng() # handle the boundary distance cuboid = self.cuboid if boundary_distance != 0: if any(cuboid.size <= 2 * boundary_distance): raise RuntimeError( "Random points would be too close to boundary") cuboid = cuboid.buffer(-boundary_distance) # create random point return cuboid.pos + rng.random(self.dim) * cuboid.size # type: ignore
def rand_fancyindex( rng: np.random.Generator, index_length: int, dtype: np.dtype, source_arr_len: int, invalid_ratio: Optional[float] = None, ) -> np.ndarray: """Create a random fancy index with the specified length and dtype.""" check_params(dtype, invalid_ratio) if dtype.kind not in "iu": # TODO: Also support floats, since mbget allows that raise ValueError( f"Only integer dtypes are currently supported by this method. dtype={dtype.name}" ) # Generate the fancy index from the uniform integer distribution. fancyindex = FastArray( rng.integers(0, source_arr_len, size=index_length, dtype=dtype) ) # If the fancy index should have some invalids/NA values, add those in now. if invalid_ratio is not None and invalid_ratio > 0.0: # TODO: Also add in some out-of-bounds accesses (and not just invalid/NA values) here? invalid_outcomes = FastArray(rng.random(size=index_length)) putmask(fancyindex, invalid_outcomes < invalid_ratio, fancyindex.inv) return fancyindex
def _ctmp_inverse( n_samples: int, probs: np.ndarray, gamma: float, csc_data: np.ndarray, csc_indices: np.ndarray, csc_indptrs: np.ndarray, rng: np.random.Generator) -> Tuple[Tuple[int], Tuple[int]]: """Apply CTMP algorithm to input counts dictionary, return sampled counts and associated shots. Equivalent to Algorithm 1 in Bravyi et al. Args: n_samples: Number of times to sample in CTMP algorithm. probs: probability vector constructed from counts. gamma: noise strength parameter csc_data: Sparse CSC matrix data array (`csc_matrix.data`). csc_indices: Sparse CSC matrix indices array (`csc_matrix.indices`). csc_indptrs: Sparse CSC matrix indices array (`csc_matrix.indptrs`). rng: RNG generator object. Returns: Tuple[Tuple[int], Tuple[int]]: Resulting list of shots and associated signs from the CTMP algorithm. """ alphas = rng.poisson(lam=gamma, size=n_samples) signs = np.mod(alphas, 2) x_vals = rng.choice(len(probs), size=n_samples, p=probs) # Apply CTMP sampling r_vals = rng.random(size=alphas.sum()) y_vals = np.zeros(x_vals.size, dtype=np.int) _markov_chain_compiled(y_vals, x_vals, r_vals, alphas, csc_data, csc_indices, csc_indptrs) return y_vals, signs
def sim_markov_c(rates: la.lnarray, peq: Optional[np.ndarray] = None, num_jump: Optional[int] = None, max_time: Optional[float] = None, rng: np.random.Generator = RNG) -> Tuple[la.lnarray, ...]: """Simulate Markov process trajectory. Parameters ---------- rates : la.lnarray (n,n) Continuous time stochastic matrix. peq : la.lnarray (n,), optional Initial-state distribution, default: use steady-state. num_jump : int, optional, default: None Stop after this many jumps. max_time : float, optional, default: None Stop after this much time. Returns ------- states : la.lnarray (w,) Vector of states visited. dwells : la.lnarray (w,) Time spent in each state. """ rates = la.asanyarray(rates) if peq is None: peq = calc_peq(rates)[0] num_states = len(peq) dwell = -1. / np.diagonal(rates) jump = rates * dwell.c jump[np.diag_indices(num_states)] = 0. est_num = num_jump if num_jump is None: if max_time is None: raise ValueError("Must specify either num_jump or max_time") est_num = int(5 * max_time / mean_dwell(rates, peq)) if max_time is None: max_time = np.inf est_num = max(est_num, 1) dwells_from = -dwell.c * np.log(rng.random(est_num)) states = sim_markov_d(jump, peq, est_num, rng) dwells = dwells_from[states, la.arange(est_num)] states, dwells = states[slice(num_jump)], dwells[slice(num_jump)] cum_dwell = np.cumsum(dwells) mask = cum_dwell < max_time if not mask[-1]: ind = np.nonzero(~mask)[0][0] mask[ind] = True dwells[ind] -= cum_dwell[ind] - max_time states, dwells = states[mask], dwells[mask] return states, dwells
def get_index_growth( rng: np.random.Generator, size: Tuple[int, int], ) -> np.ndarray: """Calculates cumulative growth to mimic index growth.""" # Subtracts 0.2 from values [0, 1] so that 1/5 have negative sign. # This is arbitrary, and results in the index increasing in 4 out # of 5 months. signs = np.sign(rng.random(size) - 0.2) # Takes a poisson dist with lambda = 1 and adds some random noise. # Multiply by signs to apply increasing / decreasing. pois = rng.poisson(1, size) noise = rng.random(size) growth = (pois + noise) * signs growth[0, :] = 0 # No growth at index start. return growth.cumsum(axis=0)
def markov_presence(n, prob: float, rg: np.random.Generator): """Simulates a symmetric markov chain, to generate a presence array for the customer.""" presence = np.zeros(n, dtype=bool) presence[0] = True # Start in state with 50% chance for i in range(n - 1): if rg.random() < prob: presence[i + 1] = not presence[i] else: presence[i + 1] = presence[i] return presence
def rand_array(rng: np.random.Generator, length: int, dtype: np.dtype, invalid_ratio: Optional[float] = None) -> np.ndarray: # TODO: Implement a flag that controls whether invalid values are included in the array? Or (instead) an invalid_ratio parameter like our other functions? check_params(dtype, invalid_ratio) if dtype.kind in "iu": info = np.iinfo(dtype) arr = FastArray(rng.integers(info.min, info.max, size=length, dtype=dtype)) elif dtype.kind == "f": # PERF: Use an FMA function here if we ever implement one arr = (FastArray(rng.random(size=length, dtype=dtype)) * 1e10) - 0.5e10 elif dtype.kind == "S": # Generate integers in the upper ASCII range, then use a view to expose those # values as fixed-length ASCII strings. # TODO: Support other character ranges (lower-range ASCII 0-127, full ASCII 0-255, lowercase+uppercase+digits). arr = FastArray(rng.integers( 65, 90, size=length * dtype.itemsize, dtype=np.int8, endpoint=True ).view(dtype)) elif dtype.kind == "U": # Generate integers in the upper ASCII range. # TODO: Support other character ranges (lower-range ASCII 0-127, full ASCII 0-255, lowercase+uppercase+digits, Unicode chars >255). arr = FastArray(rng.integers( 65, 90, size=length * (dtype.itemsize // 4), dtype=np.int32, endpoint=True ).view(dtype)) else: # TODO: Handle other dtypes raise NotImplementedError( f"The dtype {dtype} is not yet supported by this function." ) # If the fancy index should have some invalids/NA values, add those in now. if invalid_ratio is not None and invalid_ratio > 0.0: # TODO: Also add in some out-of-bounds accesses (and not just invalid/NA values) here? invalid_outcomes = FastArray(rng.random(size=length)) putmask(arr, invalid_outcomes < invalid_ratio, arr.inv) return arr
def gen_signal_2d_rectangle(ps2d: np.ndarray, f: np.ndarray, x: np.ndarray, y: np.ndarray, rng: np.random.Generator, fminx: float, fminy: float, fmax: float, alpha: int = 10) -> np.ndarray: """TODO(lgaultier)""" revert = fminy < fminx if revert: fmin, fminy = fminy, fminx x, y = y, x else: fmin = fminx # Go alpha times further in frequency to avoid interpolation aliasing. fmaxr = alpha * fmax # Build the 2D PSD following the given 1D PSD fx = np.concatenate(([0], f)) fy = np.concatenate(([0], np.arange(fminy, fmaxr + fminy, fminy))) dfx, dfy = fmin, fminy phase = rng.random((2 * len(fy) - 1, len(fx))) * 2 * np.pi phase[0, 0] = 0. phase[-len(fy) + 1:, 0] = -phase[1:len(fy), 0][::-1] fft2a = np.concatenate((0.25 * ps2d, 0.25 * ps2d[1:, :][::-1, :]), axis=0) fft2a = np.sqrt(fft2a) * np.exp(1j * phase) / np.sqrt((dfx * dfy)) fft2 = np.zeros((2 * len(fy) - 1, 2 * len(fx) - 1), dtype=complex) fft2[:, :len(fx)] = fft2a fft2[1:, -len(fx) + 1:] = fft2a[1:, 1:].conj()[::-1, ::-1] fft2[0, -len(fx) + 1:] = fft2a[0, 1:].conj()[::-1] sg = (4 * fy[-1] * fx[-1]) * np.real(IFFT2(fft2)) xg = np.linspace(0, 1 / fmin, sg.shape[1]) yg = np.linspace(0, 1 / fminy, sg.shape[0]) xgmax = xg.max() ygmax = yg.max() yl = y - y[0] yl = yl[yl < yg.max()] xl = x - x[0] xl = xl[xl < xg.max()] rectangle = np.ascontiguousarray( scipy.interpolate.interp2d(xg, yg, sg)(xl, yl)) signal = _calculate_signal(rectangle, x, y, xgmax, ygmax) return signal.transpose() if revert else signal
def _gen_signal_1d(fi: np.ndarray, psi: np.ndarray, x: np.ndarray, rng: np.random.Generator, fmin: Optional[float] = None, fmax: Optional[float] = None, alpha: int = 10, lf_extpl: bool = False, hf_extpl: bool = False) -> np.ndarray: """Generate 1d random signal using Fourier coefficient""" # Make sure fi, PSi does not contain the zero frequency: psi = psi[fi > 0] fi = fi[fi > 0] # Adjust fmin and fmax to fi bounds if not specified. Values are bounded # with respect to the frequencies of the processed spectrum. fmin = fmin or fi[0] fmax = fmax or fi[-1] # Go alpha times further in frequency to avoid interpolation aliasing. fmaxr = alpha * fmax # Interpolation of the non-zero part of the spectrum f = np.arange(fmin, fmaxr + fmin, fmin) mask = psi > 0 ps = np.exp(np.interp(np.log(f), np.log(fi[mask]), np.log(psi[mask]))) # lf_extpl=True prolongates the PSi as a plateau below min(fi). # Otherwise, we consider zeros values. same for hf ps[f < fi[0]] = psi[0] if lf_extpl else 0 ps[f > fi[-1]] = psi[-1] if hf_extpl else 0 ps[f > fmax] = 0 # Detect the sections (if any) where PSi==0 and apply it to PS mask = np.interp(f, fi, psi) ps[mask == 0] = 0 f_size = f.size phase = np.empty((2 * f_size + 1)) phase[1:(f_size + 1)] = rng.random(f_size) * 2 * np.pi phase[0] = 0 phase[-f_size:] = -phase[1:(f_size + 1)][::-1] fft1a = np.concatenate((np.array([0]), 0.5 * ps, 0.5 * ps[::-1]), axis=0) fft1a = np.sqrt(fft1a) * np.exp(1j * phase) / np.sqrt(fmin) yg = 2 * fmaxr * np.real(IFFT(fft1a)) xg = np.linspace(0, 0.5 / fmaxr * yg.shape[0], yg.shape[0]) return np.interp(np.mod(x, xg.max()), xg, yg)
def get_random_point( self, *, boundary_distance: float = 0, coords: str = "cartesian", rng: np.random.Generator = None, cartesian: bool = None, ) -> np.ndarray: """return a random point within the grid Args: boundary_distance (float): The minimal distance this point needs to have from all boundaries. coords (str): Determines the coordinate system in which the point is specified. Valid values are `cartesian`, `cell`, and `grid`; see :meth:`~pde.grids.base.GridBase.transform`. rng (:class:`~numpy.random.Generator`): Random number generator (default: :func:`~numpy.random.default_rng()`) Returns: :class:`~numpy.ndarray`: The coordinates of the point """ if cartesian is not None: # deprecated on 2022-03-11 warnings.warn( "Argument `cartesian` is deprecated. Use `coords` instead") coords = "cartesian" if cartesian else "grid" if rng is None: rng = np.random.default_rng() # handle the boundary distance cuboid = self.cuboid if boundary_distance != 0: if any(cuboid.size <= 2 * boundary_distance): raise RuntimeError( "Random points would be too close to boundary") cuboid = cuboid.buffer(-boundary_distance) # create random point point = cuboid.pos + rng.random(self.dim) * cuboid.size if coords == "cartesian" or coords == "grid": return point # type: ignore elif coords == "cell": return self.transform(point, "grid", "cell") else: raise ValueError(f"Unknown coordinate system `{coords}`")
def simplex(self, best, parent_values: List, rng: np.random.Generator): assert len(parent_values) >= 1 assert type(best) == self.type assert list(map(type, parent_values)) == ([self.type] * len(parent_values)) centroid = sum(parent_values) / len(parent_values) gradient = best - centroid weight = EvolvableHP.WEIGHT_MAX * rng.random( ) - EvolvableHP.WEIGHT_MAX / 2 midpoint = best + centroid / 2 value = self.type(midpoint + gradient * weight) value = min(value, self.max) value = max(value, self.min) return self.type(value)
def prepare_partitions( label_list: ty.List[str], dataset_raw_folder: Path, test_fold_size: float = 0.1, val_fold_size: float = 0.1, rng: np.random.Generator = None, ) -> ty.Tuple[ty.Dict[str, ty.List[str]], ty.Dict[str, ty.Tuple[str, str]]]: r"""MAKEDOC: what is prepare_partitions doing?""" # logg = logging.getLogger(f"c.{__name__}.prepare_partitions") # logg.setLevel("INFO") # logg.debug("Start prepare_partitions") partition: ty.Dict[str, ty.List[str]] = { "training": [], "validation": [], "testing": [], } ids2labels: ty.Dict[str, ty.Tuple[str, str]] = {} # get a new rng, if you want repeatable folds pass your own if rng is None: rng = np.random.default_rng(int(timer())) # analyse all interesting classes for label in label_list: label_folder = dataset_raw_folder / label for img_raw_path in label_folder.iterdir(): img_stem = img_raw_path.stem img_name = f"{label}/{img_raw_path.name}" x = rng.random() if x < test_fold_size: which = "testing" elif x < test_fold_size + val_fold_size: which = "validation" else: which = "training" ID = img_name partition[which].append(ID) ids2labels[ID] = (label, img_stem) return partition, ids2labels
def __init__(self, ps2d: np.ndarray, f: np.ndarray, rng: np.random.Generator, fminx: float, fminy: float, fmax: float, alpha: int = 10) -> None: revert = fminy < fminx if revert: fmin, fminy = fminy, fminx else: fmin = fminx # Go alpha times further in frequency to avoid interpolation aliasing. fmaxr = alpha * fmax # Build the 2D PSD following the given 1D PSD fx = np.concatenate(([0], f)) fy = np.concatenate(([0], np.arange(fminy, fmaxr + fminy, fminy))) dfx, dfy = fmin, fminy phase = rng.random((2 * len(fy) - 1, len(fx))) * 2 * np.pi phase[0, 0] = 0. phase[-len(fy) + 1:, 0] = -phase[1:len(fy), 0][::-1] fft2a = np.concatenate((0.25 * ps2d, 0.25 * ps2d[1:, :][::-1, :]), axis=0) fft2a = np.sqrt(fft2a) * np.exp(1j * phase) / np.sqrt((dfx * dfy)) fft2 = np.zeros((2 * len(fy) - 1, 2 * len(fx) - 1), dtype=complex) fft2[:, :len(fx)] = fft2a fft2[1:, -len(fx) + 1:] = fft2a[1:, 1:].conj()[::-1, ::-1] fft2[0, -len(fx) + 1:] = fft2a[0, 1:].conj()[::-1] sg = (4 * fy[-1] * fx[-1]) * np.real(IFFT2(fft2)) xg = np.linspace(0, 1 / fmin, sg.shape[1]) yg = np.linspace(0, 1 / fminy, sg.shape[0]) self.xgmax = xg.max() self.ygmax = yg.max() self.reverse = revert self.xg = xg self.yg = yg self.sg = sg
def test_raise_on_vector_dimension_mismatch( N: int, L: np.ndarray, rng: np.random.Generator, method_kwargs: Dict[str, Any], ): """Tests whether a :class:`ValueError` is raised if the shape of the vector is not compatible with the shape of the Cholesky factor""" # Generate arbitrary v with incompatible length v_len = N + rng.integers(-N, N, endpoint=True) + 1 if v_len == N: v_len += 1 v = rng.random(v_len) with pytest.raises(ValueError): cholupdates.rank_1.downdate(L=L, v=v, **method_kwargs)
def sample_generator(rg: np.random.Generator): lat, lon = get_sample(100, rg, cd, sample_df, CHC_df, CHC_sub, CHC_sub_dict, save=False) time_windows = np.zeros((len(lat), 2)) for i in range(len(lat)): if rg.random() > 0.5: time_windows[i, 0] = 0 time_windows[i, 1] = 14400 else: time_windows[i, 0] = 14400 time_windows[i, 1] = 28800 customers = [ Customer(lat[i], lon[i], 0.8, 0.8, rg=rg) for i in range(len(lat)) ] return customers, time_windows
def random_initialization(self, rng: np.random.Generator): weight = rng.random() diff = self.max - self.min return self.type(diff * weight + self.min)
def sample_generator(rg: np.random.Generator, n): cd = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "data") sample_data = os.path.join(cd, "Toll_CHC_November_Sample_Data.csv") CHC_data = os.path.join(cd, "christchurch_street.csv") sample_df, CHC_df, CHC_sub, CHC_sub_dict = read_data( sample_data, CHC_data, lat_min=-43.6147000, lat_max=-43.4375000, lon_min=172.4768000, lon_max=172.7816000, ) # customers = [Customer(lat,lon, 0.9, 0.9, presence_list[i]) for i in range(len(lat))] lat, lon = get_sample(n, rg, cd, sample_df, CHC_df, CHC_sub, CHC_sub_dict, save=False) time = 5400 rerouting_tw = True day_start = 0 day_end = 28800 num_time_windows = 4 interval = (day_end - day_start) / num_time_windows num_presence = 8 # presence has 48 digits for an interval of 10 minutes interval_presence = (day_end - day_start) / num_presence presence_per_tw = int(num_presence / num_time_windows) # lat, lon = get_sample(5, rg, cd, sample_df, CHC_df, CHC_sub, CHC_sub_dict, save=False) presence_list = [] time_windows = np.zeros((len(lat), 2)) rd = rg.integers(low=0, high=2**num_presence - 1, size=n) for i in range(len(lat)): # get a random decimal number between 0-255 # rd = rg.integers(low=0, high=2**num_presence-1, size=1)[0] # a list of random numbers # rd = random.randint(0,2**num_presence-1) # get a list of binary number representing the presence for each presence interval presence = list(map(int, "{0:08b}".format(rd[i]))) presence_list.append(presence) # to assign to the customer later on pres = presence[:] if rerouting_tw is True: j = 0 # switch presence off if the time has passed while time > interval_presence * j: pres[j] = 0 j = j + 1 # find the time window with highest presence sum_per_tw = [ sum(pres[i:i + presence_per_tw]) for i in range(0, len(pres), presence_per_tw) ] max_ind = sum_per_tw.index(max(sum_per_tw)) # add randomness to selecting time window according to presence if rg.random() > 0.9: # if random.random() > 0.9: sum_per_tw[max_ind] = 0 max_ind = sum_per_tw.index(max(sum_per_tw)) time_windows[i, 0] = interval * max_ind time_windows[i, 1] = interval * (max_ind + 1) customers = [ Customer(lat[i], lon[i], 0.9, 0.9, presence_list[i], rg) for i in range(len(lat)) ] return time_windows, customers
def try_crossover(rng: np.random.Generator, *parents_tuple: 'CnnGenome') -> Optional['CnnGenome']: """ Attempts to perform crossover. This could fail in the event that the resulting child genome has no path from the input layer to the output layer. This won't happen if the parent genome's accept_rate is 1.0 """ parents: List['CnnGenome'] = list(parents_tuple) assert len(parents) > 1 def sort_key(genome: 'CnnGenome') -> float: return genome.fitness parents = list(sorted(parents, key=sort_key)) logging.info(f"attempting crossover with {len(parents)} parents") for i, parent in enumerate(parents): logging.info( f"parent {i} has {parent.number_enabled_edges()} enabled edges and " + \ f"{parent.number_enabled_layers()} enabled layers") layer_map: Dict[int, Layer] = {} edge_map: Dict[int, Edge] = {} epigenetic_weights: Dict[str, Any] = {} disabled_edges: Set[int] = set() disabled_layers: Set[int] = set() def try_add_layer(layer: Layer, enabled: bool = True): """ Add a copy of the supplied layer to the layer map of the new genome we are constructing, enabling or disabling it based on the value of enabled """ if layer.layer_innovation_number not in layer_map: copy = layer.copy() copy.clear_io() copy.set_enabled(enabled) layer_map[layer.layer_innovation_number] = copy if not enabled: disabled_layers.add(layer.layer_innovation_number) elif enabled and layer.layer_innovation_number in disabled_layers: layer_map[layer.layer_innovation_number].set_enabled(enabled) disabled_layers.remove(layer.layer_innovation_number) def try_add_edge(edge: Edge, enabled: bool = True): """ Add a copy of the supplied edge to the edge map, enabling or disabling it based on the value of enabled """ if edge.input_layer_in in layer_map and \ edge.output_layer_in in layer_map: if edge.edge_innovation_number not in edge_map: copy = edge.copy(layer_map) copy.set_enabled(enabled) edge_map[edge.edge_innovation_number] = copy if not enabled: disabled_edges.add(edge.edge_innovation_number) elif enabled and edge.edge_innovation_number in disabled_edges: edge_map[edge.edge_innovation_number].set_enabled(enabled) disabled_edges.remove(edge.edge_innovation_number) for i, parent in enumerate(parents): accept_rate = hp.get_crossover_accept_rate(i) for layer in parent.layer_map.values(): sample = rng.random() try_add_layer(layer, sample <= accept_rate and layer.is_enabled()) for edge in parent.edge_map.values(): sample = rng.random() try_add_edge(edge, sample <= accept_rate and layer.is_enabled()) # We will get the "best" weights here because we sorted by genome fitness - the first things added # will have the best fitness for name, weights in parent.epigenetic_weights.items(): if name not in epigenetic_weights: epigenetic_weights[name] = weights conv_edges: List[ConvEdge] = [] output_edges: List[DenseEdge] = [] for edge in edge_map.values(): ty = type(edge) if issubclass(ty, ConvEdge): conv_edges.append(cast(ConvEdge, edge)) elif ty == DenseEdge: output_edges.append(cast(DenseEdge, edge)) else: raise RuntimeError(f"unrecognized edge type '{ty}'") number_outputs: int = parents[0].number_outputs input_layer: InputLayer = cast( InputLayer, layer_map[parents[0].input_layer.layer_innovation_number]) output_layer: OutputLayer = cast( OutputLayer, layer_map[parents[0].output_layer.layer_innovation_number]) epigenetic_optimizer_weights = parents[ 0].epigenetic_optimizer_weights.copy() epigenetic_optimizer_weights.update( parents[1].epigenetic_optimizer_weights) # hpc = hp.EvolvableHPConfig(list(map(lambda p: p.hp, parents)), rng) hpc = parents[0].hp # For now use default fitness, history, and accuracy # Maybe we'll want to use the ones from the parent genome child = CnnGenome(number_outputs, input_layer, output_layer, layer_map, conv_edges, output_edges, epigenetic_weights, disabled_layers, disabled_edges, hpc) if child.path_exists(child.input_layer, child.output_layer, False): logging.info("crossover succeeded!") logging.info( f"child has {child.number_enabled_edges()} enabled edges and " + \ f"{child.number_enabled_layers()} enabled layers") return child else: logging.info( "crossover failed because there was no path from the input layer to the output layer" ) return None
def uniform_population(rng: np.random.Generator) -> np.array: # Distribute population uniformly on a unit square. return rng.random((n_individuals, 2))