Esempio n. 1
0
    def getElem(self, indices, base=0, assumptions=USE_DEFAULTS, requirements=None):
        '''
        Return the tensor element at the indices location, given
        as an Expression, using the given assumptions as needed
        to interpret the location expression.  Required
        truths, proven under the given assumptions, that 
        were used to make this interpretation will be
        appended to the given 'requirements' (if provided).
        '''
        from proveit.number import num, Less, Add, subtract
        from .iteration import Iter
        from .composite import _simplifiedCoord
        if len(indices) != self.ndims:
            raise ExprArrayError("The 'indices' has the wrong number of dimensions: %d instead of %d"%(len(indices), self.ndims))
        
        if requirements is None: requirements = [] # requirements won't be passed back in this case

        if base != 0: 
            # subtract off the base if it is not zero
            indices = [subtract(index, num(self.base)) for index in indices]
        tensor_loc = [_simplifiedCoord(index, assumptions, requirements) for index in indices]

        lower_indices = []
        upper_indices = []
        for coord, sorted_coords in zip(tensor_loc, self.sortedCoordLists):
            lower, upper = None, None
            try:
                lower, upper = Less.insert(sorted_coords, coord, assumptions=assumptions)
            except:
                raise ExprArrayError("Could not determine the 'indices' range within the tensor coordinates under the given assumptions")
            # The relationship to the lower and upper coordinate bounds are requirements for determining
            # the element being assessed.
            requirements.append(Less.sort((sorted_coords[lower], coord), reorder=False, assumptions=assumptions))
            requirements.append(Less.sort((coord, sorted_coords[upper]), reorder=False, assumptions=assumptions))
            lower_indices.append(lower)
            upper_indices.append(upper)
        
        if tuple(lower_indices) not in self.entryOrigins or tuple(upper_indices) not in self.entryOrigins:
            raise ExprArrayError("Tensor element could not be found at %s"%str(tensor_loc))
        rel_entry_origin = self.relEntryOrigins[lower_indices]
        if self.relEntryOrigins[upper_indices] != rel_entry_origin:
            raise ExprArrayError("Tensor element is ambiguous for %s under the given assumptions"%str(tensor_loc))
        
        entry = self[rel_entry_origin]
        if isinstance(entry, Iter):
            # indexing into an iteration
            entry_origin = self.tensorLoc(rel_entry_origin)
            iter_start_indices = entry.start_indices
            iter_loc = [Add(iter_start, subtract(coord, origin)) for iter_start, coord, origin in zip(iter_start_indices, tensor_loc, entry_origin)] 
            simplified_iter_loc = [_simplifiedCoord(coord, assumptions, requirements) for coord in iter_loc]
            return entry.getInstance(simplified_iter_loc, assumptions=assumptions, requirements=requirements)
        else:
            # just a single-element entry
            assert lower_indices==upper_indices, "A single-element entry should not have been determined if there was an ambiguous range for 'tensor_loc'"
            return entry
Esempio n. 2
0
 def _tensorDictFromIterables(tensor, assumptions, requirements):
     '''
     From nested lists of Expressions, create a tensor dictionary, 
     mapping multi-dimensional indices to Expression elements.
     Yields location, element pairs that define a tensor.
     '''
     from proveit._core_ import KnownTruth        
     from .composite import _simplifiedCoord
     from proveit.number import zero, one, Add, subtract
     try:
         coord = zero
         for entry in tensor:
             # simplify the coordinate before moving on
             # (the simplified form will be equated with the original in the
             # sorting relations of the ExprArray).
             coord = _simplifiedCoord(coord, assumptions, requirements)
             if isinstance(entry, KnownTruth):
                 entry = entry.expr # extract the Expression from the KnownTruth
             if isinstance(entry, Expression):
                 loc = (coord,)
                 if isinstance(entry, Iter) and entry.ndims > 1:
                     loc += (zero,)*(entry.ndims-1) # append zeros for the extra dimensions
                 yield loc, entry # yield the location and element
                 if isinstance(entry, Iter):
                     # skip the coordinate ahead over the Embed expression
                     coord = Add(coord, subtract(entry.end_indices[0], entry.start_indices[0]), one)
                 else:
                     coord = Add(coord, one) # shift the coordinate ahead by one
             else:
                 for sub_loc, entry in ExprArray.TensorDictFromIterables(entry):
                     loc = (coord,)+sub_loc
                     yield loc, entry
     except TypeError:
         raise TypeError('An ExprArray must be a dictionary of indices to elements or a nested iterables of Expressions')
Esempio n. 3
0
 def _makeEntryOrigins(self):
     '''
     entryOrigins maps relative indices that contain tensor elements to
     the relative indices of the origin for the corresponding entry.
     Specifically, single-element entries map indices to themselves, but
     multi-element Iter entries map each of the encompassed 
     relative index location to the origin relative index location where
     that Iter entry is stored.
     
     Raise an ExprArrayError if there are overlapping entries.
     '''
     from .iteration import Iter
     from proveit.number import Add, subtract, one
     
     # Create the entry_origins dictionary and check for invalid
     # overlapping entries while we are at it.
     rel_entry_origins = dict()
     rel_index_tensor = self.relIndexTensor
     for rel_entry_loc, entry in rel_index_tensor.items():
         if isinstance(entry, Iter):
             loc = self.tensorLoc(rel_entry_loc)
             
             # corner location at the end of the Embed block:
             end_corner = []
             for axis, coord in enumerate(loc):
                 end_coord = Add(coord, subtract(entry.end_indices[axis], entry.start_indices[axis]), one)
                 end_corner.append(self.endCoordSimplifications[end_coord])
             
             # translate the end corner location to the corresponding relative indices
             rel_entry_end_corner = self.relEntryLoc(end_corner)
             
             # iterate over all of the relative indexed locations from the starting corner to 
             # the ending corner of the Iter block, populating the entry_origins dictionary and 
             # making sure none of the locations overlap with something else.
             for p in itertools.product(*[range(start, end) for start, end in zip(rel_entry_loc, rel_entry_end_corner)]):
                 p = tuple(p)
                 if p in rel_entry_origins:
                     raise ExprArrayError("Overlapping blocks in the ExprArray")
                 rel_entry_origins[p] = rel_entry_loc
         else:
             # single-element entry.  check for overlap and add to the entry_origins dictionary
             if rel_entry_loc in rel_entry_origins:
                 raise ExprArrayError("Overlapping blocks in the ExprArray")
             rel_entry_origins[rel_entry_loc] = rel_entry_loc
             
     # Return the entry_origins dictionary that we generated.
     return rel_entry_origins 
Esempio n. 4
0
 def endCorner(self, tensor_entry_loc):
     '''
     Given an absolute tensor entry location,
     return the absolute location for the "end-corner" of
     the entry.  If the entry is an Iter, then this
     absolute end corner gives the range of the
     iteration inclusively.  Otherwise, the end-corner is
     simply tensor_entry_loc; that is, the start and the 
     end are the same for single-element entries.
     '''
     from proveit.number import one, Add, subtract
     from .iteration import Iter
     entry = self[self.relEntryLoc(tensor_entry_loc)]
     if isinstance(entry, Iter):
         end_corner = []
         for axis, coord in enumerate(tensor_entry_loc):
             # Add (end-start)+1 of the Iter to get to the end
             # location of the entry along this axis. 
             orig_end_coord = Add(coord, subtract(entry.end_indices[axis], entry.start_indices[axis]), one)
             end_corner.append(self.endCoordSimplifications[orig_end_coord]) # use the simplified version
         return end_corner # absolute end-corner for the tensor entry
     return tensor_entry_loc # single-element entry
Esempio n. 5
0
    def __init__(self, tensor, shape=None, styles=None, assumptions=USE_DEFAULTS, requirements=tuple()):
        '''
        Create an ExprArray either with a simple, dense tensor (list of lists ... of lists) or
        with a dictionary mapping coordinates (as tuples of expressions that represent integers) 
        to expr elements or Blocks.
        Providing starting and/or ending location(s) can extend the bounds of the tensor beyond
        the elements that are supplied.
        '''
        from .composite import _simplifiedCoord
        from proveit._core_ import KnownTruth
        from proveit.number import Less, Greater, zero, one, num, Add, subtract
        
        assumptions = defaults.checkedAssumptions(assumptions)
        requirements = []                
        if not isinstance(tensor, dict):
            tensor = {loc:element for loc, element in ExprArray._tensorDictFromIterables(tensor, assumptions, requirements)}
                
        # Map direct compositions for the end-coordinate of Iter elements
        # to their simplified forms.
        self.endCoordSimplifications = dict()
                
        # generate the set of distinct coordinates for each dimension
        coord_sets = None # simplified versions
        full_tensor = dict()
        ndims = None
        if shape is not None:
            shape = ExprArray.locAsExprs(shape)
            ndims = len(shape)
        for loc, element in tensor.items():
            if isinstance(element, KnownTruth):
                element = element.expr # extract the Expression from the KnownTruth
            ndims = len(loc)
            if coord_sets is None:
                coord_sets = [set() for _ in range(ndims)]
            elif len(coord_sets) != ndims:
                if shape is not None:
                    raise ValueError("length of 'shape' is inconsistent with number of dimensions for ExprArray locations")
                else:
                    raise ValueError("inconsistent number of dimensions for locations of the ExprArray")
            for axis, coord in enumerate(list(loc)):
                if isinstance(coord, int):
                    coord = num(coord) # convert from Python int to an Expression
                    loc[axis] = coord
                coord_sets[axis].add(coord)
                if isinstance(element, Iter):
                    # Add (end-start)+1 of the Iter to get to the end
                    # location of the entry along this axis. 
                    orig_end_coord = Add(coord, subtract(element.end_indices[axis], element.start_indices[axis]), one)
                    end_coord = _simplifiedCoord(orig_end_coord, assumptions, requirements)
                    self.endCoordSimplifications[orig_end_coord] = end_coord
                    coord_sets[axis].add(end_coord)
            full_tensor[tuple(loc)] = element

        if ndims is None:
            raise ExprArrayError("Empty ExprArray is not allowed")
        if ndims <= 1:
            raise ExprArrayError("ExprArray must be 2 or more dimensions (use an ExprTuple for something 1-dimensional")

        # in each dimension, coord_indices will be a dictionary
        # that maps each tensor location coordinate to its relative entry index.
        coord_rel_indices = []
        self.sortedCoordLists = []
        self.coordDiffRelationLists = []
        for axis in range(ndims): # for each axis
            # KnownTruth sorting relation for the simplified coordinates used along this axis
            # (something with a form like a < b <= c = d <= e, that sorts the tensor location coordinates): 
            coord_sorting_relation = Less.sort(coord_sets[axis], assumptions=assumptions)
            sorted_coords = list(coord_sorting_relation.operands)
            
            if shape is None:
                # Since nothing was explicitly specified, the shape is dictacted by extending
                # one beyond the last coordinate entry. 
                sorted_coords.append(Add(sorted_coords[-1], one))
            else:
                sorted_coords.append(shape[axis]) # append the coordinate for the explicitly specified shape
            if sorted_coords[0] != zero:
                sorted_coords.insert(0, zero) # make sure the first of the sorted coordinates is zero.
            
            self.sortedCoordLists.append(ExprTuple(sorted_coords))
            
            # Add in coordinate expressions that explicitly indicate the difference between coordinates.
            # These may be used in generating the latex form of the ExprArray.
            diff_relations = []
            for c1, c2 in zip(sorted_coords[:-1], sorted_coords[1:]):
                diff = _simplifiedCoord(subtract(c2, c1), assumptions, requirements)
                # get the relationship between the difference of successive coordinate and zero.
                diff_relation = Greater.sort([zero, diff], assumptions=assumptions)
                if isinstance(diff_relation, Greater):
                    if c2 == sorted_coords[-1] and shape is not None:
                        raise ExprArrayError("Coordinates extend beyond the specified shape in axis %d: %s after %s"%(axis, str(coord_sorting_relation.operands[-1]), str(shape[axis])))                        
                    assert tuple(diff_relation.operands) == (diff, zero), 'Inconsistent Less.sort results'
                    # diff > 0, let's compare it with one now
                    diff_relation = Greater.sort([one, diff], assumptions=assumptions)
                requirements.append(diff_relation)
                diff_relations.append(diff_relation)
            self.coordDiffRelationLists.append(ExprTuple(diff_relations))
                
            # map each coordinate expression to its index into the sorting_relation operands
            coord_rel_indices.append({coord:k for k, coord in enumerate(sorted_coords)})
            
        # convert from the full tensor with arbitrary expression coordinates to coordinates that are
        # mapped according to sorted relation enumerations.
        rel_index_tensor = dict()
        for loc, element in full_tensor.items():
            rel_index_loc = (rel_index_map[coord] for coord, rel_index_map in zip(loc, coord_rel_indices))
            rel_index_tensor[rel_index_loc] = element
                
        sorted_keys = sorted(rel_index_tensor.keys())
        Expression.__init__(self, ['ExprArray', str(ndims), ';'.join(str(key) for key in sorted_keys)], self.sortedCoordLists + self.coordDiffRelationLists + [rel_index_tensor[key] for key in sorted_keys], styles=styles, requirements=requirements)
        self.ndims = ndims
        self.relIndexTensor = rel_index_tensor
        
        # entryOrigins maps relative indices that contain tensor elements to
        # the relative indices of the origin for the corresponding entry.
        # Specifically, single-element entries map indices to themselves, but
        # multi-element Iter entries map each of the encompassed 
        # relative index location to the origin relative index location where
        # that Iter entry is stored.
        self.relEntryOrigins = self._makeEntryOrigins()
        
        # the last coordinates of the sorted coordinates along each eaxis define the shape:        
        self.shape = ExprTuple([sorted_coords[-1] for sorted_coords in self.sortedCoordLists])