def _arc(p,q,s,**kwds):
    #rewrite this to use polar_plot and get points to do filled triangles
    from sage.misc.functional import det
    from sage.plot.line import line
    from sage.misc.functional import norm
    from sage.symbolic.all import pi
    from sage.plot.arc import arc

    p,q,s = map( lambda x: vector(x), [p,q,s])

    # to avoid running into division by 0 we set to be colinear vectors that are
    # almost colinear
    if abs(det(matrix([p-s,q-s])))<0.01:
        return line((p,q),**kwds)

    (cx,cy)=var('cx','cy')
    equations=[
            2*cx*(s[0]-p[0])+2*cy*(s[1]-p[1]) == s[0]**2+s[1]**2-p[0]**2-p[1]**2,
            2*cx*(s[0]-q[0])+2*cy*(s[1]-q[1]) == s[0]**2+s[1]**2-q[0]**2-q[1]**2
            ]
    c = vector( [solve( equations, (cx,cy), solution_dict=True )[0][i] for i in [cx,cy]] )
    
    r = norm(p-c)

    a_p, a_q, a_s = map(lambda x: atan2(x[1],x[0]), [p-c,q-c,s-c])
    a_p, a_q = sorted([a_p,a_q])
    if a_s < a_p or a_s > a_q:
        return arc( c, r, angle=a_q, sector=(0,2*pi-a_q+a_p), **kwds)
    return arc( c, r, angle=a_p, sector=(0,a_q-a_p), **kwds)
def _arc(p, q, s, **kwds):
    #rewrite this to use polar_plot and get points to do filled triangles
    from sage.misc.functional import det
    from sage.plot.line import line
    from sage.misc.functional import norm
    from sage.symbolic.all import pi
    from sage.plot.arc import arc

    p, q, s = map(lambda x: vector(x), [p, q, s])

    # to avoid running into division by 0 we set to be colinear vectors that are
    # almost colinear
    if abs(det(matrix([p - s, q - s]))) < 0.01:
        return line((p, q), **kwds)

    (cx, cy) = var('cx', 'cy')
    equations = [
        2 * cx * (s[0] - p[0]) + 2 * cy * (s[1] - p[1]) == s[0]**2 + s[1]**2 -
        p[0]**2 - p[1]**2, 2 * cx * (s[0] - q[0]) + 2 * cy *
        (s[1] - q[1]) == s[0]**2 + s[1]**2 - q[0]**2 - q[1]**2
    ]
    c = vector([
        solve(equations, (cx, cy), solution_dict=True)[0][i] for i in [cx, cy]
    ])

    r = norm(p - c)

    a_p, a_q, a_s = map(lambda x: atan2(x[1], x[0]), [p - c, q - c, s - c])
    a_p, a_q = sorted([a_p, a_q])
    if a_s < a_p or a_s > a_q:
        return arc(c, r, angle=a_q, sector=(0, 2 * pi - a_q + a_p), **kwds)
    return arc(c, r, angle=a_p, sector=(0, a_q - a_p), **kwds)
예제 #3
0
def refresh(vars):
    """
    Return a list of replacement of given variables with fresh ones.
    """
    if len(vars) == 0:
        return []
    elif len(vars) == 1:
        vars = list(vars) * 2
    return solve([x == x for x in vars], list(vars))[0]
예제 #4
0
    def inverse(self, chartname1=None, chartname2=None): 
        r"""
        Returns the inverse diffeomorphism. 
        
        INPUT:
    
        - ``chartname1`` -- (default: None) string defining the chart in which
          the computation of the inverse is performed; if none is provided, the
          default chart of self.manifold1 will be used
        - ``chartname2`` -- (default: None) string defining the chart in which
          the computation of the inverse is performed; if none is provided, the
          default chart of self.manifold2 will be used
        
        OUTPUT:
        
        - the inverse diffeomorphism
        
        EXAMPLES:
        
            The inverse of a rotation in the plane::
            
                sage: m = Manifold(2, "plane")
                sage: c_cart = Chart(m, 'x y', 'cart')
                sage: # A pi/3 rotation around the origin:
                sage: rot = Diffeomorphism(m, m, ((x - sqrt(3)*y)/2, (sqrt(3)*x + y)/2))
                sage: p = Point(m,(1,2))
                sage: q = rot(p)
                sage: irot = rot.inverse()
                sage: p1 = irot(q)
                sage: p1 == p
                True
        """
        from sage.symbolic.ring import SR
        from sage.symbolic.relation import solve
        from utilities import simplify_chain
        if self._inverse is not None:
            return self._inverse
            
        if chartname1 is None: chartname1 = self.manifold1.def_chart.name
        if chartname2 is None: chartname2 = self.manifold2.def_chart.name

        coord_map = self.coord_expression[(chartname1, chartname2)]
        chart1 = self.manifold1.atlas[chartname1]
        chart2 = self.manifold2.atlas[chartname2]
        
        n1 = len(chart1.xx)
        n2 = len(chart2.xx)
        
        # New symbolic variables (different from chart2.xx to allow for a 
        #  correct solution even when chart2 = chart1):
        x2 = [ SR.var('xxxx' + str(i)) for i in range(n2) ]
        equations = [x2[i] == coord_map.functions[i] for i in range(n2) ]
        solutions = solve(equations, chart1.xx, solution_dict=True)
        if len(solutions) == 0: 
            raise ValueError("No solution found")
        if len(solutions) > 1: 
            raise ValueError("Non-unique solution found")
            
        #!# This should be the Python 2.7 form: 
        # substitutions = {x2[i]: chart2.xx[i] for i in range(n2)}
        #
        # Here we use a form compatible with Python 2.6:
        substitutions = dict([(x2[i], chart2.xx[i]) for i in range(n2)])
       
        inv_functions = [solutions[0][chart1.xx[i]].subs(substitutions) 
                           for i in range(n1)]
        for i in range(n1):
            x = inv_functions[i]
            try:
                inv_functions[i] = simplify_chain(x)
            except AttributeError:
                pass        
        self._inverse = Diffeomorphism(self.manifold2, self.manifold1, 
                                       inv_functions, chartname2, chartname1)
        return self._inverse
예제 #5
0
    def inverse(self, chart1=None, chart2=None): 
        r"""
        Return the inverse diffeomorphism. 
        
        INPUT:
    
        - ``chart1`` -- (default: None) chart in which the computation of the 
          inverse is performed if necessary; if none is provided, the default 
          chart of the start domain will be used
        - ``chart2`` -- (default: None) chart in which the computation of the 
          inverse is performed if necessary; if none is provided, the default 
          chart of the arrival domain will be used
        
        OUTPUT:
        
        - the inverse diffeomorphism
        
        EXAMPLES:
        
            The inverse of a rotation in the Euclidean plane::
            
                sage: m = Manifold(2, 'R^2', r'\RR^2')
                sage: c_cart.<x,y> = m.chart('x y')
                sage: # A pi/3 rotation around the origin:
                sage: rot = Diffeomorphism(m, m, ((x - sqrt(3)*y)/2, (sqrt(3)*x + y)/2), name='R')
                sage: rot.inverse() 
                diffeomorphism 'R^(-1)' on the 2-dimensional manifold 'R^2'
                sage: rot.inverse().view()
                R^(-1): R^2 --> R^2, (x, y) |--> (1/2*sqrt(3)*y + 1/2*x, -1/2*sqrt(3)*x + 1/2*y)

            Checking that applying successively the diffeomorphism and its 
            inverse results in the identity::
            
                sage: (a, b) = var('a b')
                sage: p = Point(m, (a,b)) # a generic point on M
                sage: q = rot(p)
                sage: p1 = rot.inverse()(q)
                sage: p1 == p 
                True

        """
        from sage.symbolic.ring import SR
        from sage.symbolic.relation import solve
        from utilities import simplify_chain
        if self._inverse is not None:
            return self._inverse
            
        if chart1 is None: chart1 = self.domain1.def_chart
        if chart2 is None: chart2 = self.domain2.def_chart
        coord_map = self.coord_expression[(chart1, chart2)]
        n1 = len(chart1.xx)
        n2 = len(chart2.xx)
        
        # New symbolic variables (different from chart2.xx to allow for a 
        #  correct solution even when chart2 = chart1):
        x2 = [ SR.var('xxxx' + str(i)) for i in range(n2) ]
        equations = [ x2[i] == coord_map.functions[i].express 
                      for i in range(n2) ]
        solutions = solve(equations, chart1.xx, solution_dict=True)
        if len(solutions) == 0: 
            raise ValueError("No solution found")
        if len(solutions) > 1: 
            raise ValueError("Non-unique solution found")
            
        #!# This should be the Python 2.7 form: 
        # substitutions = {x2[i]: chart2.xx[i] for i in range(n2)}
        #
        # Here we use a form compatible with Python 2.6:
        substitutions = dict([(x2[i], chart2.xx[i]) for i in range(n2)])
       
        inv_functions = [solutions[0][chart1.xx[i]].subs(substitutions) 
                           for i in range(n1)]
        for i in range(n1):
            x = inv_functions[i]
            try:
                inv_functions[i] = simplify_chain(x)
            except AttributeError:
                pass
        if self.name is None:
            name = None
        else:
            name = self.name + '^(-1)'
        
        if self.latex_name is None:
            latex_name = None
        else:
            latex_name = self.latex_name + r'^{-1}'
        self._inverse = Diffeomorphism(self.domain2, self.domain1, 
                                       inv_functions, chart2, chart1,
                                       name=name, latex_name=latex_name)
        return self._inverse
예제 #6
0
def edges_intersection(P, i, cmatrix=None):
    r"""Return the point in the plane where the edges adjacent to the input edge intersect.

    INPUT: 

    ``P`` - a polygon (Polyhedron in 2d).

    ``i`` - integer, index of edge in ``P.inequalities_list()``.

    ``cmatrix`` - (optional) if None, the constraints matrix corresponding to P is computed inside the function.

    OUTPUT: 

    ``p`` - coordinates of the intersection of the edges that are adjacent to i.

    ``neighbor_constraints`` - indices of the edges that are adjacent to it. The edges are indexed according to P.inequalities_list(). 

    NOTES: 

    - This has been tested for P in QQ and RDF.
    """
    from sage.symbolic.ring import SR
    from sage.symbolic.relation import solve

    if cmatrix is None:
        cmatrix = vertex_connections(P)

    got_QQ = True if P.base_ring() == QQ else False

    #constraint_i = P.inequalities_list()[i]

    neighbor_constraints = []

    # vertices associated to the given edge i
    vert_i = cmatrix[i]

    for j, cj in enumerate(cmatrix):
        if (vert_i[0] in cj or vert_i[1] in cj) and (j != i):
            neighbor_constraints.append(j)

    # first one
    constr_1 = P.inequalities_list()[neighbor_constraints[0]]

    # second one
    constr_2 = P.inequalities_list()[neighbor_constraints[1]]

    # write and solve the intersection of the two lines
    x1 = SR.var('x1')
    x2 = SR.var('x2')

    eq1 = constr_1[1] * x1 + constr_1[2] * x2 == -constr_1[0]
    eq2 = constr_2[1] * x1 + constr_2[2] * x2 == -constr_2[0]

    p = solve([eq1, eq2], x1, x2)
    p = [p[0][0].right_hand_side(), p[0][1].right_hand_side()]

    if not got_QQ:  # assuming RDF
        # transform to RDF (because solve produces in general rational answers)
        p = [RDF(pi) for pi in p]

    return p, neighbor_constraints
예제 #7
0
    def inverse(self, chartname1=None, chartname2=None):
        r"""
        Returns the inverse diffeomorphism. 
        
        INPUT:
    
        - ``chartname1`` -- (default: None) string defining the chart in which
          the computation of the inverse is performed; if none is provided, the
          default chart of self.manifold1 will be used
        - ``chartname2`` -- (default: None) string defining the chart in which
          the computation of the inverse is performed; if none is provided, the
          default chart of self.manifold2 will be used
        
        OUTPUT:
        
        - the inverse diffeomorphism
        
        EXAMPLES:
        
            The inverse of a rotation in the plane::
            
                sage: m = Manifold(2, "plane")
                sage: c_cart = Chart(m, 'x y', 'cart')
                sage: # A pi/3 rotation around the origin:
                sage: rot = Diffeomorphism(m, m, ((x - sqrt(3)*y)/2, (sqrt(3)*x + y)/2))
                sage: p = Point(m,(1,2))
                sage: q = rot(p)
                sage: irot = rot.inverse()
                sage: p1 = irot(q)
                sage: p1 == p
                True
        """
        from sage.symbolic.ring import SR
        from sage.symbolic.relation import solve
        from utilities import simplify_chain
        if self._inverse is not None:
            return self._inverse

        if chartname1 is None: chartname1 = self.manifold1.def_chart.name
        if chartname2 is None: chartname2 = self.manifold2.def_chart.name

        coord_map = self.coord_expression[(chartname1, chartname2)]
        chart1 = self.manifold1.atlas[chartname1]
        chart2 = self.manifold2.atlas[chartname2]

        n1 = len(chart1.xx)
        n2 = len(chart2.xx)

        # New symbolic variables (different from chart2.xx to allow for a
        #  correct solution even when chart2 = chart1):
        x2 = [SR.var('xxxx' + str(i)) for i in range(n2)]
        equations = [x2[i] == coord_map.functions[i] for i in range(n2)]
        solutions = solve(equations, chart1.xx, solution_dict=True)
        if len(solutions) == 0:
            raise ValueError("No solution found")
        if len(solutions) > 1:
            raise ValueError("Non-unique solution found")

        #!# This should be the Python 2.7 form:
        # substitutions = {x2[i]: chart2.xx[i] for i in range(n2)}
        #
        # Here we use a form compatible with Python 2.6:
        substitutions = dict([(x2[i], chart2.xx[i]) for i in range(n2)])

        inv_functions = [
            solutions[0][chart1.xx[i]].subs(substitutions) for i in range(n1)
        ]
        for i in range(n1):
            x = inv_functions[i]
            try:
                inv_functions[i] = simplify_chain(x)
            except AttributeError:
                pass
        self._inverse = Diffeomorphism(self.manifold2, self.manifold1,
                                       inv_functions, chartname2, chartname1)
        return self._inverse
예제 #8
0
 def _compute_init_vector(self, point, pt0, pr0, pth0, pph0, r_increase,
                          th_increase, verbose):
     r"""
     Computes the initial 4-momentum vector `p` from the constants of motion
     """
     BLchart = self._spacetime.boyer_lindquist_coordinates()
     basis = BLchart.frame().at(point)
     r, th = BLchart(point)[1:3]
     a, m = self._a, self._m
     r2 = r**2
     a2 = a**2
     rho2 = r2 + (a * cos(th))**2
     Delta = r2 - 2 * m * r + a2
     if pt0 is None:
         if (self._mu is not None and pr0 is not None and pth0 is not None
                 and pph0 is not None):
             xxx = SR.var('xxx')
             v = self._spacetime.tangent_space(point)(
                 (xxx, pr0, pth0, pph0), basis=basis)
             muv2 = -self._spacetime.metric().at(point)(v, v)
             muv2 = muv2.substitute(self._numerical_substitutions())
             solutions = solve(muv2 == self._mu**2, xxx, solution_dict=True)
             if verbose:
                 print("Solutions for p^t:")
                 pretty_print(solutions)
             for sol in solutions:
                 if sol[xxx] > 0:
                     pt0 = sol[xxx]
                     break
             else:  # pt0 <= 0 might occur in the ergoregion
                 pt0 = solutions[0][xxx]
             try:
                 pt0 = RR(pt0)
             except TypeError:  # pt0 contains some symbolic expression
                 pass
         else:
             if self._E is None:
                 raise ValueError("the constant E must be provided")
             if self._L is None:
                 raise ValueError("the constant L must be provided")
             E, L = self._E, self._L
             pt0 = ((r2 + a2) / Delta * ((r2 + a2) * E - a * L) + a *
                    (L - a * E * sin(th)**2)) / rho2
     if pph0 is None:
         if self._E is None:
             raise ValueError("the constant E must be provided")
         if self._L is None:
             raise ValueError("the constant L must be provided")
         E, L = self._E, self._L
         pph0 = (L / sin(th)**2 - a * E + a / Delta *
                 ((r2 + a2) * E - a * L)) / rho2
     if pr0 is None:
         if self._E is None:
             raise ValueError("the constant E must be provided")
         if self._L is None:
             raise ValueError("the constant L must be provided")
         if self._mu is None:
             raise ValueError("the constant mu must be provided")
         if self._Q is None:
             raise ValueError("the constant Q must be provided")
         E, L, Q = self._E, self._L, self._Q
         mu2 = self._mu**2
         E2_mu2 = E**2 - mu2
         pr0 = sqrt((E2_mu2) * r**4 + 2 * m * mu2 * r**3 +
                    (a2 * E2_mu2 - L**2 - Q) * r**2 + 2 * m *
                    (Q + (L - a * E)**2) * r - a2 * Q) / rho2
         if not r_increase:
             pr0 = -pr0
     if pth0 is None:
         if self._E is None:
             raise ValueError("the constant E must be provided")
         if self._L is None:
             raise ValueError("the constant L must be provided")
         if self._mu is None:
             raise ValueError("the constant mu must be provided")
         if self._Q is None:
             raise ValueError("the constant Q must be provided")
         E2 = self._E**2
         L2 = self._L**2
         mu2 = self._mu**2
         Q = self._Q
         pth0 = sqrt(Q + cos(th)**2 * (a2 *
                                       (E2 - mu2) - L2 / sin(th)**2)) / rho2
         if not th_increase:
             pth0 = -pth0
     return self._spacetime.tangent_space(point)((pt0, pr0, pth0, pph0),
                                                 basis=basis,
                                                 name='p')
예제 #9
0
def find(expressions, vars, conditions=None, solver=None):
    """
    Generate assignments of values to variables
    such that the values of the expressions are integral
    and subject to the specified lower and upper bounds.

    Assumes that the expressions and conditions are linear in the variables.
    """
    expressions = dict(expressions)
    conditions = set() if conditions is None else set(conditions)
    vars = set(vars)
    for c in set(conditions):
        if verify(c):
            conditions.discard(c)
        elif verify(c.negation()):
            return
    if len(vars) > 0:
        _, opt = min((Infinity if None in (u, l) else u - l, e)
                     for e, (l, u) in expressions.items()
                     if not is_constant(e))
        _, x = min((abs(opt.coefficient(y)), y) for y in variables(opt))
        s = symbol("__opt")
        xsol = solve(s == opt, x)[0]
        rest = vars - {x}
        zero = [z == 0 for z in vars]
        lp = MixedIntegerLinearProgram(maximization=False, solver=solver)
        v = lp.new_variable(real=True)
        w = lp.new_variable(integer=True)
        lp.add_constraint(lp[1] == 1)

        def makeLPExpression(e):
            return sum(e.coefficient(y) * v[str(y)] for y in vars) \
                   + e.subs(zero) * lp[1]

        lpopt = makeLPExpression(opt)

        def addCondition(c):
            op = c.operator()
            if op is operator.gt:
                op = operator.ge
            elif op is operator.lt:
                op = operator.le
            elif op not in [operator.eq, operator.ge, operator.le]:
                return
            lp.add_constraint(
                op(makeLPExpression(c.lhs()), makeLPExpression(c.rhs())))

    else:
        x = None
    delete = set()
    for i, (e, (l, u)) in enumerate(expressions.items()):
        if is_constant(e):
            try:
                integralize(e)
            except TypeError:
                return
            if e < l or e > u:
                return
            delete.add(e)
            continue
        elif x is None:
            return
        lp.add_constraint(w[i] == makeLPExpression(e))
        lp.set_min(w[i], l)
        lp.set_max(w[i], u)
    for e in delete:
        del expressions[e]
    if x is None:
        yield ()
        return
    for c in conditions:
        addCondition(c)
    lp.set_objective(-lpopt)
    try:
        vmax = round(-lp.solve())
    except MIPSolverException as ex:
        if len(ex.args) == 0 or 'feasible' in ex.args[0]:
            return
    lp.set_objective(lpopt)
    vnew = vmin = round(lp.solve())
    while vmin <= vmax:
        eq = xsol.subs(s == vmin)
        g = find(make_expressions(
            (e.subs(eq), l, u) for e, (l, u) in expressions.items()),
                 vars=rest,
                 conditions={c.subs(eq)
                             for c in conditions})
        try:
            while vnew == vmin:
                sol = next(g)
                t = (yield (eq.subs(sol), ) + sol)
                while t is not None:
                    b, c = t
                    if b:
                        t = (yield find(expressions,
                                        vars=vars,
                                        conditions=conditions | {c}))
                    else:
                        if c in conditions or verify(c):
                            t = yield
                            continue
                        elif verify(c.negation()):
                            return
                        conditions.add(c)
                        addCondition(c)
                        lp.set_objective(-lpopt)
                        try:
                            vmax = round(-lp.solve())
                        except MIPSolverException:
                            return
                        lp.set_objective(lpopt)
                        vnew = round(lp.solve())
                        if vnew == vmin:
                            g.send((False, c.subs(eq)))
                        t = yield
            vmin = vnew
        except StopIteration:
            vmin += 1
            vnew = vmin
        finally:
            g.close()
예제 #10
0
    def cluster_expansion(self, beta):
        if beta == 0:
            return dict()

        coefficients=beta.monomial_coefficients()
        if any ( x < 0 for x in coefficients.values() ):
            alpha = [ -x for x in self.initial_cluster() ]
            negative_part = dict( [(-alpha[x],-coefficients[x]) for x in
                    coefficients if coefficients[x] < 0 ] )
            positive_part = sum( [ coefficients[x]*alpha[x] for x in
                    coefficients if coefficients[x] > 0 ] )
            return dict( negative_part.items() +
                    self.cluster_expansion(positive_part).items() )

        if self.is_affine():
            if self.gamma().associated_coroot().scalar(beta) < 0:
                shifted_expansion = self.cluster_expansion( self.tau_c()(beta) )
                return dict( [ (self.tau_c_inverse()(x),shifted_expansion[x]) for x in
                    shifted_expansion ] )
            elif self.gamma().associated_coroot().scalar(beta) > 0:
                shifted_expansion = self.cluster_expansion( self.tau_c_inverse()(beta) )
                return dict( [ (self.tau_c()(x),shifted_expansion[x]) for x in
                    shifted_expansion ] )
            else:
                ###
                # Assumptions
                #
                # Two cases are possible for vectors in the interior of the cone
                # according to how many tubes there are:
                # 1) If there is only one tube then its extremal rays are linearly
                # independent, therefore a point is in the interior of the cone
                # if and only if it is a linear combination of all the extremal
                # rays with strictly positive coefficients. In this case solve()
                # should produce only one solution.
                # 2) If there are two or three tubes then the extreme rays are
                # linearly dependent. A vector is in the interior of the cone if
                # and only if it can be written as a strictly positive linear
                # combination of all the rays of at least one tube. In this case
                # solve() should return at least two solutions.
                #
                # If a vector is on one face of the cone than it can be written
                # uniquely as linear combination of the rays of that face (they
                # are linearly independent). solve() should return only one
                # solution no matter how many tubes there are.

                rays = flatten([ t[0] for t in self.affine_tubes() ])
                system = matrix( map( vector, rays ) ).transpose()
                x = vector( var ( ['x%d'%i for i in range(len(rays))] ) )
                eqs =  [ (system*x)[i] == vector(beta)[i] for i in
                        range(self._n)]
                ieqs = [ y >= 0 for y in x ]
                solutions = solve( eqs+ieqs, x, solution_dict=True )

                if not solutions:
                    # we are outside the cone
                    shifted_expansion = self.cluster_expansion( self.tau_c()(beta) )
                    return dict( [ (self.tau_c_inverse()(v),shifted_expansion[v]) for v in
                        shifted_expansion ] )

                if len(solutions) > 1 or all( v > 0 for v in solutions[0].values() ):
                    # we are in the interior of the cone
                    raise ValueError("Vectors in the interior of the cone do "
                        "not have a cluster expansion")

                # we are on the boundary of the cone
                solution_dict=dict( [(rays[i],solutions[0][x[i]]) for i in range(len(rays)) ] )
                tube_bases = [ t[0] for t in self.affine_tubes() ]
                connected_components = []
                index = 0
                for t in tube_bases:
                    component = []
                    for a in t:
                        if solution_dict[a] == 0:
                            if component:
                                connected_components.append( component )
                                component = []
                        else:
                            component.append( (a,solution_dict[a]) )
                    if component:
                        if connected_components:
                            connected_components[index] = ( component +
                                    connected_components[index] )
                        else:
                            connected_components.append( component )
                    index = len(connected_components)
                expansion = dict()
                while connected_components:
                    component = connected_components.pop()
                    c = min( [ a[1] for a in component] )
                    expansion[sum( [a[0] for a in component])] = c
                    component = [ (a[0],a[1]-c) for a in component ]
                    new_component = []
                    for a in component:
                        if a[1] == 0:
                            if new_component:
                                connected_components.append( new_component )
                                new_component = []
                        else:
                            new_component.append( a )
                    if new_component:
                        connected_components.append( new_component )
                return expansion

        if self.is_finite():
            shifted_expansion = self.cluster_expansion( self.tau_c()(beta) )
            return dict( [ (self.tau_c_inverse()(x),shifted_expansion[x]) for x
                in shifted_expansion ] )
예제 #11
0
    def cluster_expansion(self, beta):
        if beta == 0:
            return dict()

        coefficients = beta.monomial_coefficients()
        if any(x < 0 for x in coefficients.values()):
            alpha = [-x for x in self.initial_cluster()]
            negative_part = dict([(-alpha[x], -coefficients[x])
                                  for x in coefficients
                                  if coefficients[x] < 0])
            positive_part = sum([
                coefficients[x] * alpha[x] for x in coefficients
                if coefficients[x] > 0
            ])
            return dict(negative_part.items() +
                        self.cluster_expansion(positive_part).items())

        if self.is_affine():
            if self.gamma().associated_coroot().scalar(beta) < 0:
                shifted_expansion = self.cluster_expansion(self.tau_c()(beta))
                return dict([(self.tau_c_inverse()(x), shifted_expansion[x])
                             for x in shifted_expansion])
            elif self.gamma().associated_coroot().scalar(beta) > 0:
                shifted_expansion = self.cluster_expansion(
                    self.tau_c_inverse()(beta))
                return dict([(self.tau_c()(x), shifted_expansion[x])
                             for x in shifted_expansion])
            else:
                ###
                # Assumptions
                #
                # Two cases are possible for vectors in the interior of the cone
                # according to how many tubes there are:
                # 1) If there is only one tube then its extremal rays are linearly
                # independent, therefore a point is in the interior of the cone
                # if and only if it is a linear combination of all the extremal
                # rays with strictly positive coefficients. In this case solve()
                # should produce only one solution.
                # 2) If there are two or three tubes then the extreme rays are
                # linearly dependent. A vector is in the interior of the cone if
                # and only if it can be written as a strictly positive linear
                # combination of all the rays of at least one tube. In this case
                # solve() should return at least two solutions.
                #
                # If a vector is on one face of the cone than it can be written
                # uniquely as linear combination of the rays of that face (they
                # are linearly independent). solve() should return only one
                # solution no matter how many tubes there are.

                rays = flatten([t[0] for t in self.affine_tubes()])
                system = matrix(map(vector, rays)).transpose()
                x = vector(var(['x%d' % i for i in range(len(rays))]))
                eqs = [(system * x)[i] == vector(beta)[i]
                       for i in range(self._n)]
                ieqs = [y >= 0 for y in x]
                solutions = solve(eqs + ieqs, x, solution_dict=True)

                if not solutions:
                    # we are outside the cone
                    shifted_expansion = self.cluster_expansion(
                        self.tau_c()(beta))
                    return dict([(self.tau_c_inverse()(v),
                                  shifted_expansion[v])
                                 for v in shifted_expansion])

                if len(solutions) > 1 or all(v > 0
                                             for v in solutions[0].values()):
                    # we are in the interior of the cone
                    raise ValueError("Vectors in the interior of the cone do "
                                     "not have a cluster expansion")

                # we are on the boundary of the cone
                solution_dict = dict([(rays[i], solutions[0][x[i]])
                                      for i in range(len(rays))])
                tube_bases = [t[0] for t in self.affine_tubes()]
                connected_components = []
                index = 0
                for t in tube_bases:
                    component = []
                    for a in t:
                        if solution_dict[a] == 0:
                            if component:
                                connected_components.append(component)
                                component = []
                        else:
                            component.append((a, solution_dict[a]))
                    if component:
                        if connected_components:
                            connected_components[index] = (
                                component + connected_components[index])
                        else:
                            connected_components.append(component)
                    index = len(connected_components)
                expansion = dict()
                while connected_components:
                    component = connected_components.pop()
                    c = min([a[1] for a in component])
                    expansion[sum([a[0] for a in component])] = c
                    component = [(a[0], a[1] - c) for a in component]
                    new_component = []
                    for a in component:
                        if a[1] == 0:
                            if new_component:
                                connected_components.append(new_component)
                                new_component = []
                        else:
                            new_component.append(a)
                    if new_component:
                        connected_components.append(new_component)
                return expansion

        if self.is_finite():
            shifted_expansion = self.cluster_expansion(self.tau_c()(beta))
            return dict([(self.tau_c_inverse()(x), shifted_expansion[x])
                         for x in shifted_expansion])