Exemplo n.º 1
0
    def translate(self,F,clone=None):
        """ Return the translation by a list of operations.
        
        :param F: the list of operations to translate by
        :param clone: the clone within which the translation is taking place

        :returns: the translation
        """
        if len(F) != self.arity:
            raise ArityError(self.arity,len(F))
        k = F[0].arity
        
        if clone is None:
            clone = Clone.generate(self.ops + F,k)
        
        if k != clone[0].arity:
            raise ArityError(k,clone[0].arity)
        if clone[0].dom != self.dom:
            raise DomainError(clone[0].dom)

        row = [0 for _ in range(len(clone))]
        for (f,w) in self.weight_iter():
            try: 
                row[clone.get_index(f.compose(F))] += w
            except KeyError:
                raise KeyError
        return row
Exemplo n.º 2
0
    def translations(self,arity,clone=None):
        """ Return the set of translations by elements of a clone.

        :param clone: The clone we want to translate by.
        :type clone: :class:`Clone`, Optional
        :returns: The matrix of translations, with columns index by the
            clone. Each row corresponds to a single translation,
            storing the weight assigned to the i-th operation in the
            clone at the i-th location.
        :rtype: :class:`cdd.Matrix`

        .. note:: If no clone is passed as input, we use the smallest
            clone containing all the elements of self.ops.
        """

        if clone is None:
            clone = Clone.generate(self.ops,arity)
        N = len(clone)

        # Each tuple of terms in the clone gives rise to a generator
        A = []
        for t in it.product(range(N),repeat=self.arity):
            F = [clone[i] for i in t]
            row = [0 for _ in range(N)]
            for (f,w) in self.weight_iter():#zip(self.ops,self.weights):
                try: 
                    row[clone.get_index(f.compose(F))] += w
                except KeyError:
                    raise KeyError

            # Add non-zero rows if they are are not already in A.
            # We keep A sorted to make this check more efficient
            if min(row) != 0:
                if len(A) == 0:
                    A = [row]
                else:
                    i = binary_search(A,row)
                    if i == len(A) or A[i] != row:
                        A.insert(i,row)
        return A
Exemplo n.º 3
0
    def wclone(self,k,clone=None,log=False):
        """ Returns the weighted clone generated by this weighted
        operation.   

        :param k: The arity.
        :type k: integer
        :param clone: The supporting clone.    
        :type clone: :class:`Clone`, optional
        :returns: A list of k-ary weighted operations which added together
            to get any k-ary element of the weighted clone.
        :rtype: :py:func:`list` of :class:`WeightedOperation`
            
        .. note::
            At the moment, this is implemented rather crudely, requiring
            two calls to CDD. The first obtains a set of inequalities
            defining the cone generated by the translations. Then,
            inequalities to ensure that only projections receive positive
            weight are added and this new set of inequalities are used to
            obtain a set of generators for the cone of k-ary elements in
            the weighted clone.
        """

        if clone is None:
            clone = Clone.generate(self.ops,k)
            if log:
                print "Computed Clone"
            
        # First, get the inequalities defining the cone generated by
        # the translations
        T = self.translations(k,clone)        
        T = map(lambda row: [0] + row, T)
        
            
        trans_mat = cdd.Matrix(T)
        trans_mat.rep_type = cdd.RepType.GENERATOR
        trans_mat.canonicalize()
        if log:
            print "Computed Translations"
            for r in T:
                print r
            
        trans_poly = cdd.Polyhedron(trans_mat)
        A = trans_poly.get_inequalities()        
            
        # Next, add inequalities to ensure that non-projections cannot
        # receive negitive weight.
        # Recall that the projections will always be the first k
        # elements of the clone
        wop_ineq = [[0 for _ in range(len(clone)+1)]
                    for _ in range(k,len(clone))]
        for i in range(k,len(clone)):
            wop_ineq[i-k][i+1] = 1
        A.extend(wop_ineq)
        A.canonicalize()
        if log:
            print "Computed Inequalities"
            for a in A:
                print a

        # Finally, compute generators for the weighted clone
        wclone_mat = cdd.Matrix(A)
        wclone_poly = cdd.Polyhedron(A)
        wclone_gen = wclone_poly.get_generators()

        if log:
            print "Computed Generators"
        wclone = []
        for r in wclone_gen:
            ops = []
            weights = []
            if log:
                print r
            for i in range(1,len(r)):
                rval = round(r[i],self.dom)
                if rval != 0:
                    ops.append(clone[i-1])
                    weights.append(rval)
            wclone.append(WeightedOperation(k,self.dom,ops,weights))
        return wclone
Exemplo n.º 4
0
    def in_wclone(self,other,clone=None):
        """ Test if another weighted operation is in the weighted
        clone generated by this weighted operation. 

        :param other: The other weighted operation.
        :type other: :class:`WeightedOperation`
        :param clone: The supporting clone (this must be of the same
            arity as other) and all operations of w must be contained
            in clone.  
        :type clone: :class:`Clone`, Optional
        :returns: True if other is contained in the weighted clone,
            False otherwise. If True, we also return a certificate,
            which is a list of pairs of weights and translations. If
            False, we return a separating cost function.  
        :rtype: (boolean,:py:func:`list`)

        .. note: If no clone is passed as input, we use the method
            Clone.generate to obtain the clone.
        """
        
        if clone is None:
            # Use the clone generated by the operations in self.ops
            clone = Clone.generate(self.ops,other.arity)

        # All operations in other must be contained in the clone
        for f in other.ops:
            if not f in clone:
                return False
            
        N = len(clone)
        A = self.translations(other.arity,clone)

        prob = pulp.LpProblem()

        # A variable for each non-redundant translation
        y = pulp.LpVariable.dicts("y",range(len(A)),0)

        # No objective function
        prob += 0

        for j in range(N):
            #k = other.ops.index(clone[j])
            prob += (sum([A[i][j]*y[i] for i in range(len(A))])
                     == other.get_weight(clone[j]))            

        prob.solve()

        if pulp.LpStatus[prob.status] == 'Optimal':
            cert = []
            for i in range(len(A)):
                val = round(pulp.value(y[i]),self.dom)
                if val != 0:
                    cert.append((val,
                                 [(A[i][j],str(clone[j])) for j in range(N)
                                  if A[i][j] != 0]))
            return (True,cert)
        # If no solution, then solve the dual
        # Need to figure out better method than this. Should be able
        # to use values of primal variables on termination.
        else:
            prob = pulp.LpProblem()

            # A variable for each non-redundant translation
            z = pulp.LpVariable.dicts("z",range(N),0)

            # No objective function
            prob += 0

            for i in range(len(A)):
                prob += (pulp.lpSum([A[i][j]*z[j] for j in range(N)]) <= 0, "")
            prob += pulp.lpSum([w*z[clone.get_index(f)]
                           for (f,w) in other.weight_iter()]) >= 1,""
            prob.solve()
            costs = dict()
            for i in range(N):
                costs[clone[i].value_tuple()] = round(pulp.value(z[i]),
                                                      self.dom)                
            return (False,CostFunction(len(costs.keys()[0]),self.dom,costs))
Exemplo n.º 5
0
 def test_generate(self):
     clone = Clone.generate([self.f], 2)
     self.assertEqual(clone, self.clone)