Source code for PEPit.block_partition

from PEPit.point import Point, null_point
from PEPit.constraint import Constraint


[docs] class BlockPartition(object): """ A :class:`BlockPartition` encodes an abstract block partitioning (of :math:`d` blocks of variables) of the ambient space. Attributes: blocks_dict (dict): dictionary describing the decomposition of :class:`Point` objects. Keys are :class:`Point` objects. Values are lists of :class:`Point` objects corresponding to the decompositions of the key in blocks. list_of_constraints (list): The list of :class:`Constraint` objects associated with this :class:`BlockPartition`. d (int): encodes the number of blocks (:math:`d \\geq 1`). counter (int): counts the number of :class:`BlockPartition` objects. Example: >>> from PEPit import PEP >>> pep = PEP() >>> block_partition = pep.declare_block_partition(d=5) """ # Class counter. # It counts the number of partitions defined from scratch. counter = 0 list_of_partitions = list() def __init__(self, d): """ :class:`BlockPartition` objects can also be instantiated via the following arguments Args: d (int): encodes the number of blocks (:math:`d \\geq 1`). Instantiating the :class:`BlockPartition` object of the first example can be done by Raises: AssertionError: if provided :math:`d` is not a positive integer. Example: >>> block_partition = BlockPartition(d=5) """ # Verify that d is not 0 assert isinstance(d, int) and d >= 1 # Store attributes self.d = d self.list_of_constraints = list() self.blocks_dict = dict() self.counter = BlockPartition.counter # Update class counter and list_of_partitions BlockPartition.counter += 1 BlockPartition.list_of_partitions.append(self)
[docs] def get_nb_blocks(self): """ Return the number of blocks of this partition. """ return self.d
[docs] def get_block(self, point, block_number): """ Decompose a :class:`Point` as a sum of into :math:`d` orthogonal :class:`Point` according to the partitioning. Args: point (Point): any :class:`Point` object. block_number (int): an integer between 0 and the number of blocks (corresponding to the block index, between 0 and d-1) Returns: point (Point): a :class:`Point` corresponding to the projection of `point` onto the block `block_number` of the partitioning. Raises: AssertionError: if provided `point` is not a :class:`Point` or `block_number` is not a valid integer (which should be nonnegative and strictly smaller than the number of blocks) """ assert isinstance(point, Point) and isinstance(block_number, int) assert 0 <= block_number <= self.d - 1 # Case 1: point is already in the list: do nothing (just return) # Case 2: point is not partitioned yet: partition (and return) if point not in self.blocks_dict.keys(): point_partition = list() accumulation = null_point # Fill the partition with d-1 new :class:`Point`. for i in range(self.d-1): new_point = Point() accumulation += new_point point_partition.append(new_point) # The last element is set so that the sum is equal to point. point_partition.append(point - accumulation) self.blocks_dict[point] = point_partition # Return the desired block projection of point return self.blocks_dict[point][block_number]
[docs] def add_constraint(self, constraint): """ Store a new :class:`Constraint` to the list of constraints of this :class:`BlockPartition`. Args: constraint (Constraint): typically resulting from a comparison of 2 :class:`Expression` objects. Raises: AssertionError: if provided `constraint` is not a :class:`Constraint` object. """ # Verify constraint is an actual Constraint object assert isinstance(constraint, Constraint) # Add constraint to the list of self's constraints self.list_of_constraints.append(constraint)
[docs] def add_partition_constraints(self): """ Formulate the list of orthogonality constraints induced by the partitioning. """ for xi_decomposed in self.blocks_dict.values(): for xj_decomposed in self.blocks_dict.values(): for k in range(self.d): for l in range(k): self.add_constraint(xi_decomposed[k] * xj_decomposed[l] == 0)