Beispiel #1
0
    def agents_neighbour_flat_positions(self):
        """
        For any agent we find 3x3x3 array of their neighbour cells indexes.
        Flatten position in env of an agent will be at the center of this array.
        Result array will be 4 dimensional.
        """
        agents_positions = cp.asarray(self._agents_positions)

        neighbour_positions = cp.expand_dims(agents_positions,
                                             0).repeat(27, axis=0)
        neighbour_positions = cp.moveaxis(neighbour_positions, (0, 1, 2),
                                          (2, 0, 1))
        neighbour_positions = neighbour_positions.reshape(
            (-1, 3, 3, 3, 3)).swapaxes(1, 4)
        neighbour_positions = neighbour_positions + NEIGHBOUR_POSITIONS_DELTA
        neighbour_positions = neighbour_positions.reshape(-1, 3).T
        neighbour_flattened_poses = cp.ravel_multi_index(
            neighbour_positions, self.env.shape)
        return neighbour_flattened_poses.reshape((-1, 3, 3, 3))
Beispiel #2
0
def histogramdd(sample, bins=10, range=None, weights=None, density=False):
    """Compute the multidimensional histogram of some data.

    Args:
        sample (cupy.ndarray): The data to be histogrammed. (N, D) or (D, N)
            array

            Note the unusual interpretation of sample when an array_like:

            * When an array, each row is a coordinate in a D-dimensional
              space - such as ``histogramdd(cupy.array([p1, p2, p3]))``.
            * When an array_like, each element is the list of values for single
              coordinate - such as ``histogramdd((X, Y, Z))``.

            The first form should be preferred.
        bins (int or tuple of int or cupy.ndarray): The bin specification:

            * A sequence of arrays describing the monotonically increasing bin
              edges along each dimension.
            * The number of bins for each dimension (nx, ny, ... =bins)
            * The number of bins for all dimensions (nx=ny=...=bins).
        range (sequence, optional): A sequence of length D, each an optional
            (lower, upper) tuple giving the outer bin edges to be used if the
            edges are not given explicitly in `bins`. An entry of None in the
            sequence results in the minimum and maximum values being used for
            the corresponding dimension. The default, None, is equivalent to
            passing a tuple of D None values.
        weights (cupy.ndarray): An array of values `w_i` weighing each sample
            `(x_i, y_i, z_i, ...)`. The values of the returned histogram are
            equal to the sum of the weights belonging to the samples falling
            into each bin.
        density (bool, optional): If False, the default, returns the number of
            samples in each bin. If True, returns the probability *density*
            function at the bin, ``bin_count / sample_count / bin_volume``.

    Returns:
        H (cupy.ndarray): The multidimensional histogram of sample x. See
            normed and weights for the different possible semantics.
        edges (list of cupy.ndarray): A list of D arrays describing the bin
            edges for each dimension.

    .. warning::

        This function may synchronize the device.

    .. seealso:: :func:`numpy.histogramdd`
    """
    if isinstance(sample, cupy.ndarray):
        # Sample is an ND-array.
        if sample.ndim == 1:
            sample = sample[:, cupy.newaxis]
        nsamples, ndim = sample.shape
    else:
        sample = cupy.stack(sample, axis=-1)
        nsamples, ndim = sample.shape

    nbin = numpy.empty(ndim, int)
    edges = ndim * [None]
    dedges = ndim * [None]
    if weights is not None:
        weights = cupy.asarray(weights)

    try:
        nbins = len(bins)
        if nbins != ndim:
            raise ValueError(
                'The dimension of bins must be equal to the dimension of the '
                ' sample x.')
    except TypeError:
        # bins is an integer
        bins = ndim * [bins]

    # normalize the range argument
    if range is None:
        range = (None, ) * ndim
    elif len(range) != ndim:
        raise ValueError('range argument must have one entry per dimension')

    # Create edge arrays
    for i in _range(ndim):
        if cupy.ndim(bins[i]) == 0:
            if bins[i] < 1:
                raise ValueError(
                    '`bins[{}]` must be positive, when an integer'.format(i))
            smin, smax = _get_outer_edges(sample[:, i], range[i])
            num = int(bins[i] + 1)  # synchronize!
            edges[i] = cupy.linspace(smin, smax, num)
        elif cupy.ndim(bins[i]) == 1:
            if not isinstance(bins[i], cupy.ndarray):
                raise ValueError('array-like bins not supported')
            edges[i] = bins[i]
            if (edges[i][:-1] > edges[i][1:]).any():  # synchronize!
                raise ValueError(
                    '`bins[{}]` must be monotonically increasing, when an '
                    'array'.format(i))
        else:
            raise ValueError(
                '`bins[{}]` must be a scalar or 1d array'.format(i))

        nbin[i] = len(edges[i]) + 1  # includes an outlier on each end
        dedges[i] = cupy.diff(edges[i])

    # Compute the bin number each sample falls into.
    ncount = tuple(
        # avoid cupy.digitize to work around NumPy issue gh-11022
        cupy.searchsorted(edges[i], sample[:, i], side='right')
        for i in _range(ndim))

    # Using digitize, values that fall on an edge are put in the right bin.
    # For the rightmost bin, we want values equal to the right edge to be
    # counted in the last bin, and not as an outlier.
    for i in _range(ndim):
        # Find which points are on the rightmost edge.
        on_edge = sample[:, i] == edges[i][-1]
        # Shift these points one bin to the left.
        ncount[i][on_edge] -= 1

    # Compute the sample indices in the flattened histogram matrix.
    # This raises an error if the array is too large.
    xy = cupy.ravel_multi_index(ncount, nbin)

    # Compute the number of repetitions in xy and assign it to the
    # flattened histmat.
    hist = cupy.bincount(xy, weights, minlength=numpy.prod(nbin))

    # Shape into a proper matrix
    hist = hist.reshape(nbin)

    # This preserves the (bad) behavior observed in NumPy gh-7845, for now.
    hist = hist.astype(float)  # Note: NumPy uses casting='safe' here too

    # Remove outliers (indices 0 and -1 for each dimension).
    core = ndim * (slice(1, -1), )
    hist = hist[core]

    if density:
        # calculate the probability density function
        s = hist.sum()
        for i in _range(ndim):
            shape = [1] * ndim
            shape[i] = nbin[i] - 2
            hist = hist / dedges[i].reshape(shape)
        hist /= s

    if any(hist.shape != numpy.asarray(nbin) - 2):
        raise RuntimeError('Internal Shape Error')
    return hist, edges
Beispiel #3
0
    def born(self, number_of_agents, ratio_random_birth):
        """
        Method, which born new agents.
        1.  All new agents should be borned in free cells.
        2.  {BORN_IN_BOTTOM} agents are born at the bottom of ENV.
            There can be hypothetical situations, when we already have died agents in bottom cells.
            To avoid this problem, we filter this cells.
        3.  Generate an indexes of borned agents in envs.
            !!!!!WARNING!!!!!: Perhaps situation, when we have less available cells than number of new agents
            (ex.: if we have 10x10x100 env and want to create 200 agents at bottom).
            We can simple handle it, setting number of new agents like min(free_cells, number_of_agents).
        4.  The remaining agents should not appear into already occupied positions.
            {agent_available_env_bottom} is just a view, really we change {agent_available_env} array.
        5. Receive X,Y,Z positions of borned agents. Specify Z manually, because we now, that it is a bottom.
        6.  Other agents should be borned randomly in the whole envs.
            It is too slow to sample from whole {is_available_env}. So, use simple hack - because envs are
            much bigger, than number of agents - let's generate some random indexes there and just select free.
            Todo: Strictly, it can give us problems in some cases, when there will be too many agents,
            but don't worry about it now.

        7. All agents, which were born on the top will die immediately.
        8. Combine all new agents with others.

        :param number_of_agents: number of agents born each turn
        :param ratio_random_birth: ratio of agent birth locations (i.e. base of environment vs. random)
        """
        # (1)
        # (2)
        born_in_bottom = int(number_of_agents * (1 - ratio_random_birth))
        agent_available_env_bottom = self.is_available_env[:, :, -2]
        available_flat_bottom_positions = cp.flatnonzero(
            agent_available_env_bottom == True)
        # (3)
        selected_flat_bottom_positions = cp.random.choice(
            available_flat_bottom_positions, born_in_bottom, replace=False)
        # (4)
        self.is_available_env[:, :, -2].ravel(
        )[selected_flat_bottom_positions] = False
        # (5)
        bottom_agents_positions = cp.unravel_index(
            selected_flat_bottom_positions,
            (*agent_available_env_bottom.shape, 1))
        bottom_agents_positions = cp.vstack(bottom_agents_positions)
        bottom_agents_positions[2] = (self.is_available_env.shape[2] - 2)

        # (6)
        born_in_random = number_of_agents - born_in_bottom
        random_positions = cp.array([
            # Use numpy function, because it is faster.
            np.random.randint(1, ax_shape - 1, born_in_random * 4)
            for ax_shape in self.is_available_env.shape
        ])
        random_flat_positions = cp.ravel_multi_index(
            random_positions, self.is_available_env.shape)

        is_available = self.is_available_env.ravel()[random_flat_positions]

        selected_flat_uniform_positions = random_flat_positions[
            is_available][:born_in_random]
        uniform_agents_positions = cp.unravel_index(
            selected_flat_uniform_positions, self.is_available_env.shape)
        uniform_agents_positions = cp.vstack(uniform_agents_positions)
        # Todo: This code is correct, but too slow. Replace it with code above.

        # available_flat_uniform_positions = cp.flatnonzero(self.is_available_env)
        # selected_flat_uniform_positions = cp.random.choice(
        #     available_flat_uniform_positions,
        #     number_of_agents - born_in_bottom,
        #     replace=False
        # )
        # uniform_agents_positions = cp.unravel_index(selected_flat_uniform_positions, self.is_available_env.shape)
        # uniform_agents_positions = cp.vstack(uniform_agents_positions)

        # (7)
        new_agent_positions = cp.hstack(
            [uniform_agents_positions, bottom_agents_positions]).T
        new_agent_state = (new_agent_positions[:, 2] != 1).astype(cp.bool)

        # (8)
        if self._agents_positions is None:
            self._agents_positions = new_agent_positions
            self.agents_state = new_agent_state
        else:
            self._agents_positions = cp.vstack(
                [self._agents_positions, new_agent_positions])
            self.agents_state = cp.hstack([self.agents_state, new_agent_state])

        self.is_available_env.ravel()[self.agents_flat_positions] = False
Beispiel #4
0
 def agents_flat_positions(self):
     """Return flat indexes in env for all agents."""
     if self._agents_positions is None:
         return []
     return cp.ravel_multi_index(self._agents_positions.T, self.env.shape)