Beispiel #1
0
    def __init__(self, arguments, import_manager):
        """
        Initialize the reconstruction planning problem.

        The number of measurements that we intend to make is a fixed parameter
        and cannot be optimized by the current algorithm in one run.

        The network size is defined in the same way as in the weight matrix
        of the reconstruction problem itself.
        """

        if not isinstance(arguments, Arguments):
            raise ValueError("'arguments' must be an instance of Arguments")

        # Import the settings for the planning problem.
        self.arguments = arguments
        self._import_manager = import_manager

        # Always use the ellipse model for the reconstruction weight model.
        # This is a fast weight model, and it does not have a lot of modeling
        # of attenuation and log path loss weighting that would easily make all
        # pixels be intersected by lines at low weight.
        reconstruction_settings = self.arguments.get_settings("reconstruction")
        reconstruction_settings.set("model_class", "Ellipse_Model")

        self.settings = self.arguments.get_settings("planning_problem")
        self.N = self.settings.get("number_of_measurements")
        self.network_size = self.settings.get("network_size")
        self.padding = self.settings.get("network_padding")

        num_variables, domain = self.get_domain()
        super(Reconstruction_Plan, self).__init__(num_variables, domain)

        # The actual network sizes excluding the padding.
        self.network_width = self.network_size[0] - self.padding[0] * 2
        self.network_height = self.network_size[1] - self.padding[1] * 2
        self.size = [self.network_width, self.network_height]

        # Initial weight matrix object which can be filled with current
        # locations during evaluations and reset to be reused.
        self.weight_matrix = self.get_weight_matrix()

        # Resulting output from the weight matrix.
        self.matrix = None
        self.unsnappable = 0
        self.distances = None

        # The maximum number of unsnappable points in an individual.
        self.unsnappable_max = self.N * self.settings.get("unsnappable_rate")

        # A grid-based Geometry object that the Problem instance can use to
        # make lines and points, if necessary.
        self.geometry = Geometry_Grid()

        self.assigner = Greedy_Assignment(self.arguments, self.geometry,
                                          self._import_manager)
        self.delta_rate = self.settings.get("delta_rate")

        self.travel_distance = 0.0
        self.sensor_distances = np.empty(0)
    def __init__(self, arguments, import_manager):
        """
        Initialize the reconstruction planning problem.

        The number of measurements that we intend to make is a fixed parameter
        and cannot be optimized by the current algorithm in one run.

        The network size is defined in the same way as in the weight matrix
        of the reconstruction problem itself.
        """

        if not isinstance(arguments, Arguments):
            raise ValueError("'arguments' must be an instance of Arguments")

        # Import the settings for the planning problem.
        self.arguments = arguments
        self._import_manager = import_manager

        # Always use the ellipse model for the reconstruction weight model. 
        # This is a fast weight model, and it does not have a lot of modeling 
        # of attenuation and log path loss weighting that would easily make all 
        # pixels be intersected by lines at low weight.
        reconstruction_settings = self.arguments.get_settings("reconstruction")
        reconstruction_settings.set("model_class", "Ellipse_Model")

        self.settings = self.arguments.get_settings("planning_problem")
        self.N = self.settings.get("number_of_measurements")
        self.network_size = self.settings.get("network_size")
        self.padding = self.settings.get("network_padding")

        num_variables, domain = self.get_domain()
        super(Reconstruction_Plan, self).__init__(num_variables, domain)

        # The actual network sizes excluding the padding.
        self.network_width = self.network_size[0] - self.padding[0]*2
        self.network_height = self.network_size[1] - self.padding[1]*2
        self.size = [self.network_width, self.network_height]

        # Initial weight matrix object which can be filled with current 
        # locations during evaluations and reset to be reused.
        self.weight_matrix = self.get_weight_matrix()

        # Resulting output from the weight matrix.
        self.matrix = None
        self.unsnappable = 0
        self.distances = None

        # The maximum number of unsnappable points in an individual.
        self.unsnappable_max = self.N * self.settings.get("unsnappable_rate")

        # A grid-based Geometry object that the Problem instance can use to 
        # make lines and points, if necessary.
        self.geometry = Geometry_Grid()

        self.assigner = Greedy_Assignment(self.arguments, self.geometry,
                                          self._import_manager)
        self.delta_rate = self.settings.get("delta_rate")

        self.travel_distance = 0.0
        self.sensor_distances = np.empty(0)
class Reconstruction_Plan(Problem):
    def __init__(self, arguments, import_manager):
        """
        Initialize the reconstruction planning problem.

        The number of measurements that we intend to make is a fixed parameter
        and cannot be optimized by the current algorithm in one run.

        The network size is defined in the same way as in the weight matrix
        of the reconstruction problem itself.
        """

        if not isinstance(arguments, Arguments):
            raise ValueError("'arguments' must be an instance of Arguments")

        # Import the settings for the planning problem.
        self.arguments = arguments
        self._import_manager = import_manager

        # Always use the ellipse model for the reconstruction weight model. 
        # This is a fast weight model, and it does not have a lot of modeling 
        # of attenuation and log path loss weighting that would easily make all 
        # pixels be intersected by lines at low weight.
        reconstruction_settings = self.arguments.get_settings("reconstruction")
        reconstruction_settings.set("model_class", "Ellipse_Model")

        self.settings = self.arguments.get_settings("planning_problem")
        self.N = self.settings.get("number_of_measurements")
        self.network_size = self.settings.get("network_size")
        self.padding = self.settings.get("network_padding")

        num_variables, domain = self.get_domain()
        super(Reconstruction_Plan, self).__init__(num_variables, domain)

        # The actual network sizes excluding the padding.
        self.network_width = self.network_size[0] - self.padding[0]*2
        self.network_height = self.network_size[1] - self.padding[1]*2
        self.size = [self.network_width, self.network_height]

        # Initial weight matrix object which can be filled with current 
        # locations during evaluations and reset to be reused.
        self.weight_matrix = self.get_weight_matrix()

        # Resulting output from the weight matrix.
        self.matrix = None
        self.unsnappable = 0
        self.distances = None

        # The maximum number of unsnappable points in an individual.
        self.unsnappable_max = self.N * self.settings.get("unsnappable_rate")

        # A grid-based Geometry object that the Problem instance can use to 
        # make lines and points, if necessary.
        self.geometry = Geometry_Grid()

        self.assigner = Greedy_Assignment(self.arguments, self.geometry,
                                          self._import_manager)
        self.delta_rate = self.settings.get("delta_rate")

        self.travel_distance = 0.0
        self.sensor_distances = np.empty(0)

    def get_domain(self):
        """
        Determine the domain of each variable and the number of variables that
        the problem instance requires.
        """

        raise NotImplementedError("Subclass must implement `get_domain`")

    def get_weight_matrix(self):
        """
        Create a clean weight matrix for the problem's parameters.
        """

        return Weight_Matrix(self.arguments, self.padding, self.size,
                             snap_inside=True, number_of_links=self.N)

    def format_steps(self, steps):
        # Convert a list of step sizes that has the same number of elements as 
        # there are variables for a single measurement so that they apply to 
        # each variable of each measurement.
        if len(steps) == self.dim/self.N:
            repeated_steps = itertools.chain(*[[s] * self.N for s in steps])
            return np.array(list(repeated_steps))

        return super(Reconstruction_Plan, self).format_steps(steps)

    def generate_positions(self, point, index):
        """
        Generate a pair of positions from an individual `point` using the
        variable value at `index` and any dependent variable based on the index.

        The result is a list containing the two positions, which themselves are
        2-length lists with the position coordinates.
        """

        raise NotImplementedError("Subclass must implement `generate_positions(point, index)`")

    def get_positions(self, point, weight_matrix):
        """
        Generate pairs of positions from an individual `point`, which is a list
        of variable values.

        The positions are checked against the given `weight_matrix` for
        snappability and other constraints. The weight matrix is therefore
        updated in this method, and it is not safe to use the same weight matrix
        on multiple individuals without resetting it in between.

        The result is a numpy array of selected (potentially snapped) positions
        and the number of unsnappable positions. The numpy array has three
        dimensions, the first grouping the position pairs, the second splitting
        those pairs into one position, and finally the two coordinates of the
        positions.
        """

        unsnappable = 0

        # Generate positions, check snappability and create weight matrix
        positions = []

        point = self.format_point(point)
        for i in range(self.N):
            sensor_points = self.generate_positions(point, i)
            snapped_points = self.select_positions(sensor_points, weight_matrix)
            if snapped_points is None:
                unsnappable += 1
            else:
                positions.append(snapped_points)

        return np.array(positions), unsnappable

    def select_positions(self, sensor_points, weight_matrix):
        """
        Select the positions for the given `sensor_points` using the weight
        matrix passed into `weight_matrix`.

        This method updates the weight matrix with the given points, and
        returns the final positions for the sensors which may be changed by the
        snap to boundary algorithm of the weight matrix.
        The method returns `None` when the positions could not be snapped.
        """

        snapped_points = weight_matrix.update(*sensor_points)
        return snapped_points

    def evaluate_point(self, point, feasible=None):
        self.weight_matrix.reset()
        positions, unsnappable = self.get_positions(point, self.weight_matrix)

        # Set up variables used by the constraint and objective functions.
        if positions.size > 0:
            # Generate distances between all the pairs of sensor positions.
            pair_diffs = positions[:, 0, :] - positions[:, 1, :]
            self.sensor_distances = np.linalg.norm(pair_diffs, axis=1)
        else:
            self.sensor_distances = np.empty(0)

        self.unsnappable = unsnappable

        # Check whether the point is feasible before performing more 
        # calculations that are only used for objective functions.
        if feasible is None:
            point = self.format_point(point)
            feasible = self.is_feasible(point)

        if feasible:
            self.matrix = self.weight_matrix.output()

            # If the sensor distances to waypoint distances ratio is 1, then 
            # there is no need to calculate the waypoint distance.
            if self.delta_rate < 1.0:
                distance = self.assigner.assign(positions)[1]
                self.travel_distance = float(distance)
                if self.travel_distance == np.inf:
                    feasible = False
            else:
                self.travel_distance = 0.0

        return super(Reconstruction_Plan, self).evaluate_point(point, feasible)

    def get_objectives(self):
        return [
            # Matrix should have many columns (pixels) that have multiple links 
            # (measurements) intersecting that pixel.
            lambda x: -np.sum(np.sum(self.matrix > 0, axis=0)),
            # The distances of the links should be minimized, since a longer 
            # link is weaker and thus contributes less clearly to a solution of 
            # the reconstruction.
            lambda x: self.delta_rate * self.sensor_distances.sum() + \
                      (1 - self.delta_rate) * self.travel_distance
            # Matrix should have values that are similar to each other in the 
            # columns, so that pixels are evenly measured by links
            #lambda x: np.var(self.matrix, axis=0).mean()
        ]

    def get_objective_names(self):
        return ["intersections", "distances"]

    def get_constraints(self):
        constraints = super(Reconstruction_Plan, self).get_constraints()
        constraints.extend([
            # Matrix must not have too many columns that have only zeroes, 
            # since then a pixel in the image is not intersected by any line. 
            # This is mostly a baseline to push the evolutionary algorithm in 
            # the right direction, since we also have an objective to make them 
            # intersect more often.
            lambda x: self.weight_matrix.check(),
            # Variables should not be in such a way that a pair of positions do 
            # not intersect with the network. At least it should not happen too 
            # often, otherwise the mission is useless. It can be useful to 
            # allow a number of them, since then we can have missions that 
            # solve the problem with fewer measurements than the fixed 
            # parameter.
            lambda x: self.unsnappable < self.unsnappable_max
        ])
        return constraints
Beispiel #4
0
class Reconstruction_Plan(Problem):
    def __init__(self, arguments, import_manager):
        """
        Initialize the reconstruction planning problem.

        The number of measurements that we intend to make is a fixed parameter
        and cannot be optimized by the current algorithm in one run.

        The network size is defined in the same way as in the weight matrix
        of the reconstruction problem itself.
        """

        if not isinstance(arguments, Arguments):
            raise ValueError("'arguments' must be an instance of Arguments")

        # Import the settings for the planning problem.
        self.arguments = arguments
        self._import_manager = import_manager

        # Always use the ellipse model for the reconstruction weight model.
        # This is a fast weight model, and it does not have a lot of modeling
        # of attenuation and log path loss weighting that would easily make all
        # pixels be intersected by lines at low weight.
        reconstruction_settings = self.arguments.get_settings("reconstruction")
        reconstruction_settings.set("model_class", "Ellipse_Model")

        self.settings = self.arguments.get_settings("planning_problem")
        self.N = self.settings.get("number_of_measurements")
        self.network_size = self.settings.get("network_size")
        self.padding = self.settings.get("network_padding")

        num_variables, domain = self.get_domain()
        super(Reconstruction_Plan, self).__init__(num_variables, domain)

        # The actual network sizes excluding the padding.
        self.network_width = self.network_size[0] - self.padding[0] * 2
        self.network_height = self.network_size[1] - self.padding[1] * 2
        self.size = [self.network_width, self.network_height]

        # Initial weight matrix object which can be filled with current
        # locations during evaluations and reset to be reused.
        self.weight_matrix = self.get_weight_matrix()

        # Resulting output from the weight matrix.
        self.matrix = None
        self.unsnappable = 0
        self.distances = None

        # The maximum number of unsnappable points in an individual.
        self.unsnappable_max = self.N * self.settings.get("unsnappable_rate")

        # A grid-based Geometry object that the Problem instance can use to
        # make lines and points, if necessary.
        self.geometry = Geometry_Grid()

        self.assigner = Greedy_Assignment(self.arguments, self.geometry,
                                          self._import_manager)
        self.delta_rate = self.settings.get("delta_rate")

        self.travel_distance = 0.0
        self.sensor_distances = np.empty(0)

    def get_domain(self):
        """
        Determine the domain of each variable and the number of variables that
        the problem instance requires.
        """

        raise NotImplementedError("Subclass must implement `get_domain`")

    def get_weight_matrix(self):
        """
        Create a clean weight matrix for the problem's parameters.
        """

        return Weight_Matrix(self.arguments,
                             self.padding,
                             self.size,
                             snap_inside=True,
                             number_of_links=self.N)

    def format_steps(self, steps):
        # Convert a list of step sizes that has the same number of elements as
        # there are variables for a single measurement so that they apply to
        # each variable of each measurement.
        if len(steps) == self.dim / self.N:
            repeated_steps = itertools.chain(*[[s] * self.N for s in steps])
            return np.array(list(repeated_steps))

        return super(Reconstruction_Plan, self).format_steps(steps)

    def generate_positions(self, point, index):
        """
        Generate a pair of positions from an individual `point` using the
        variable value at `index` and any dependent variable based on the index.

        The result is a list containing the two positions, which themselves are
        2-length lists with the position coordinates.
        """

        raise NotImplementedError(
            "Subclass must implement `generate_positions(point, index)`")

    def get_positions(self, point, weight_matrix):
        """
        Generate pairs of positions from an individual `point`, which is a list
        of variable values.

        The positions are checked against the given `weight_matrix` for
        snappability and other constraints. The weight matrix is therefore
        updated in this method, and it is not safe to use the same weight matrix
        on multiple individuals without resetting it in between.

        The result is a numpy array of selected (potentially snapped) positions
        and the number of unsnappable positions. The numpy array has three
        dimensions, the first grouping the position pairs, the second splitting
        those pairs into one position, and finally the two coordinates of the
        positions.
        """

        unsnappable = 0

        # Generate positions, check snappability and create weight matrix
        positions = []

        point = self.format_point(point)
        for i in range(self.N):
            sensor_points = self.generate_positions(point, i)
            snapped_points = self.select_positions(sensor_points,
                                                   weight_matrix)
            if snapped_points is None:
                unsnappable += 1
            else:
                positions.append(snapped_points)

        return np.array(positions), unsnappable

    def select_positions(self, sensor_points, weight_matrix):
        """
        Select the positions for the given `sensor_points` using the weight
        matrix passed into `weight_matrix`.

        This method updates the weight matrix with the given points, and
        returns the final positions for the sensors which may be changed by the
        snap to boundary algorithm of the weight matrix.
        The method returns `None` when the positions could not be snapped.
        """

        snapped_points = weight_matrix.update(*sensor_points)
        return snapped_points

    def evaluate_point(self, point, feasible=None):
        self.weight_matrix.reset()
        positions, unsnappable = self.get_positions(point, self.weight_matrix)

        # Set up variables used by the constraint and objective functions.
        if positions.size > 0:
            # Generate distances between all the pairs of sensor positions.
            pair_diffs = positions[:, 0, :] - positions[:, 1, :]
            self.sensor_distances = np.linalg.norm(pair_diffs, axis=1)
        else:
            self.sensor_distances = np.empty(0)

        self.unsnappable = unsnappable

        # Check whether the point is feasible before performing more
        # calculations that are only used for objective functions.
        if feasible is None:
            point = self.format_point(point)
            feasible = self.is_feasible(point)

        if feasible:
            self.matrix = self.weight_matrix.output()

            # If the sensor distances to waypoint distances ratio is 1, then
            # there is no need to calculate the waypoint distance.
            if self.delta_rate < 1.0:
                distance = self.assigner.assign(positions)[1]
                self.travel_distance = float(distance)
                if self.travel_distance == np.inf:
                    feasible = False
            else:
                self.travel_distance = 0.0

        return super(Reconstruction_Plan, self).evaluate_point(point, feasible)

    def get_objectives(self):
        return [
            # Matrix should have many columns (pixels) that have multiple links
            # (measurements) intersecting that pixel.
            lambda x: -np.sum(np.sum(self.matrix > 0, axis=0)),
            # The distances of the links should be minimized, since a longer
            # link is weaker and thus contributes less clearly to a solution of
            # the reconstruction.
            lambda x: self.delta_rate * self.sensor_distances.sum() + \
                      (1 - self.delta_rate) * self.travel_distance
            # Matrix should have values that are similar to each other in the
            # columns, so that pixels are evenly measured by links
            #lambda x: np.var(self.matrix, axis=0).mean()
        ]

    def get_objective_names(self):
        return ["intersections", "distances"]

    def get_constraints(self):
        constraints = super(Reconstruction_Plan, self).get_constraints()
        constraints.extend([
            # Matrix must not have too many columns that have only zeroes,
            # since then a pixel in the image is not intersected by any line.
            # This is mostly a baseline to push the evolutionary algorithm in
            # the right direction, since we also have an objective to make them
            # intersect more often.
            lambda x: self.weight_matrix.check(),
            # Variables should not be in such a way that a pair of positions do
            # not intersect with the network. At least it should not happen too
            # often, otherwise the mission is useless. It can be useful to
            # allow a number of them, since then we can have missions that
            # solve the problem with fewer measurements than the fixed
            # parameter.
            lambda x: self.unsnappable < self.unsnappable_max
        ])
        return constraints