Ticket #10537: trac_10537_dictionary_vector_input.patch

File trac_10537_dictionary_vector_input.patch, 7.9 KB (added by rbeezer, 11 years ago)
  • sage/modules/free_module_element.pyx

    # HG changeset patch
    # User Rob Beezer <beezer@ups.edu>
    # Date 1293791147 28800
    # Node ID b849f0558252b6778b0e45f6163a7cd09886d027
    # Parent  a4f56eba9ffe0a81b82dfb89475a3ab197ba4d23
    10537: fix dictionary input to vector constructor
    
    diff -r a4f56eba9ffe -r b849f0558252 sage/modules/free_module_element.pyx
    a b  
    268268        sage: vector({1:1.1, 3:3.14})
    269269        (0.000000000000000, 1.10000000000000, 0.000000000000000, 3.14000000000000)
    270270
    271     It is very unlikely that giving a degree and a dictionary will succeed. ::
    272 
    273         sage: v = vector(QQ, 8, {0:1/2, 4:-6}); v
    274         Traceback (most recent call last):
    275         ...
    276         TypeError: cannot specify the degree of a vector while entries are given by a dictionary
    277 
    278     Instead, provide a "terminal" element (likely a zero) to fill out
    279     the vector to the desired number of entries.  ::
     271    With no degree given, a dictionary of entries implicitly declares a
     272    degree by the largest index (key) present.  So you can provide a
     273    terminal element (perhaps a zero?) to set the degree.  But it is probably safer
     274    to just include a degree in your construction.  ::
    280275
    281276        sage: v = vector(QQ, {0:1/2, 4:-6, 7:0}); v
    282277        (1/2, 0, 0, 0, -6, 0, 0, 0)
     
    284279        8
    285280        sage: v.is_sparse()
    286281        True
     282        sage: w = vector(QQ, 8, {0:1/2, 4:-6})
     283        sage: w == v
     284        True
    287285
    288286    It is an error to specify a negative degree. ::
    289287
     
    292290        ...
    293291        ValueError: cannot specify the degree of a vector as a negative integer (-4)
    294292
     293    And it is an error to specify an index in a dictionary
     294    that is greater than or equal to a requested degree. ::
     295
     296        sage: vector(ZZ, 10, {3:4, 7:-2, 10:637})
     297        Traceback (most recent call last):
     298        ...
     299        ValueError: dictionary of entries has a key (index) exceeding the requested degree
     300
    295301    Any 1 dimensional numpy array of type float or complex may be
    296302    passed to vector. The result will be a vector in the appropriate
    297303    dimensional vector space over the real double field or the complex
     
    347353    if hasattr(arg1, '_vector_'):
    348354        return arg1._vector_(arg0)
    349355
     356    # consider a possible degree specified in second argument
     357    degree = None
     358    maxindex = None
    350359    if sage.rings.integer.is_Integer(arg1) or isinstance(arg1,(int,long)):
    351360        if arg1 < 0:
    352361            raise ValueError("cannot specify the degree of a vector as a negative integer (%s)" % arg1)
    353362        if isinstance(arg2, dict):
    354             raise TypeError("cannot specify the degree of a vector while entries are given by a dictionary")
     363            maxindex = max([-1]+[index for index in arg2])
     364            if not maxindex < arg1:
     365                raise ValueError("dictionary of entries has a key (index) exceeding the requested degree")
     366        # arg1 is now a legitimate degree
     367        # replace it with a zero list or a dictionary, or a size-checked iterable
     368        # so then down to just two arguments
     369        degree = arg1
    355370        if arg2 is None:
    356             arg1 = [0]*arg1
     371            arg1 = [0]*degree
    357372        else:
    358             if len(arg2) != arg1:
     373            if not isinstance(arg2, dict) and len(arg2) != degree:
    359374                raise ValueError, "incompatible degrees in vector constructor"
    360375            arg1 = arg2
    361376
     377    # Analyze arg0 and arg1 to create a ring (R) and entries (v)
    362378    if is_Ring(arg0):
    363379        R = arg0
    364380        v = arg1
     
    385401                return _v
    386402
    387403    if isinstance(v, dict):
     404        if degree is None:
     405            degree = max([-1]+[index for index in v])+1
    388406        if sparse is None:
    389407            sparse = True
    390         v, R = prepare_dict(v, R)
    391408    else:
     409        degree = None
    392410        if sparse is None:
    393411            sparse = False
    394         v, R = prepare(v, R)
     412
     413    v, R = prepare(v, R, degree)
    395414
    396415    if sparse:
    397416        import free_module  # slow -- can we improve
     
    401420
    402421free_module_element = vector
    403422
    404 def prepare(v, R):
    405     """
    406     Find a common ring (using R as universe) that contains every
    407     element of v, and replace v with the sequence of elements in v
    408     coerced to this ring.  For more details, see the
    409     sage.structure.sequence.Sequence object.
     423def prepare(v, R, degree=None):
     424    r"""
     425    Converts an object describing elements of a vector into a list of entries in a common ring.
    410426
    411427    INPUT:
    412428
    413         - v -- list or tuple
    414         - R -- ring or None
     429    - ``v`` - a dictionary with non-negative integers as keys,
     430      or a list or other object that can be converted to Sequence
     431    - ``R`` - a ring containing all the entries, possibly given as ``None``
     432    - ``degree`` -  a requested size for the list when the input is a dictionary,
     433      otherwise ignored
     434
     435    OUTPUT:
     436
     437    A pair.
     438
     439    The first item is a list of the values specified in the object ``v``.
     440    If the object is a dictionary , entries are placed in the list
     441    according to the indices that were their keys in the dictionary,
     442    and the remainder of the entries are zero.  The value of
     443    ``degree`` is assumed to be larger than any index provided
     444    in the dictionary and will be used as the number of entries
     445    in the returned list.
     446
     447    The second item returned is a ring that contains all of
     448    the entries in the list. If ``R`` is given, the entries
     449    are coerced in.  Otherwise a common ring is found. For
     450    more details, see the
     451    :class:`~sage.structure.sequence.Sequence` object.
     452
    415453
    416454    EXAMPLES::
    417455
    418         sage: sage.modules.free_module_element.prepare([1,2/3,5],None)
     456        sage: from sage.modules.free_module_element import prepare
     457        sage: prepare([1,2/3,5],None)
    419458        ([1, 2/3, 5], Rational Field)
    420         sage: sage.modules.free_module_element.prepare([1,2/3,5],RR)
     459
     460        sage: prepare([1,2/3,5],RR)
    421461        ([1.00000000000000, 0.666666666666667, 5.00000000000000], Real Field with 53 bits of precision)
    422         sage: sage.modules.free_module_element.prepare([1,2/3,'10',5],None)
     462
     463        sage: prepare({1:4, 3:-2}, ZZ, 6)
     464        ([0, 4, 0, -2, 0, 0], Integer Ring)
     465
     466        sage: prepare({3:1, 5:3}, QQ, 6)
     467        ([0, 0, 0, 1, 0, 3], Rational Field)
     468
     469        sage: prepare([1,2/3,'10',5],RR)
     470        ([1.00000000000000, 0.666666666666667, 10.0000000000000, 5.00000000000000], Real Field with 53 bits of precision)
     471
     472        sage: prepare({},QQ, 0)
     473        ([], Rational Field)
     474
     475        sage: prepare([1,2/3,'10',5],None)
    423476        Traceback (most recent call last):
    424477        ...
    425478        TypeError: unable to find a common ring for all elements
    426         sage: sage.modules.free_module_element.prepare([1,2/3,'10',5],RR)
    427         ([1.00000000000000, 0.666666666666667, 10.0000000000000, 5.00000000000000], Real Field with 53 bits of precision)
     479
    428480    """
     481    if isinstance(v, dict):
     482        # convert to a list
     483        X = [0]*degree
     484        for key, value in v.iteritems():
     485            X[key] = value
     486        v = X
     487    # convert to a Sequence over common ring
    429488    v = Sequence(v, universe=R, use_sage_types=True)
    430489    ring = v.universe()
    431490    if not is_Ring(ring):
    432491        raise TypeError, "unable to find a common ring for all elements"
    433492    return v, ring
    434493
    435 def prepare_dict(w, R):
    436     """
    437     EXAMPLES::
    438    
    439         sage: from sage.modules.free_module_element import prepare_dict
    440         sage: prepare_dict({3:1 , 5:3}, QQ)
    441         ([0, 0, 0, 1, 0, 3], Rational Field)
    442         sage: prepare_dict({},QQ)
    443         ([], Rational Field)
    444     """
    445     Z = w.items()
    446     cdef Py_ssize_t n
    447     n = max([-1]+[key for key,value in Z])+1
    448     X = [0]*n
    449     for key, value in Z:
    450         X[key] = value
    451     return prepare(X, R)
    452 
    453494def zero_vector(arg0, arg1=None):
    454495    r"""
    455496    Returns a vector or free module element with a specified number of zeros.